diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 812d22fec913b..da3a104f9ca32 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -142,6 +142,7 @@ enabled: - 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/detection_engine_api_integration/security_and_spaces/group10/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 diff --git a/.buildkite/scripts/steps/checks/bazel_packages.sh b/.buildkite/scripts/steps/checks/bazel_packages.sh index ca82c6fef609e..a8a631ed48ae4 100755 --- a/.buildkite/scripts/steps/checks/bazel_packages.sh +++ b/.buildkite/scripts/steps/checks/bazel_packages.sh @@ -6,5 +6,12 @@ source .buildkite/scripts/common/util.sh echo --- Check Bazel Packages Manifest node scripts/generate packages_build_manifest - check_for_changed_files 'node scripts/generate packages_build_manifest' true + +echo --- Check Codeowners Manifest +if [ -f ".github/CODEOWNERS" ]; then + node scripts/generate codeowners + check_for_changed_files 'node scripts/generate codeowners' true +else + echo "skipping, no existing .github/CODEOWNERS file found" +fi diff --git a/.eslintrc.js b/.eslintrc.js index b73b88ba95de9..406e58e17d7a6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,7 +9,9 @@ const Path = require('path'); const Fs = require('fs'); -const globby = require('globby'); +const normalizePath = require('normalize-path'); +const { discoverPackageManifestPaths, Jsonc } = require('@kbn/bazel-packages'); +const { REPO_ROOT } = require('@kbn/utils'); const APACHE_2_0_LICENSE_HEADER = ` /* @@ -119,15 +121,10 @@ const VENN_DIAGRAM_HEADER = ` */ `; -const packagePkgJsons = globby.sync('*/package.json', { - cwd: Path.resolve(__dirname, 'packages'), - absolute: true, -}); - /** Packages which should not be included within production code. */ -const DEV_PACKAGES = packagePkgJsons.flatMap((path) => { - const pkg = JSON.parse(Fs.readFileSync(path, 'utf8')); - return pkg.kibana && pkg.kibana.devOnly ? Path.dirname(Path.basename(path)) : []; +const DEV_PACKAGE_DIRS = discoverPackageManifestPaths(REPO_ROOT).flatMap((path) => { + const manifest = Jsonc.parse(Fs.readFileSync(path, 'utf8')); + return !!manifest.devOnly ? normalizePath(Path.relative(REPO_ROOT, Path.dirname(path))) : []; }); /** Directories (at any depth) which include dev-only code. */ @@ -145,6 +142,7 @@ const DEV_DIRECTORIES = [ 'integration_tests', 'manual_tests', 'mock', + 'mocks', 'storybook', 'scripts', 'test', @@ -153,6 +151,7 @@ const DEV_DIRECTORIES = [ 'test_utilities', 'test_helpers', 'tests_client_integration', + 'tsd_tests', ]; /** File patterns for dev-only code. */ @@ -171,7 +170,7 @@ const DEV_FILE_PATTERNS = [ /** Glob patterns which describe dev-only code. */ const DEV_PATTERNS = [ - ...DEV_PACKAGES.map((pkg) => `packages/${pkg}/**/*`), + ...DEV_PACKAGE_DIRS.map((pkg) => `${pkg}/**/*`), ...DEV_DIRECTORIES.map((dir) => `{packages,src,x-pack}/**/${dir}/**/*`), ...DEV_FILE_PATTERNS.map((file) => `{packages,src,x-pack}/**/${file}`), 'packages/kbn-interpreter/tasks/**/*', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 71af2a3e67230..ebc25aadd21ac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -42,7 +42,6 @@ /src/plugins/chart_expressions/expression_partition_vis/ @elastic/kibana-vis-editors /src/plugins/chart_expressions/expression_xy/ @elastic/kibana-vis-editors /src/plugins/url_forwarding/ @elastic/kibana-vis-editors -/packages/kbn-tinymath/ @elastic/kibana-vis-editors /x-pack/test/functional/apps/lens @elastic/kibana-vis-editors /x-pack/test/api_integration/apis/lens/ @elastic/kibana-vis-editors /test/functional/apps/visualize/ @elastic/kibana-vis-editors @@ -62,12 +61,6 @@ /examples/field_formats_example/ @elastic/kibana-app-services /examples/partial_results_example/ @elastic/kibana-app-services /examples/search_examples/ @elastic/kibana-app-services -/packages/kbn-datemath/ @elastic/kibana-app-services -/packages/kbn-interpreter/ @elastic/kibana-app-services -/packages/kbn-monaco/ @elastic/kibana-app-services -/packages/kbn-react-field/ @elastic/kibana-app-services -/packages/kbn-es-query/ @elastic/kibana-app-services -/packages/kbn-field-types/ @elastic/kibana-app-services /src/plugins/bfetch/ @elastic/kibana-app-services /src/plugins/data/ @elastic/kibana-app-services /src/plugins/data_views/ @elastic/kibana-app-services @@ -116,6 +109,7 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/observability/public/pages/cases @elastic/actionable-observability /x-pack/plugins/observability/public/pages/rules @elastic/actionable-observability /x-pack/plugins/observability/public/pages/rule_details @elastic/actionable-observability +/x-pack/test/observability_functional @elastic/actionable-observability @elastic/unified-observability # Infra Monitoring /x-pack/plugins/infra/ @elastic/infra-monitoring-ui @@ -139,12 +133,9 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/apm/ @elastic/apm-ui /x-pack/test/functional/apps/apm/ @elastic/apm-ui /x-pack/test/apm_api_integration/ @elastic/apm-ui -/src/plugins/apm_oss/ @elastic/apm-ui /src/apm.js @elastic/kibana-core @vigneshshanmugam -/packages/kbn-apm-config-loader/ @elastic/kibana-core @vigneshshanmugam +/packages/kbn-apm-config-loader/ @vigneshshanmugam /src/core/types/elasticsearch @elastic/apm-ui -/packages/kbn-apm-synthtrace/ @elastic/apm-ui -/packages/kbn-shared-svg @elastic/apm-ui /packages/kbn-utility-types/src/dot.ts @dgieselaar /packages/kbn-utility-types/src/dot_test.ts @dgieselaar #CC# /src/plugins/apm_oss/ @elastic/apm-ui @@ -220,7 +211,6 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/test/functional/apps/transform/ @elastic/ml-ui /x-pack/test/functional/services/transform/ @elastic/ml-ui /x-pack/test/functional_basic/apps/transform/ @elastic/ml-ui -/x-pack/packages/ml/ @elastic/ml-ui /examples/response_stream/ @elastic/ml-ui # Maps @@ -234,39 +224,11 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/stack_alerts/public/alert_types/geo_containment @elastic/kibana-gis #CC# /x-pack/plugins/file_upload @elastic/kibana-gis /x-pack/plugins/file_upload @elastic/kibana-gis -/packages/kbn-mapbox-gl @elastic/kibana-gis # Operations /src/dev/license_checker/config.ts @elastic/kibana-operations -/packages/kbn-docs-utils/ @elastic/kibana-operations /src/dev/ @elastic/kibana-operations /src/setup_node_env/ @elastic/kibana-operations -/packages/*eslint*/ @elastic/kibana-operations -/packages/*babel*/ @elastic/kibana-operations -/packages/kbn-ambient-ui-types/ @elastic/kibana-operations -/packages/kbn-ambient-storybook-types/ @elastic/kibana-operations -/packages/kbn-bazel-packages/ @elastic/kibana-operations -/packages/kbn-bazel-runner/ @elastic/kibana-operations -/packages/kbn-cli-dev-mode/ @elastic/kibana-operations -/packages/kbn-dev-utils*/ @elastic/kibana-operations -/packages/kbn-es-archiver/ @elastic/kibana-operations -/packages/kbn-es/ @elastic/kibana-operations -/packages/kbn-eslint-plugin-imports/ @elastic/kibana-operations -/packages/kbn-generate/ @elastic/kibana-operations -/packages/kbn-import-resolver/ @elastic/kibana-operations -/packages/kbn-optimizer/ @elastic/kibana-operations -/packages/kbn-plugin-discovery/ @elastic/kibana-operations -/packages/kbn-pm/ @elastic/kibana-operations -/packages/kbn-test/ @elastic/kibana-operations -/packages/kbn-type-summarizer*/ @elastic/kibana-operations -/packages/kbn-ui-shared-deps-npm/ @elastic/kibana-operations -/packages/kbn-ui-shared-deps-src/ @elastic/kibana-operations -/packages/kbn-utils/ @elastic/kibana-operations -/packages/kbn-jsonc/ @elastic/kibana-operations -/packages/kbn-kibana-manifest-parser/ @elastic/kibana-operations -/packages/kbn-kibana-manifest-schema/ @elastic/kibana-operations -/packages/kbn-managed-vscode-config/ @elastic/kibana-operations -/packages/kbn-managed-vscode-config-cli/ @elastic/kibana-operations /src/cli/keystore/ @elastic/kibana-operations /.ci/es-snapshots/ @elastic/kibana-operations /.github/workflows/ @elastic/kibana-operations @@ -277,12 +239,8 @@ x-pack/examples/files_example @elastic/kibana-app-services /.bazelrc.common @elastic/kibana-operations /.bazelversion @elastic/kibana-operations /WORKSPACE.bazel @elastic/kibana-operations -#CC# /packages/kbn-expect/ @elastic/kibana-operations /.buildkite/ @elastic/kibana-operations -# Performance testing -/packages/kbn-performance-testing-dataset-extractor/ @elastic/kibana-performance-testing - # Quality Assurance /src/dev/code_coverage @elastic/kibana-qa /vars/*Coverage.groovy @elastic/kibana-qa @@ -307,15 +265,6 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/saved_objects_tagging/ @elastic/kibana-core /x-pack/test/saved_objects_field_count/ @elastic/kibana-core /x-pack/test/saved_object_tagging/ @elastic/kibana-core -/packages/core/ @elastic/kibana-core -/packages/analytics/ @elastic/kibana-core -/packages/kbn-config-schema/ @elastic/kibana-core -/packages/kbn-std/ @elastic/kibana-core -/packages/kbn-config/ @elastic/kibana-core -/packages/kbn-logging/ @elastic/kibana-core -/packages/kbn-logging-mocks/ @elastic/kibana-core -/packages/kbn-server-http-tools/ @elastic/kibana-core -/packages/kbn-es-errors/ @elastic/kibana-core /src/plugins/saved_objects_management/ @elastic/kibana-core /src/plugins/advanced_settings/ @elastic/kibana-core /x-pack/plugins/global_search_bar/ @elastic/kibana-core @@ -328,12 +277,7 @@ x-pack/examples/files_example @elastic/kibana-app-services #CC# /src/plugins/newsfeed @elastic/kibana-core #CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core -# Kibana Docs -/packages/kbn-doc-links/ @elastic/kibana-docs - # Kibana Telemetry -/packages/kbn-analytics/ @elastic/kibana-core -/packages/kbn-telemetry-tools/ @elastic/kibana-core /src/plugins/kibana_usage_collection/ @elastic/kibana-core /src/plugins/newsfeed/ @elastic/kibana-core /src/plugins/telemetry/ @elastic/kibana-core @@ -349,14 +293,9 @@ x-pack/examples/files_example @elastic/kibana-app-services # Kibana Localization /src/dev/i18n/ @elastic/kibana-localization @elastic/kibana-core /src/core/public/i18n/ @elastic/kibana-localization @elastic/kibana-core -/packages/kbn-i18n/ @elastic/kibana-localization @elastic/kibana-core #CC# /x-pack/plugins/translations/ @elastic/kibana-localization @elastic/kibana-core # Kibana Platform Security -/packages/kbn-crypto/ @elastic/kibana-security -/packages/kbn-handlebars/ @elastic/kibana-security -/packages/kbn-user-profile-components/ @elastic/kibana-security -/packages/core/http/core-http-server-internal/src/csp/ @elastic/kibana-security @elastic/kibana-core /src/plugins/interactive_setup/ @elastic/kibana-security /src/plugins/telemetry/server/config/telemetry_labels.ts @elastic/kibana-security /test/interactive_setup_api_integration/ @elastic/kibana-security @@ -421,7 +360,6 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/upgrade_assistant/ @elastic/platform-deployment-management /x-pack/plugins/watcher/ @elastic/platform-deployment-management /x-pack/plugins/ingest_pipelines/ @elastic/platform-deployment-management -/packages/kbn-ace/ @elastic/platform-deployment-management #CC# /x-pack/plugins/cross_cluster_replication/ @elastic/platform-deployment-management # Security Solution @@ -559,7 +497,6 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/security_solution/public/common/components/sourcerer @elastic/security-solution-platform /x-pack/plugins/security_solution/server/lib/sourcerer @elastic/security-solution-platform -/packages/kbn-securitysolution* @elastic/security-solution-platform ## Security Threat Intelligence - Under Security Platform /x-pack/plugins/security_solution/public/common/components/threat_match @elastic/security-solution-platform @@ -637,7 +574,6 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience # Design (at the bottom for specificity of SASS files) **/*.scss @elastic/kibana-design -#CC# /packages/kbn-ui-framework/ @elastic/kibana-design # Observability design /x-pack/plugins/apm/**/*.scss @elastic/observability-design @@ -680,16 +616,11 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience # Application Experience -## Shared UX Team -/packages/shared-ux/ @elastic/shared-ux -/packages/shared-ux-*/ @elastic/shared-ux - ### Kibana React (to be deprecated) /src/plugins/kibana_react/ @elastic/shared-ux /src/plugins/kibana_react/public/code_editor @elastic/shared-ux @elastic/kibana-presentation ### Home Plugin and Packages -/packages/home/ @elastic/shared-ux /src/plugins/home/public @elastic/shared-ux /src/plugins/home/server/*.ts @elastic/shared-ux /src/plugins/home/server/services/ @elastic/shared-ux @@ -704,3 +635,311 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience #CC# /src/plugins/home/public @elastic/shared-ux #CC# /src/plugins/home/server/services/ @elastic/shared-ux #CC# /src/plugins/home/ @elastic/shared-ux + +#### +## Everything below this comment is automatically generated based on kibana.jsonc +## "owner" fields. This file is automatically updated by CI or can be updated locally +## by running `node scripts/generate codeowners`. +#### + +packages/analytics/client @elastic/kibana-core +packages/analytics/shippers/elastic_v3/browser @elastic/kibana-core +packages/analytics/shippers/elastic_v3/common @elastic/kibana-core +packages/analytics/shippers/elastic_v3/server @elastic/kibana-core +packages/analytics/shippers/fullstory @elastic/kibana-core +packages/core/analytics/core-analytics-browser @elastic/kibana-core +packages/core/analytics/core-analytics-browser-internal @elastic/kibana-core +packages/core/analytics/core-analytics-browser-mocks @elastic/kibana-core +packages/core/analytics/core-analytics-server @elastic/kibana-core +packages/core/analytics/core-analytics-server-internal @elastic/kibana-core +packages/core/analytics/core-analytics-server-mocks @elastic/kibana-core +packages/core/application/core-application-browser @elastic/kibana-core +packages/core/application/core-application-browser-internal @elastic/kibana-core +packages/core/application/core-application-browser-mocks @elastic/kibana-core +packages/core/application/core-application-common @elastic/kibana-core +packages/core/base/core-base-browser-internal @elastic/kibana-core +packages/core/base/core-base-browser-mocks @elastic/kibana-core +packages/core/base/core-base-common @elastic/kibana-core +packages/core/base/core-base-common-internal @elastic/kibana-core +packages/core/base/core-base-server-internal @elastic/kibana-core +packages/core/base/core-base-server-mocks @elastic/kibana-core +packages/core/capabilities/core-capabilities-browser-internal @elastic/kibana-core +packages/core/capabilities/core-capabilities-browser-mocks @elastic/kibana-core +packages/core/capabilities/core-capabilities-common @elastic/kibana-core +packages/core/capabilities/core-capabilities-server @elastic/kibana-core +packages/core/capabilities/core-capabilities-server-internal @elastic/kibana-core +packages/core/capabilities/core-capabilities-server-mocks @elastic/kibana-core +packages/core/chrome/core-chrome-browser @elastic/kibana-core +packages/core/chrome/core-chrome-browser-internal @elastic/kibana-core +packages/core/chrome/core-chrome-browser-mocks @elastic/kibana-core +packages/core/config/core-config-server-internal @elastic/kibana-core +packages/core/deprecations/core-deprecations-browser @elastic/kibana-core +packages/core/deprecations/core-deprecations-browser-internal @elastic/kibana-core +packages/core/deprecations/core-deprecations-browser-mocks @elastic/kibana-core +packages/core/deprecations/core-deprecations-common @elastic/kibana-core +packages/core/deprecations/core-deprecations-server @elastic/kibana-core +packages/core/deprecations/core-deprecations-server-internal @elastic/kibana-core +packages/core/deprecations/core-deprecations-server-mocks @elastic/kibana-core +packages/core/doc-links/core-doc-links-browser @elastic/kibana-core +packages/core/doc-links/core-doc-links-browser-internal @elastic/kibana-core +packages/core/doc-links/core-doc-links-browser-mocks @elastic/kibana-core +packages/core/doc-links/core-doc-links-server @elastic/kibana-core +packages/core/doc-links/core-doc-links-server-internal @elastic/kibana-core +packages/core/doc-links/core-doc-links-server-mocks @elastic/kibana-core +packages/core/elasticsearch/core-elasticsearch-client-server-internal @elastic/kibana-core +packages/core/elasticsearch/core-elasticsearch-client-server-mocks @elastic/kibana-core +packages/core/elasticsearch/core-elasticsearch-server @elastic/kibana-core +packages/core/elasticsearch/core-elasticsearch-server-internal @elastic/kibana-core +packages/core/elasticsearch/core-elasticsearch-server-mocks @elastic/kibana-core +packages/core/environment/core-environment-server-internal @elastic/kibana-core +packages/core/environment/core-environment-server-mocks @elastic/kibana-core +packages/core/execution-context/core-execution-context-browser @elastic/kibana-core +packages/core/execution-context/core-execution-context-browser-internal @elastic/kibana-core +packages/core/execution-context/core-execution-context-browser-mocks @elastic/kibana-core +packages/core/execution-context/core-execution-context-common @elastic/kibana-core +packages/core/execution-context/core-execution-context-server @elastic/kibana-core +packages/core/execution-context/core-execution-context-server-internal @elastic/kibana-core +packages/core/execution-context/core-execution-context-server-mocks @elastic/kibana-core +packages/core/fatal-errors/core-fatal-errors-browser @elastic/kibana-core +packages/core/fatal-errors/core-fatal-errors-browser-internal @elastic/kibana-core +packages/core/fatal-errors/core-fatal-errors-browser-mocks @elastic/kibana-core +packages/core/http/core-http-browser @elastic/kibana-core +packages/core/http/core-http-browser-internal @elastic/kibana-core +packages/core/http/core-http-browser-mocks @elastic/kibana-core +packages/core/http/core-http-common @elastic/kibana-core +packages/core/http/core-http-context-server-internal @elastic/kibana-core +packages/core/http/core-http-context-server-mocks @elastic/kibana-core +packages/core/http/core-http-router-server-internal @elastic/kibana-core +packages/core/http/core-http-router-server-mocks @elastic/kibana-core +packages/core/http/core-http-server @elastic/kibana-core +packages/core/http/core-http-server-internal @elastic/kibana-core +packages/core/http/core-http-server-mocks @elastic/kibana-core +packages/core/i18n/core-i18n-browser @elastic/kibana-core +packages/core/i18n/core-i18n-browser-internal @elastic/kibana-core +packages/core/i18n/core-i18n-browser-mocks @elastic/kibana-core +packages/core/i18n/core-i18n-server @elastic/kibana-core +packages/core/i18n/core-i18n-server-internal @elastic/kibana-core +packages/core/i18n/core-i18n-server-mocks @elastic/kibana-core +packages/core/injected-metadata/core-injected-metadata-browser @elastic/kibana-core +packages/core/injected-metadata/core-injected-metadata-browser-internal @elastic/kibana-core +packages/core/injected-metadata/core-injected-metadata-browser-mocks @elastic/kibana-core +packages/core/injected-metadata/core-injected-metadata-common-internal @elastic/kibana-core +packages/core/integrations/core-integrations-browser-internal @elastic/kibana-core +packages/core/integrations/core-integrations-browser-mocks @elastic/kibana-core +packages/core/logging/core-logging-server @elastic/kibana-core +packages/core/logging/core-logging-server-internal @elastic/kibana-core +packages/core/logging/core-logging-server-mocks @elastic/kibana-core +packages/core/metrics/core-metrics-collectors-server-internal @elastic/kibana-core +packages/core/metrics/core-metrics-collectors-server-mocks @elastic/kibana-core +packages/core/metrics/core-metrics-server @elastic/kibana-core +packages/core/metrics/core-metrics-server-internal @elastic/kibana-core +packages/core/metrics/core-metrics-server-mocks @elastic/kibana-core +packages/core/mount-utils/core-mount-utils-browser @elastic/kibana-core +packages/core/mount-utils/core-mount-utils-browser-internal @elastic/kibana-core +packages/core/node/core-node-server @elastic/kibana-core +packages/core/node/core-node-server-internal @elastic/kibana-core +packages/core/node/core-node-server-mocks @elastic/kibana-core +packages/core/notifications/core-notifications-browser @elastic/kibana-core +packages/core/notifications/core-notifications-browser-internal @elastic/kibana-core +packages/core/notifications/core-notifications-browser-mocks @elastic/kibana-core +packages/core/overlays/core-overlays-browser @elastic/kibana-core +packages/core/overlays/core-overlays-browser-internal @elastic/kibana-core +packages/core/overlays/core-overlays-browser-mocks @elastic/kibana-core +packages/core/preboot/core-preboot-server @elastic/kibana-core +packages/core/preboot/core-preboot-server-internal @elastic/kibana-core +packages/core/preboot/core-preboot-server-mocks @elastic/kibana-core +packages/core/rendering/core-rendering-browser-internal @elastic/kibana-core +packages/core/rendering/core-rendering-browser-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-api-browser @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-api-server @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-api-server-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-api-server-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-base-server-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-base-server-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-browser @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-browser-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-browser-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-common @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-import-export-server-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-import-export-server-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-migration-server-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-migration-server-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-server @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-server-internal @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-server-mocks @elastic/kibana-core +packages/core/saved-objects/core-saved-objects-utils-server @elastic/kibana-core +packages/core/status/core-status-common @elastic/kibana-core +packages/core/status/core-status-common-internal @elastic/kibana-core +packages/core/status/core-status-server @elastic/kibana-core +packages/core/status/core-status-server-internal @elastic/kibana-core +packages/core/status/core-status-server-mocks @elastic/kibana-core +packages/core/test-helpers/core-test-helpers-deprecations-getters @elastic/kibana-core +packages/core/test-helpers/core-test-helpers-http-setup-browser @elastic/kibana-core +packages/core/theme/core-theme-browser @elastic/kibana-core +packages/core/theme/core-theme-browser-internal @elastic/kibana-core +packages/core/theme/core-theme-browser-mocks @elastic/kibana-core +packages/core/ui-settings/core-ui-settings-browser @elastic/kibana-core +packages/core/ui-settings/core-ui-settings-browser-internal @elastic/kibana-core +packages/core/ui-settings/core-ui-settings-browser-mocks @elastic/kibana-core +packages/core/ui-settings/core-ui-settings-common @elastic/kibana-core +packages/core/usage-data/core-usage-data-base-server-internal @elastic/kibana-core +packages/core/usage-data/core-usage-data-server @elastic/kibana-core +packages/core/usage-data/core-usage-data-server-internal @elastic/kibana-core +packages/core/usage-data/core-usage-data-server-mocks @elastic/kibana-core +packages/home/sample_data_card @elastic/shared-ux +packages/home/sample_data_tab @elastic/shared-ux +packages/home/sample_data_types @elastic/shared-ux +packages/kbn-ace @elastic/platform-deployment-management +packages/kbn-alerts @elastic/security-solution +packages/kbn-ambient-storybook-types @elastic/kibana-operations +packages/kbn-ambient-ui-types @elastic/kibana-operations +packages/kbn-analytics @elastic/kibana-core +packages/kbn-apm-config-loader @elastic/kibana-core +packages/kbn-apm-synthtrace @elastic/apm-ui +packages/kbn-apm-utils @elastic/apm-ui +packages/kbn-axe-config @elastic/kibana-qa +packages/kbn-babel-plugin-synthetic-packages @elastic/kibana-operations +packages/kbn-babel-preset @elastic/kibana-operations +packages/kbn-bazel-packages @elastic/kibana-operations +packages/kbn-bazel-runner @elastic/kibana-operations +packages/kbn-chart-icons @elastic/kibana-vis-editors +packages/kbn-ci-stats-core @elastic/kibana-operations +packages/kbn-ci-stats-performance-metrics @elastic/kibana-operations +packages/kbn-ci-stats-reporter @elastic/kibana-operations +packages/kbn-cli-dev-mode @elastic/kibana-operations +packages/kbn-coloring @elastic/kibana-vis-editors +packages/kbn-config @elastic/kibana-core +packages/kbn-config-mocks @elastic/kibana-core +packages/kbn-config-schema @elastic/kibana-core +packages/kbn-crypto @elastic/kibana-security +packages/kbn-crypto-browser @elastic/kibana-core +packages/kbn-datemath @elastic/kibana-app-services +packages/kbn-dev-cli-errors @elastic/kibana-operations +packages/kbn-dev-cli-runner @elastic/kibana-operations +packages/kbn-dev-proc-runner @elastic/kibana-operations +packages/kbn-dev-utils @elastic/kibana-operations +packages/kbn-doc-links @elastic/kibana-docs +packages/kbn-docs-utils @elastic/kibana-operations +packages/kbn-ebt-tools @elastic/kibana-core +packages/kbn-es @elastic/kibana-operations +packages/kbn-es-archiver @elastic/kibana-operations +packages/kbn-es-errors @elastic/kibana-core +packages/kbn-es-query @elastic/kibana-app-services +packages/kbn-eslint-config @elastic/kibana-operations +packages/kbn-eslint-plugin-disable @elastic/kibana-operations +packages/kbn-eslint-plugin-eslint @elastic/kibana-operations +packages/kbn-eslint-plugin-imports @elastic/kibana-operations +packages/kbn-expect @elastic/kibana-operations +packages/kbn-field-types @elastic/kibana-app-services +packages/kbn-find-used-node-modules @elastic/kibana-operations +packages/kbn-flot-charts @elastic/kibana-operations +packages/kbn-generate @elastic/kibana-operations +packages/kbn-get-repo-files @elastic/kibana-operations +packages/kbn-handlebars @elastic/kibana-security +packages/kbn-hapi-mocks @elastic/kibana-core +packages/kbn-i18n @elastic/kibana-core +packages/kbn-i18n-react @elastic/kibana-core +packages/kbn-import-resolver @elastic/kibana-operations +packages/kbn-interpreter @elastic/kibana-app-services +packages/kbn-io-ts-utils @elastic/apm-ui +packages/kbn-jest-serializers @elastic/kibana-operations +packages/kbn-kibana-manifest-schema @elastic/kibana-operations +packages/kbn-logging @elastic/kibana-core +packages/kbn-logging-mocks @elastic/kibana-core +packages/kbn-managed-vscode-config @elastic/kibana-operations +packages/kbn-managed-vscode-config-cli @elastic/kibana-operations +packages/kbn-mapbox-gl @elastic/kibana-gis +packages/kbn-monaco @elastic/kibana-app-services +packages/kbn-optimizer @elastic/kibana-operations +packages/kbn-optimizer-webpack-helpers @elastic/kibana-operations +packages/kbn-performance-testing-dataset-extractor @elastic/kibana-performance-testing +packages/kbn-plugin-discovery @elastic/kibana-operations +packages/kbn-plugin-generator @elastic/kibana-operations +packages/kbn-plugin-helpers @elastic/kibana-operations +packages/kbn-react-field @elastic/kibana-app-services +packages/kbn-repo-source-classifier @elastic/kibana-operations +packages/kbn-repo-source-classifier-cli @elastic/kibana-operations +packages/kbn-rule-data-utils @elastic/apm-ui +packages/kbn-safer-lodash-set @elastic/kibana-security +packages/kbn-securitysolution-autocomplete @elastic/security-solution-platform +packages/kbn-securitysolution-es-utils @elastic/security-solution-platform +packages/kbn-securitysolution-hook-utils @elastic/security-solution-platform +packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-solution-platform +packages/kbn-securitysolution-io-ts-list-types @elastic/security-solution-platform +packages/kbn-securitysolution-io-ts-types @elastic/security-solution-platform +packages/kbn-securitysolution-io-ts-utils @elastic/security-solution-platform +packages/kbn-securitysolution-list-api @elastic/security-solution-platform +packages/kbn-securitysolution-list-constants @elastic/security-solution-platform +packages/kbn-securitysolution-list-hooks @elastic/security-solution-platform +packages/kbn-securitysolution-list-utils @elastic/security-solution-platform +packages/kbn-securitysolution-rules @elastic/security-solution-platform +packages/kbn-securitysolution-t-grid @elastic/security-solution-platform +packages/kbn-securitysolution-utils @elastic/security-solution-platform +packages/kbn-server-http-tools @elastic/kibana-core +packages/kbn-server-route-repository @elastic/apm-ui +packages/kbn-shared-svg @elastic/apm-ui +packages/kbn-shared-ux-utility @elastic/shared-ux +packages/kbn-some-dev-log @elastic/kibana-operations +packages/kbn-sort-package-json @elastic/kibana-operations +packages/kbn-spec-to-console @elastic/platform-deployment-management +packages/kbn-std @elastic/kibana-core +packages/kbn-stdio-dev-helpers @elastic/kibana-operations +packages/kbn-storybook @elastic/kibana-operations +packages/kbn-synthetic-package-map @elastic/kibana-operations +packages/kbn-telemetry-tools @elastic/kibana-core +packages/kbn-test @elastic/kibana-operations +packages/kbn-test-jest-helpers @elastic/kibana-operations +packages/kbn-test-subj-selector @elastic/kibana-operations +packages/kbn-timelion-grammar @elastic/kibana-vis-editors +packages/kbn-tinymath @elastic/kibana-vis-editors +packages/kbn-tooling-log @elastic/kibana-operations +packages/kbn-type-summarizer @elastic/kibana-operations +packages/kbn-type-summarizer-cli @elastic/kibana-operations +packages/kbn-type-summarizer-core @elastic/kibana-operations +packages/kbn-typed-react-router-config @elastic/apm-ui +packages/kbn-ui-framework @elastic/kibana-design +packages/kbn-ui-shared-deps-npm @elastic/kibana-operations +packages/kbn-ui-shared-deps-src @elastic/kibana-operations +packages/kbn-ui-theme @elastic/kibana-operations +packages/kbn-user-profile-components @elastic/kibana-security +packages/kbn-utility-types @elastic/kibana-core +packages/kbn-utility-types-jest @elastic/kibana-operations +packages/kbn-utils @elastic/kibana-operations +packages/kbn-yarn-lock-validator @elastic/kibana-operations +packages/shared-ux/avatar/solution @elastic/shared-ux +packages/shared-ux/button_toolbar @elastic/shared-ux +packages/shared-ux/button/exit_full_screen/impl @elastic/shared-ux +packages/shared-ux/button/exit_full_screen/mocks @elastic/shared-ux +packages/shared-ux/button/exit_full_screen/types @elastic/shared-ux +packages/shared-ux/card/no_data/impl @elastic/shared-ux +packages/shared-ux/card/no_data/mocks @elastic/shared-ux +packages/shared-ux/card/no_data/types @elastic/shared-ux +packages/shared-ux/link/redirect_app/impl @elastic/shared-ux +packages/shared-ux/link/redirect_app/mocks @elastic/shared-ux +packages/shared-ux/link/redirect_app/types @elastic/shared-ux +packages/shared-ux/page/analytics_no_data/impl @elastic/shared-ux +packages/shared-ux/page/analytics_no_data/mocks @elastic/shared-ux +packages/shared-ux/page/analytics_no_data/types @elastic/shared-ux +packages/shared-ux/page/kibana_no_data/impl @elastic/shared-ux +packages/shared-ux/page/kibana_no_data/mocks @elastic/shared-ux +packages/shared-ux/page/kibana_no_data/types @elastic/shared-ux +packages/shared-ux/page/kibana_template/impl @elastic/shared-ux +packages/shared-ux/page/kibana_template/mocks @elastic/shared-ux +packages/shared-ux/page/kibana_template/types @elastic/shared-ux +packages/shared-ux/page/no_data_config/impl @elastic/shared-ux +packages/shared-ux/page/no_data_config/mocks @elastic/shared-ux +packages/shared-ux/page/no_data_config/types @elastic/shared-ux +packages/shared-ux/page/no_data/impl @elastic/shared-ux +packages/shared-ux/page/no_data/mocks @elastic/shared-ux +packages/shared-ux/page/no_data/types @elastic/shared-ux +packages/shared-ux/page/solution_nav @elastic/shared-ux +packages/shared-ux/prompt/no_data_views/impl @elastic/shared-ux +packages/shared-ux/prompt/no_data_views/mocks @elastic/shared-ux +packages/shared-ux/prompt/no_data_views/types @elastic/shared-ux +packages/shared-ux/storybook/config @elastic/shared-ux +packages/shared-ux/storybook/mock @elastic/shared-ux +x-pack/packages/ml/agg_utils @elastic/ml-ui +x-pack/packages/ml/aiops_components @elastic/ml-ui +x-pack/packages/ml/aiops_utils @elastic/ml-ui +x-pack/packages/ml/is_populated_object @elastic/ml-ui +x-pack/packages/ml/string_hash @elastic/ml-ui diff --git a/.github/workflows/add-to-apm-project.yml b/.github/workflows/add-to-apm-project.yml index 5df47b56792cd..8b55449060028 100644 --- a/.github/workflows/add-to-apm-project.yml +++ b/.github/workflows/add-to-apm-project.yml @@ -12,36 +12,41 @@ jobs: - uses: octokit/graphql-action@v2.x id: add_to_project with: - headers: '{"GraphQL-Features": "projects_next_graphql"}' query: | mutation add_to_project($projectid:ID!,$contentid:ID!) { - addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { - projectNextItem { - id + addProjectV2ItemById(input:{projectId:$projectid contentId:$contentid}) { + item { + ... on ProjectV2Item { + id + } } } } projectid: ${{ env.PROJECT_ID }} contentid: ${{ github.event.issue.node_id }} env: - PROJECT_ID: "PN_kwDOAGc3Zs0VSg" + PROJECT_ID: "PVT_kwDOAGc3Zs0VSg" GITHUB_TOKEN: ${{ secrets.APM_TECH_KIBANA_USER_TOKEN }} - uses: octokit/graphql-action@v2.x id: label_team with: - headers: '{"GraphQL-Features": "projects_next_graphql"}' query: | mutation label_team($projectid:ID!,$itemid:ID!,$fieldid:ID!,$value:String!) { - updateProjectNextItemField(input: { projectId:$projectid itemId:$itemid fieldId:$fieldid value:$value }) { - projectNextItem { + updateProjectV2ItemFieldValue(input: { projectId:$projectid itemId:$itemid fieldId:$fieldid value:{singleSelectOptionId: $value} }) { + projectV2Item { id + content { + ... on Issue { + number + } + } } } } projectid: ${{ env.PROJECT_ID }} - itemid: ${{ fromJSON(steps.add_to_project.outputs.data).addProjectNextItem.projectNextItem.id }} - fieldid: "MDE2OlByb2plY3ROZXh0RmllbGQ0NDE0Ng==" + itemid: ${{ fromJSON(steps.add_to_project.outputs.data).addProjectV2ItemById.item.id }} + fieldid: "PVTSSF_lADOAGc3Zs0VSs2scg" value: "c33f5c54" env: - PROJECT_ID: "PN_kwDOAGc3Zs0VSg" + PROJECT_ID: "PVT_kwDOAGc3Zs0VSg" GITHUB_TOKEN: ${{ secrets.APM_TECH_KIBANA_USER_TOKEN }} diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index dadf167e8740e..e89fd0e7ed367 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index bff8c3b825061..a6de0eddd53d6 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.devdocs.json b/api_docs/aiops.devdocs.json index d1fca06df35b7..ec6cbc9258812 100644 --- a/api_docs/aiops.devdocs.json +++ b/api_docs/aiops.devdocs.json @@ -48,25 +48,7 @@ "interfaces": [], "enums": [], "misc": [], - "objects": [], - "start": { - "parentPluginId": "aiops", - "id": "def-public.AiopsPluginStart", - "type": "Type", - "tags": [], - "label": "AiopsPluginStart", - "description": [ - "\naiops plugin public start contract" - ], - "signature": [ - "void" - ], - "path": "x-pack/plugins/aiops/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "lifecycle": "start", - "initialIsOpen": true - } + "objects": [] }, "server": { "classes": [], diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 689c0f3dea5c1..db20b98ed0f6a 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; @@ -21,13 +21,10 @@ Contact [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 9 | 0 | 0 | 1 | +| 7 | 0 | 0 | 1 | ## Client -### Start - - ### Functions diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 5eecc7808e303..21eec3051632e 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -2810,6 +2810,16 @@ "section": "def-common.IExecutionLogResult", "text": "IExecutionLogResult" }, + ">; getGlobalExecutionLogWithAuth: ({ dateStart, dateEnd, filter, page, perPage, sort, }: ", + "GetGlobalExecutionLogParams", + ") => Promise<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.IExecutionLogResult", + "text": "IExecutionLogResult" + }, ">; getActionErrorLog: ({ id, dateStart, dateEnd, filter, page, perPage, sort, }: ", "GetActionErrorLogByIdParams", ") => Promise<", @@ -4128,6 +4138,17 @@ "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.IExecutionLog.rule_id", + "type": "string", + "tags": [], + "label": "rule_id", + "description": [], + "path": "x-pack/plugins/alerting/common/execution_log_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index a05850bed5d32..d46ff5aa181b5 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 368 | 0 | 359 | 21 | +| 369 | 0 | 360 | 22 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index f5b4ceba305d8..6c0d9f74ea580 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -792,7 +792,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services/{serviceName}/serviceNodes\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"POST /internal/apm/correlations/field_stats/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\"" + "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services/{serviceName}/serviceNodes\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"POST /internal/apm/correlations/field_stats/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\"" ], "path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -1064,6 +1064,154 @@ "SpanLinkDetails", "[]; }, ", "APMRouteCreateOptions", + ">; \"GET /internal/apm/storage_explorer_summary_stats\": ", + "ServerRoute", + "<\"GET /internal/apm/storage_explorer_summary_stats\", ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ indexLifecyclePhase: ", + "UnionC", + "<[", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".All>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Hot>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Warm>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Cold>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Frozen>]>; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>, ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + "NonEmptyStringBrand", + ">]>; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", + "Type", + "; end: ", + "Type", + "; }>]>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { tracesPerMinute: number; numberOfServices: number; estimatedSize: number; dailyDataGeneration: number; }, ", + "APMRouteCreateOptions", + ">; \"GET /internal/apm/storage_explorer/privileges\": ", + "ServerRoute", + "<\"GET /internal/apm/storage_explorer/privileges\", undefined, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { hasPrivileges: boolean; }, ", + "APMRouteCreateOptions", + ">; \"GET /internal/apm/storage_chart\": ", + "ServerRoute", + "<\"GET /internal/apm/storage_chart\", ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ indexLifecyclePhase: ", + "UnionC", + "<[", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".All>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Hot>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Warm>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Cold>, ", + "LiteralC", + "<", + "IndexLifecyclePhaseSelectOption", + ".Frozen>]>; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>, ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + "NonEmptyStringBrand", + ">]>; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", + "Type", + "; end: ", + "Type", + "; }>]>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { storageTimeSeries: { serviceName: string; timeseries: { x: number; y: number; }[]; }[]; }, ", + "APMRouteCreateOptions", ">; \"GET /internal/apm/services/{serviceName}/storage_details\": ", "ServerRoute", "<\"GET /internal/apm/services/{serviceName}/storage_details\", ", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 25fe4d5efea10..371251c3d2f6b 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 78291085e75c2..48441c7541f20 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 3aa8cd01a8314..051acd0e75900 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 2f71a9aa930fa..a0952458e0f45 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 5f4998268d334..dbd163dff3005 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.devdocs.json b/api_docs/charts.devdocs.json index 18bb2aa2a806a..7131b518535f3 100644 --- a/api_docs/charts.devdocs.json +++ b/api_docs/charts.devdocs.json @@ -670,6 +670,51 @@ "children": [], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-public.Warnings", + "type": "Function", + "tags": [], + "label": "Warnings", + "description": [], + "signature": [ + "({ warnings }: { warnings: React.ReactNode[]; }) => JSX.Element | null" + ], + "path": "src/plugins/charts/public/static/components/warnings.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-public.Warnings.$1", + "type": "Object", + "tags": [], + "label": "{ warnings }", + "description": [], + "path": "src/plugins/charts/public/static/components/warnings.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-public.Warnings.$1.warnings", + "type": "Array", + "tags": [], + "label": "warnings", + "description": [], + "signature": [ + "React.ReactNode[]" + ], + "path": "src/plugins/charts/public/static/components/warnings.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 0d3e659e8b785..94a6853c57cce 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 261 | 2 | 246 | 9 | +| 264 | 2 | 249 | 9 | ## Client diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 14a4c1d38c53e..67f888cd10fab 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 5a3a2a236e7fc..2d93e7b6a7ef6 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 3df1c839b0f08..885e264937e68 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index d29e799e72fd0..07fbe27b7c048 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index 35d2b408052f5..44e4755f2f47d 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -576,6 +576,10 @@ "plugin": "@kbn/core-analytics-server-internal", "path": "packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.ts" + }, { "plugin": "security", "path": "x-pack/plugins/security/server/analytics/analytics_service.test.ts" @@ -588,6 +592,18 @@ "plugin": "@kbn/core-analytics-server-mocks", "path": "packages/core/analytics/core-analytics-server-mocks/src/analytics_service.mock.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" @@ -918,6 +934,10 @@ "plugin": "@kbn/core-execution-context-browser-internal", "path": "packages/core/execution-context/core-execution-context-browser-internal/src/execution_context_service.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.ts" + }, { "plugin": "cloud", "path": "x-pack/plugins/cloud/public/plugin.test.ts" @@ -982,6 +1002,14 @@ "plugin": "@kbn/core-elasticsearch-server-internal", "path": "packages/core/elasticsearch/core-elasticsearch-server-internal/src/register_analytics_context_provider.test.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.mocks.ts" @@ -18603,6 +18631,10 @@ "plugin": "@kbn/core-analytics-server-internal", "path": "packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.ts" + }, { "plugin": "security", "path": "x-pack/plugins/security/server/analytics/analytics_service.test.ts" @@ -18615,6 +18647,18 @@ "plugin": "@kbn/core-analytics-server-mocks", "path": "packages/core/analytics/core-analytics-server-mocks/src/analytics_service.mock.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" @@ -18945,6 +18989,10 @@ "plugin": "@kbn/core-execution-context-browser-internal", "path": "packages/core/execution-context/core-execution-context-browser-internal/src/execution_context_service.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.ts" + }, { "plugin": "cloud", "path": "x-pack/plugins/cloud/public/plugin.test.ts" @@ -19009,6 +19057,14 @@ "plugin": "@kbn/core-elasticsearch-server-internal", "path": "packages/core/elasticsearch/core-elasticsearch-server-internal/src/register_analytics_context_provider.test.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/status_service.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.mocks.ts" @@ -21123,13 +21179,7 @@ "{@link StatusServiceSetup}" ], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.StatusServiceSetup", - "text": "StatusServiceSetup" - } + "StatusServiceSetup" ], "path": "src/core/server/index.ts", "deprecated": false, @@ -21383,7 +21433,10 @@ "description": [ "\nStatus of core services.\n" ], - "path": "src/core/server/status/types.ts", + "signature": [ + "CoreStatus" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -21398,7 +21451,7 @@ "ServiceStatus", "" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -21413,7 +21466,7 @@ "ServiceStatus", "" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false } @@ -36027,6 +36080,14 @@ "plugin": "@kbn/core-metrics-server-internal", "path": "packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts" }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/routes/status.ts" + }, + { + "plugin": "@kbn/core-status-server-internal", + "path": "packages/core/status/core-status-server-internal/src/routes/status.ts" + }, { "plugin": "@kbn/core-usage-data-server-internal", "path": "packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts" @@ -46420,7 +46481,7 @@ "ServiceStatus", "" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -46436,7 +46497,7 @@ "signature": [ "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46449,7 +46510,7 @@ "description": [ "\nA high-level summary of the service status." ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46465,7 +46526,7 @@ "signature": [ "string | undefined" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46481,7 +46542,7 @@ "signature": [ "string | undefined" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46497,7 +46558,7 @@ "signature": [ "Meta | undefined" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false } @@ -46906,7 +46967,10 @@ "description": [ "\nAPI for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status.\n" ], - "path": "src/core/server/status/types.ts", + "signature": [ + "StatusServiceSetup" + ], + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -46922,16 +46986,10 @@ "signature": [ "Observable", "<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreStatus", - "text": "CoreStatus" - }, + "CoreStatus", ">" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46950,7 +47008,7 @@ "ServiceStatus", ">" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -46970,7 +47028,7 @@ "ServiceStatus", ">) => void" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -46987,7 +47045,7 @@ "ServiceStatus", ">" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -47010,7 +47068,7 @@ "ServiceStatus", ">>" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -47029,7 +47087,7 @@ "ServiceStatus", ">" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -47045,7 +47103,7 @@ "signature": [ "() => boolean" ], - "path": "src/core/server/status/types.ts", + "path": "node_modules/@types/kbn__core-status-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -52214,7 +52272,7 @@ "signature": [ "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -52405,7 +52463,7 @@ "signature": [ "{ readonly available: Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }>; readonly degraded: Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }>; readonly unavailable: Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }>; readonly critical: Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>; }" ], - "path": "node_modules/@types/kbn__core-base-common/index.d.ts", + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 541ca6affdbc5..9d6e782717324 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2657 | 1 | 61 | 2 | +| 2657 | 1 | 58 | 2 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index d2c87cab417d3..604f24762a12b 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 9cc5265026ffb..454aa521d5942 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 45a942a1b27f0..125f6303237f3 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index d3115cfccb73c..bb06789a9d338 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3131 | 34 | 2441 | 23 | +| 3144 | 34 | 2444 | 23 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index acc0894eabca6..e862a2588c40d 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3131 | 34 | 2441 | 23 | +| 3144 | 34 | 2444 | 23 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 652737b0a23a4..399c9e5b7f188 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -10605,6 +10605,281 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets", + "type": "Class", + "tags": [], + "label": "TimeBuckets", + "description": [ + "\nHelper class for wrapping the concept of an \"Interval\",\nwhich describes a timespan that will separate moments.\n" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "timeBucketConfig", + "description": [], + "signature": [ + "TimeBucketsConfig" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.setBounds", + "type": "Function", + "tags": [], + "label": "setBounds", + "description": [ + "\nSet the bounds that these buckets are expected to cover.\nThis is required to support interval \"auto\" as well\nas interval scaling.\n" + ], + "signature": [ + "(input?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + " | ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + "[] | undefined) => void" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.setBounds.$1", + "type": "CompoundType", + "tags": [], + "label": "input", + "description": [ + "- an object with properties min and max,\nrepresenting the edges for the time span\nwe should cover" + ], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + " | ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + "[] | undefined" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.clearBounds", + "type": "Function", + "tags": [ + "return" + ], + "label": "clearBounds", + "description": [ + "\nClear the stored bounds\n" + ], + "signature": [ + "() => void" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.hasBounds", + "type": "Function", + "tags": [ + "return" + ], + "label": "hasBounds", + "description": [ + "\nCheck to see if we have received bounds yet\n" + ], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.getBounds", + "type": "Function", + "tags": [ + "return" + ], + "label": "getBounds", + "description": [ + "\nReturn the current bounds, if we have any.\n\nTHIS DOES NOT CLONE THE BOUNDS, so editing them\nmay have unexpected side-effects. Always\ncall bounds.min.clone() before editing\n" + ], + "signature": [ + "() => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + " | undefined" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [ + "- If bounds are not defined, this\nreturns undefined, else it returns the bounds\nfor these buckets. This object has two props,\nmin and max. Each property will be a moment()\nobject" + ] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.setInterval", + "type": "Function", + "tags": [], + "label": "setInterval", + "description": [ + "\nUpdate the interval at which buckets should be\ngenerated.\n\nInput can be one of the following:\n - Any object from src/legacy/ui/agg_types.js\n - \"auto\"\n - Pass a valid moment unit\n" + ], + "signature": [ + "(input: string | Record | null) => void" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.setInterval.$1", + "type": "CompoundType", + "tags": [], + "label": "input", + "description": [ + "- see desc" + ], + "signature": [ + "string | Record | null" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.getInterval", + "type": "Function", + "tags": [], + "label": "getInterval", + "description": [ + "\nGet the interval for the buckets. If the\nnumber of buckets created by the interval set\nis larger than config:histogram:maxBars then the\ninterval will be scaled up. If the number of buckets\ncreated is less than one, the interval is scaled back.\n\nThe interval object returned is a moment.duration\nobject that has been decorated with the following\nproperties.\n\ninterval.description: a text description of the interval.\n designed to be used list \"field per {{ desc }}\".\n - \"minute\"\n - \"10 days\"\n - \"3 years\"\n\ninterval.expression: the elasticsearch expression that creates this\n interval. If the interval does not properly form an elasticsearch\n expression it will be forced into one.\n\ninterval.scaled: the interval was adjusted to\n accommodate the maxBars setting.\n\ninterval.scale: the number that y-values should be\n multiplied by" + ], + "signature": [ + "(useNormalizedEsInterval?: boolean) => TimeBucketsInterval" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.getInterval.$1", + "type": "boolean", + "tags": [], + "label": "useNormalizedEsInterval", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.TimeBuckets.getScaledDateFormat", + "type": "Function", + "tags": [ + "return" + ], + "label": "getScaledDateFormat", + "description": [ + "\nGet a date format string that will represent dates that\nprogress at our interval.\n\nSince our interval can be as small as 1ms, the default\ndate format is usually way too much. with `dateFormat:scaled`\nusers can modify how dates are formatted within series\nproduced by TimeBuckets\n" + ], + "signature": [ + "() => string" + ], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false } ], "functions": [ @@ -24123,7 +24398,13 @@ "label": "buckets", "description": [], "signature": [ - "TimeBuckets" + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.TimeBuckets", + "text": "TimeBuckets" + } ], "path": "src/plugins/data/common/search/aggs/buckets/date_histogram.ts", "deprecated": false, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 1eb654fe6b51a..247b776e53e30 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3131 | 34 | 2441 | 23 | +| 3144 | 34 | 2444 | 23 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 06a7e6674ad4c..594122e246d10 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index f5aac30bac571..2881eb78d6fbd 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 02ba97f233424..fe964392680cd 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 5166413c81d6a..35957ede2cc4f 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 0f90354db488e..63c9c3051d4e4 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 4b61218ebf768..d9ba036f7bbe6 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -74,7 +74,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | savedObjectsTaggingOss, dashboard | 8.8.0 | | | dashboard | 8.8.0 | | | maps, dashboard, @kbn/core-saved-objects-migration-server-internal | 8.8.0 | -| | monitoring, kibanaUsageCollection, @kbn/core-metrics-server-internal, @kbn/core-usage-data-server-internal | 8.8.0 | +| | monitoring, kibanaUsageCollection, @kbn/core-metrics-server-internal, @kbn/core-status-server-internal, @kbn/core-usage-data-server-internal | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index d9da58e4a98dd..07f70de66a166 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -70,6 +70,14 @@ so TS and code-reference navigation might not highlight them. | +## @kbn/core-status-server-internal + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [status.ts](https://github.com/elastic/kibana/tree/main/packages/core/status/core-status-server-internal/src/routes/status.ts#:~:text=process), [status.ts](https://github.com/elastic/kibana/tree/main/packages/core/status/core-status-server-internal/src/routes/status.ts#:~:text=process) | 8.8.0 | + + + ## @kbn/core-usage-data-server-internal | Deprecated API | Reference location(s) | Remove By | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 3027321eb6245..4a9e44b189a79 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -80,7 +80,7 @@ so TS and code-reference navigation might not highlight them. | Note to maintainers: when looking at usages, mind that typical use could be inside a `catch` block, so TS and code-reference navigation might not highlight them. | | @kbn/core-saved-objects-migration-server-internal | | [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/document_migrator.test.ts#:~:text=warning), [migration_logger.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/migration_logger.ts#:~:text=warning) | 8.8.0 | -| @kbn/core-metrics-server-internal | | [ops_metrics_collector.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts#:~:text=process), [get_ops_metrics_log.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts#:~:text=process), [get_ops_metrics_log.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process) | 8.8.0 | +| @kbn/core-metrics-server-internal | | [ops_metrics_collector.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts#:~:text=process), [get_ops_metrics_log.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts#:~:text=process), [get_ops_metrics_log.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts#:~:text=process), [status.ts](https://github.com/elastic/kibana/tree/main/packages/core/status/core-status-server-internal/src/routes/status.ts#:~:text=process), [status.ts](https://github.com/elastic/kibana/tree/main/packages/core/status/core-status-server-internal/src/routes/status.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process), [core_usage_data_service.ts](https://github.com/elastic/kibana/tree/main/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts#:~:text=process) | 8.8.0 | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 7152c3dbc6e48..6ab2af56de8a6 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 1964aab8a60e7..c21e6fc7a14bd 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 5811b914930df..844946af12dba 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 70caf3a3423d2..6d0f630e84048 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 96d0b7e56b548..cf1334edf5db3 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 3b1f766d5d43f..31c161302239c 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 322441b1ad852..337602bc8deb6 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index d2b0950778bfe..88767bde098f9 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.devdocs.json b/api_docs/event_annotation.devdocs.json index debcba1bf6599..45ace5e5039e6 100644 --- a/api_docs/event_annotation.devdocs.json +++ b/api_docs/event_annotation.devdocs.json @@ -58,6 +58,61 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "eventAnnotation", + "id": "def-public.isQueryAnnotationConfig", + "type": "Function", + "tags": [], + "label": "isQueryAnnotationConfig", + "description": [], + "signature": [ + "(annotation?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.EventAnnotationConfig", + "text": "EventAnnotationConfig" + }, + " | undefined) => annotation is ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.QueryPointEventAnnotationConfig", + "text": "QueryPointEventAnnotationConfig" + } + ], + "path": "src/plugins/event_annotation/public/event_annotation_service/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventAnnotation", + "id": "def-public.isQueryAnnotationConfig.$1", + "type": "CompoundType", + "tags": [], + "label": "annotation", + "description": [], + "signature": [ + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.EventAnnotationConfig", + "text": "EventAnnotationConfig" + }, + " | undefined" + ], + "path": "src/plugins/event_annotation/public/event_annotation_service/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "eventAnnotation", "id": "def-public.isRangeAnnotationConfig", @@ -142,14 +197,15 @@ "section": "def-common.EventAnnotationConfig", "text": "EventAnnotationConfig" }, - ") => ", + "[]) => ", { "pluginId": "expressions", "scope": "common", "docId": "kibExpressionsPluginApi", "section": "def-common.ExpressionAstExpression", "text": "ExpressionAstExpression" - } + }, + "[]" ], "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", "deprecated": false, @@ -158,7 +214,7 @@ { "parentPluginId": "eventAnnotation", "id": "def-public.EventAnnotationServiceType.toExpression.$1", - "type": "CompoundType", + "type": "Array", "tags": [], "label": "props", "description": [], @@ -169,7 +225,8 @@ "docId": "kibEventAnnotationPluginApi", "section": "def-common.EventAnnotationConfig", "text": "EventAnnotationConfig" - } + }, + "[]" ], "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", "deprecated": false, @@ -178,6 +235,84 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "eventAnnotation", + "id": "def-public.EventAnnotationServiceType.toFetchExpression", + "type": "Function", + "tags": [], + "label": "toFetchExpression", + "description": [], + "signature": [ + "(props: { interval: string; groups: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.EventAnnotationGroupConfig", + "text": "EventAnnotationGroupConfig" + }, + "[]; }) => ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionAstExpression", + "text": "ExpressionAstExpression" + }, + "[]" + ], + "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventAnnotation", + "id": "def-public.EventAnnotationServiceType.toFetchExpression.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventAnnotation", + "id": "def-public.EventAnnotationServiceType.toFetchExpression.$1.interval", + "type": "string", + "tags": [], + "label": "interval", + "description": [], + "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "eventAnnotation", + "id": "def-public.EventAnnotationServiceType.toFetchExpression.$1.groups", + "type": "Array", + "tags": [], + "label": "groups", + "description": [], + "signature": [ + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.EventAnnotationGroupConfig", + "text": "EventAnnotationGroupConfig" + }, + "[]" + ], + "path": "src/plugins/event_annotation/public/event_annotation_service/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -375,6 +510,52 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "eventAnnotation", + "id": "def-common.EventAnnotationGroupConfig", + "type": "Interface", + "tags": [], + "label": "EventAnnotationGroupConfig", + "description": [], + "path": "src/plugins/event_annotation/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventAnnotation", + "id": "def-common.EventAnnotationGroupConfig.annotations", + "type": "Array", + "tags": [], + "label": "annotations", + "description": [], + "signature": [ + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.EventAnnotationConfig", + "text": "EventAnnotationConfig" + }, + "[]" + ], + "path": "src/plugins/event_annotation/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "eventAnnotation", + "id": "def-common.EventAnnotationGroupConfig.indexPatternId", + "type": "string", + "tags": [], + "label": "indexPatternId", + "description": [], + "path": "src/plugins/event_annotation/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "eventAnnotation", "id": "def-common.FetchEventAnnotationsArgs", @@ -538,8 +719,17 @@ "label": "ManualPointEventAnnotationArgs", "description": [], "signature": [ - "{ id: string; time: string; } & ", - "PointStyleProps" + "{ id: string; time: string; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -554,9 +744,17 @@ "label": "ManualPointEventAnnotationOutput", "description": [], "signature": [ - "{ id: string; time: string; } & ", - "PointStyleProps", - " & { type: \"manual_point_event_annotation\"; }" + "{ id: string; time: string; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; } & { type: \"manual_point_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -565,14 +763,13 @@ }, { "parentPluginId": "eventAnnotation", - "id": "def-common.ManualPointEventAnnotationRow", + "id": "def-common.ManualRangeEventAnnotationArgs", "type": "Type", "tags": [], - "label": "ManualPointEventAnnotationRow", + "label": "ManualRangeEventAnnotationArgs", "description": [], "signature": [ - "{ id: string; time: string; type: \"point\"; timebucket: string; skippedCount?: string | undefined; } & ", - "PointStyleProps" + "{ id: string; time: string; endTime: string; } & StyleSharedProps & { outside?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -581,14 +778,13 @@ }, { "parentPluginId": "eventAnnotation", - "id": "def-common.ManualRangeEventAnnotationArgs", + "id": "def-common.ManualRangeEventAnnotationOutput", "type": "Type", "tags": [], - "label": "ManualRangeEventAnnotationArgs", + "label": "ManualRangeEventAnnotationOutput", "description": [], "signature": [ - "{ id: string; time: string; endTime: string; } & ", - "RangeStyleProps" + "{ id: string; time: string; endTime: string; } & StyleSharedProps & { outside?: boolean | undefined; } & { type: \"manual_range_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -597,15 +793,13 @@ }, { "parentPluginId": "eventAnnotation", - "id": "def-common.ManualRangeEventAnnotationOutput", + "id": "def-common.ManualRangeEventAnnotationRow", "type": "Type", "tags": [], - "label": "ManualRangeEventAnnotationOutput", + "label": "ManualRangeEventAnnotationRow", "description": [], "signature": [ - "{ id: string; time: string; endTime: string; } & ", - "RangeStyleProps", - " & { type: \"manual_range_event_annotation\"; }" + "{ id: string; time: string; endTime: string; type: \"range\"; } & StyleSharedProps & { outside?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -614,14 +808,23 @@ }, { "parentPluginId": "eventAnnotation", - "id": "def-common.ManualRangeEventAnnotationRow", + "id": "def-common.PointEventAnnotationRow", "type": "Type", "tags": [], - "label": "ManualRangeEventAnnotationRow", + "label": "PointEventAnnotationRow", "description": [], "signature": [ - "{ id: string; time: string; endTime: string; type: \"range\"; } & ", - "RangeStyleProps" + "{ id: string; time: string; type: \"point\"; timebucket: string; skippedCount?: number | undefined; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; } & Record" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/types.ts", "deprecated": false, @@ -636,8 +839,17 @@ "label": "PointInTimeEventAnnotationConfig", "description": [], "signature": [ - "{ id: string; key: { type: \"point_in_time\"; timestamp: string; }; } & ", - "PointStyleProps" + "{ id: string; type: \"manual\"; key: { type: \"point_in_time\"; timestamp: string; }; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/types.ts", "deprecated": false, @@ -660,8 +872,17 @@ "section": "def-common.KibanaQueryOutput", "text": "KibanaQueryOutput" }, - "; timeField: string; extraFields?: string[] | undefined; textField?: string | undefined; } & ", - "PointStyleProps" + "; timeField?: string | undefined; extraFields?: string[] | undefined; textField?: string | undefined; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/query_point_event_annotation/types.ts", "deprecated": false, @@ -676,7 +897,7 @@ "label": "QueryPointEventAnnotationConfig", "description": [], "signature": [ - "{ id: string; filter: ", + "{ id: string; type: \"query\"; filter: ", { "pluginId": "data", "scope": "common", @@ -684,8 +905,17 @@ "section": "def-common.KibanaQueryOutput", "text": "KibanaQueryOutput" }, - "; timeField: string; textField: string; extraFields?: string[] | undefined; key: { type: \"point_in_time\"; }; } & ", - "PointStyleProps" + "; timeField?: string | undefined; textField?: string | undefined; extraFields?: string[] | undefined; key: { type: \"point_in_time\"; }; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/types.ts", "deprecated": false, @@ -708,9 +938,17 @@ "section": "def-common.KibanaQueryOutput", "text": "KibanaQueryOutput" }, - "; timeField: string; extraFields?: string[] | undefined; textField?: string | undefined; } & ", - "PointStyleProps", - " & { type: \"query_point_event_annotation\"; }" + "; timeField?: string | undefined; extraFields?: string[] | undefined; textField?: string | undefined; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; } & { type: \"query_point_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/query_point_event_annotation/types.ts", "deprecated": false, @@ -725,8 +963,7 @@ "label": "RangeEventAnnotationConfig", "description": [], "signature": [ - "{ id: string; key: { type: \"range\"; timestamp: string; endTimestamp: string; }; } & ", - "RangeStyleProps" + "{ type: \"manual\"; id: string; key: { type: \"range\"; timestamp: string; endTimestamp: string; }; } & StyleSharedProps & { outside?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/types.ts", "deprecated": false, @@ -1240,7 +1477,7 @@ "section": "def-common.ManualPointEventAnnotationArgs", "text": "ManualPointEventAnnotationArgs" }, - ") => { id: string; time: string; label: string; color?: string | undefined; icon?: ", + ") => { id: string; time: string; label: string; color?: string | undefined; isHidden?: boolean | undefined; icon?: ", { "pluginId": "eventAnnotation", "scope": "common", @@ -1250,7 +1487,7 @@ }, " | undefined; lineWidth?: number | undefined; lineStyle?: ", "LineStyle", - " | undefined; textVisibility?: boolean | undefined; isHidden?: boolean | undefined; type: \"manual_point_event_annotation\"; }" + " | undefined; textVisibility?: boolean | undefined; type: \"manual_point_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/index.ts", "deprecated": false, @@ -1279,8 +1516,17 @@ "label": "args", "description": [], "signature": [ - "{ id: string; time: string; } & ", - "PointStyleProps" + "{ id: string; time: string; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/index.ts", "deprecated": false, @@ -1706,7 +1952,7 @@ "section": "def-common.ManualRangeEventAnnotationArgs", "text": "ManualRangeEventAnnotationArgs" }, - ") => { id: string; time: string; endTime: string; label: string; color?: string | undefined; outside?: boolean | undefined; isHidden?: boolean | undefined; type: \"manual_range_event_annotation\"; }" + ") => { id: string; time: string; endTime: string; label: string; color?: string | undefined; isHidden?: boolean | undefined; outside?: boolean | undefined; type: \"manual_range_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/index.ts", "deprecated": false, @@ -1735,8 +1981,7 @@ "label": "args", "description": [], "signature": [ - "{ id: string; time: string; endTime: string; } & ", - "RangeStyleProps" + "{ id: string; time: string; endTime: string; } & StyleSharedProps & { outside?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/index.ts", "deprecated": false, @@ -1923,20 +2168,6 @@ "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "eventAnnotation", - "id": "def-common.queryPointEventAnnotation.args.filter.required", - "type": "boolean", - "tags": [], - "label": "required", - "description": [], - "signature": [ - "true" - ], - "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", - "deprecated": false, - "trackAdoption": false } ] }, @@ -2003,20 +2234,6 @@ "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "eventAnnotation", - "id": "def-common.queryPointEventAnnotation.args.timeField.required", - "type": "boolean", - "tags": [], - "label": "required", - "description": [], - "signature": [ - "true" - ], - "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "eventAnnotation", "id": "def-common.queryPointEventAnnotation.args.timeField.types", @@ -2416,7 +2633,7 @@ "section": "def-common.KibanaQueryOutput", "text": "KibanaQueryOutput" }, - "; timeField: string; extraFields?: string[] | undefined; textField?: string | undefined; label: string; color?: string | undefined; icon?: ", + "; timeField?: string | undefined; extraFields?: string[] | undefined; textField?: string | undefined; label: string; color?: string | undefined; isHidden?: boolean | undefined; icon?: ", { "pluginId": "eventAnnotation", "scope": "common", @@ -2426,7 +2643,7 @@ }, " | undefined; lineWidth?: number | undefined; lineStyle?: ", "LineStyle", - " | undefined; textVisibility?: boolean | undefined; isHidden?: boolean | undefined; type: \"query_point_event_annotation\"; }" + " | undefined; textVisibility?: boolean | undefined; type: \"query_point_event_annotation\"; }" ], "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", "deprecated": false, @@ -2463,8 +2680,17 @@ "section": "def-common.KibanaQueryOutput", "text": "KibanaQueryOutput" }, - "; timeField: string; extraFields?: string[] | undefined; textField?: string | undefined; } & ", - "PointStyleProps" + "; timeField?: string | undefined; extraFields?: string[] | undefined; textField?: string | undefined; } & StyleSharedProps & { icon?: ", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.AvailableAnnotationIcon", + "text": "AvailableAnnotationIcon" + }, + " | undefined; lineWidth?: number | undefined; lineStyle?: ", + "LineStyle", + " | undefined; textVisibility?: boolean | undefined; }" ], "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", "deprecated": false, diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 0b17864905a2e..bfb01ed547536 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 163 | 0 | 163 | 5 | +| 170 | 0 | 170 | 3 | ## Client diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index 8e717efd4ad23..491bb3f165423 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -696,6 +696,48 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "eventLog", + "id": "def-server.ClusterClientAdapter.aggregateEventsWithAuthFilter", + "type": "Function", + "tags": [], + "label": "aggregateEventsWithAuthFilter", + "description": [], + "signature": [ + "(queryOptions: ", + "AggregateEventsWithAuthFilter", + ") => Promise<", + { + "pluginId": "eventLog", + "scope": "server", + "docId": "kibEventLogPluginApi", + "section": "def-server.AggregateEventsBySavedObjectResult", + "text": "AggregateEventsBySavedObjectResult" + }, + ">" + ], + "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventLog", + "id": "def-server.ClusterClientAdapter.aggregateEventsWithAuthFilter.$1", + "type": "Object", + "tags": [], + "label": "queryOptions", + "description": [], + "signature": [ + "AggregateEventsWithAuthFilter" + ], + "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -1007,6 +1049,82 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "eventLog", + "id": "def-server.IEventLogClient.aggregateEventsWithAuthFilter", + "type": "Function", + "tags": [], + "label": "aggregateEventsWithAuthFilter", + "description": [], + "signature": [ + "(type: string, authFilter: ", + "KueryNode", + ", options?: Partial<", + "AggregateOptionsType", + "> | undefined) => Promise<", + { + "pluginId": "eventLog", + "scope": "server", + "docId": "kibEventLogPluginApi", + "section": "def-server.AggregateEventsBySavedObjectResult", + "text": "AggregateEventsBySavedObjectResult" + }, + ">" + ], + "path": "x-pack/plugins/event_log/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "eventLog", + "id": "def-server.IEventLogClient.aggregateEventsWithAuthFilter.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/event_log/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "eventLog", + "id": "def-server.IEventLogClient.aggregateEventsWithAuthFilter.$2", + "type": "Object", + "tags": [], + "label": "authFilter", + "description": [], + "signature": [ + "KueryNode" + ], + "path": "x-pack/plugins/event_log/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "eventLog", + "id": "def-server.IEventLogClient.aggregateEventsWithAuthFilter.$3", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Partial<", + "AggregateOptionsType", + "> | undefined" + ], + "path": "x-pack/plugins/event_log/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index cf1ddf2891d12..67fc800a393f5 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 100 | 0 | 100 | 9 | +| 106 | 0 | 106 | 10 | ## Server diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 06faf9fb410ec..55e41dee84df0 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 20e186f5644ce..272f14fa331c1 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 404f0688b7d1d..883e69752d7c2 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 4fd5a55f50085..2fd851509964a 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index d7358588b94c0..dd3e3a7c1b9c4 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 75b2eb2c6f778..ec3bd2846e1a8 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 63c77f4efce12..323af21664b16 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 99a7e75a8a1de..ce8b70c802891 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 02146ab3b4dd6..44d11e482c899 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index c992509c3b90a..170818628b752 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 157d67a02113c..ba84f73e3531a 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index b43acd78e213a..a1190ad13be1a 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index 9f270c01df433..86ab5bf035752 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -426,92 +426,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "expressionXY", - "id": "def-common.CollectiveConfig", - "type": "Interface", - "tags": [], - "label": "CollectiveConfig", - "description": [], - "signature": [ - { - "pluginId": "expressionXY", - "scope": "common", - "docId": "kibExpressionXYPluginApi", - "section": "def-common.CollectiveConfig", - "text": "CollectiveConfig" - }, - " extends Omit<", - { - "pluginId": "eventAnnotation", - "scope": "common", - "docId": "kibEventAnnotationPluginApi", - "section": "def-common.ManualPointEventAnnotationArgs", - "text": "ManualPointEventAnnotationArgs" - }, - ", \"icon\">" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "expressionXY", - "id": "def-common.CollectiveConfig.timebucket", - "type": "number", - "tags": [], - "label": "timebucket", - "description": [], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "expressionXY", - "id": "def-common.CollectiveConfig.position", - "type": "string", - "tags": [], - "label": "position", - "description": [], - "signature": [ - "\"bottom\"" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "expressionXY", - "id": "def-common.CollectiveConfig.icon", - "type": "string", - "tags": [], - "label": "icon", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "expressionXY", - "id": "def-common.CollectiveConfig.customTooltipDetails", - "type": "Function", - "tags": [], - "label": "customTooltipDetails", - "description": [], - "signature": [ - "AnnotationTooltipFormatter", - " | undefined" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "expressionXY", "id": "def-common.DataDecorationConfig", @@ -1058,6 +972,119 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation", + "type": "Interface", + "tags": [], + "label": "MergedAnnotation", + "description": [], + "signature": [ + { + "pluginId": "expressionXY", + "scope": "common", + "docId": "kibExpressionXYPluginApi", + "section": "def-common.MergedAnnotation", + "text": "MergedAnnotation" + }, + " extends Omit<", + { + "pluginId": "eventAnnotation", + "scope": "common", + "docId": "kibEventAnnotationPluginApi", + "section": "def-common.ManualPointEventAnnotationArgs", + "text": "ManualPointEventAnnotationArgs" + }, + ", \"icon\">" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.timebucket", + "type": "number", + "tags": [], + "label": "timebucket", + "description": [], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.position", + "type": "string", + "tags": [], + "label": "position", + "description": [], + "signature": [ + "\"bottom\"" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.icon", + "type": "string", + "tags": [], + "label": "icon", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.customTooltipDetails", + "type": "Function", + "tags": [], + "label": "customTooltipDetails", + "description": [], + "signature": [ + "(details?: string | undefined) => JSX.Element | null" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.customTooltipDetails.$1", + "type": "string", + "tags": [], + "label": "details", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@elastic/charts/dist/chart_types/xy_chart/annotations/types.d.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "expressionXY", + "id": "def-common.MergedAnnotation.isGrouped", + "type": "boolean", + "tags": [], + "label": "isGrouped", + "description": [], + "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "expressionXY", "id": "def-common.ReferenceLineDecorationConfig", @@ -1479,21 +1506,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "expressionXY", - "id": "def-common.XYArgs.annotationLayers", - "type": "Array", - "tags": [], - "label": "annotationLayers", - "description": [], - "signature": [ - "AnnotationLayerConfigResult", - "[]" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "expressionXY", "id": "def-common.XYArgs.fittingFunction", @@ -2587,9 +2599,7 @@ "docId": "kibExpressionXYPluginApi", "section": "def-common.ReferenceLineLayerConfigResult", "text": "ReferenceLineLayerConfigResult" - }, - " | ", - "ExtendedAnnotationLayerConfigResult" + } ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, @@ -2612,15 +2622,15 @@ "text": "DataLayerArgs" }, " | ", + "ReferenceLineArgs", + " | ", { "pluginId": "expressionXY", "scope": "common", "docId": "kibExpressionXYPluginApi", "section": "def-common.AnnotationLayerArgs", "text": "AnnotationLayerArgs" - }, - " | ", - "ReferenceLineArgs" + } ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index f5aa37c47d8bd..e58dcecd23eb3 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 152 | 0 | 142 | 11 | +| 153 | 0 | 142 | 9 | ## Client diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index e170014e3ffc7..be93903834285 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 1dc8dd28ad100..7084d5dd8614b 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index cca2af12379c4..a3a2ffc66d724 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 7d7005d146d32..5acb5094dcbe3 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 6a97b8d21b89b..d2657f3258452 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 0600244c7d591..267549af75838 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -8914,14 +8914,14 @@ { "parentPluginId": "fleet", "id": "def-common.FleetServerAgent.upgraded_at", - "type": "CompoundType", + "type": "string", "tags": [], "label": "upgraded_at", "description": [ "\nDate/time the Elastic Agent was last upgraded" ], "signature": [ - "string | null | undefined" + "string | undefined" ], "path": "x-pack/plugins/fleet/common/types/models/agent.ts", "deprecated": false, @@ -8943,6 +8943,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "fleet", + "id": "def-common.FleetServerAgent.upgrade_status", + "type": "CompoundType", + "tags": [], + "label": "upgrade_status", + "description": [ + "\nUpgrade status" + ], + "signature": [ + "\"completed\" | \"started\" | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/agent.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "fleet", "id": "def-common.FleetServerAgent.access_api_key_id", @@ -9111,7 +9127,7 @@ "tags": [], "label": "last_checkin_status", "description": [ - "\nLst checkin status" + "\nLast checkin status" ], "signature": [ "\"error\" | \"online\" | \"updating\" | \"degraded\" | undefined" diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 7c71b34ed84e5..deb8f6162875b 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 969 | 3 | 873 | 10 | +| 970 | 3 | 873 | 10 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index b034c8b7cb9e0..3318c54d6b6f9 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 4c9eda5502b86..ad5da6eb52715 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index f034d977a97fc..a566a62d1f846 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 15c82e01c6a32..5285ccf531912 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 5926c936d8864..96c41a5268b4d 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 6935d2be13e2d..28ccaee3ae731 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 8f4d5abc9e06a..e2601319591a4 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 121e38701329c..1e71960980198 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 6d5933c4337e9..f55d67c1fc3dc 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 54ba172cc6220..89439f8a35a2a 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 2c6d1d0c39de0..132c55a0f5320 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 7ec9a3e17c035..023dcb2b4bb36 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 9a511085c8190..e491c482cb629 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index ec0dee99a8470..c4ba600af1dab 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 2e503510d978f..bb77b10ad5c55 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index ae8f78c83ab2b..1aa6719c6e313 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index b9bd132b73244..180b6a7699ad3 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index c4f8b6213ed6d..4b863396dbcf4 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index a61909d3c5481..01a492a721e05 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index c51c92f4e555b..14b01e3c7be4a 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 257134279eba5..a129e1b408e44 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 3145ac4c1a5ac..dc390f25f0170 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 2ffa59cb7e82b..741443fe91e35 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 2686b6b92199b..40098db9d607f 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 9f99819517f57..4d457a94dff83 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 296dc4ec77bac..75ad6cf16351d 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 94885afcb9162..988bba727b23c 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index e5bf6e1dbfb83..f308239b6d475 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 710c8557fec4e..0714ab8c48ebf 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 59050347d3e1d..1cd0b626f5510 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 4de5f236edc48..7777a057c2894 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 17666254be1d6..b85fd73d4c901 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index d1ab2d84b53c3..8937453ad90ef 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 90bc6b5afe237..21d0c98603bd5 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 9d850e4df759d..5bf553a6b4e27 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index c5d70f92b8750..84e37926b9d2e 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 6cc7d0b4866db..08601a43abe02 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 79e8c34727b5d..42e241da77343 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 44d5b180671ba..5600e432ddde8 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 5a94eee634d9a..36b74b926858b 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 915ba4547bc97..53392790ff321 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.devdocs.json b/api_docs/kbn_core_base_common.devdocs.json index 5a164327bf338..e5fae7dc02d76 100644 --- a/api_docs/kbn_core_base_common.devdocs.json +++ b/api_docs/kbn_core_base_common.devdocs.json @@ -142,109 +142,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus", - "type": "Interface", - "tags": [], - "label": "ServiceStatus", - "description": [ - "\nThe current status of a service at a point in time.\n" - ], - "signature": [ - { - "pluginId": "@kbn/core-base-common", - "scope": "server", - "docId": "kibKbnCoreBaseCommonPluginApi", - "section": "def-server.ServiceStatus", - "text": "ServiceStatus" - }, - "" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus.level", - "type": "CompoundType", - "tags": [], - "label": "level", - "description": [ - "\nThe current availability level of the service." - ], - "signature": [ - "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus.summary", - "type": "string", - "tags": [], - "label": "summary", - "description": [ - "\nA high-level summary of the service status." - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus.detail", - "type": "string", - "tags": [], - "label": "detail", - "description": [ - "\nA more detailed description of the service status." - ], - "signature": [ - "string | undefined" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus.documentationUrl", - "type": "string", - "tags": [], - "label": "documentationUrl", - "description": [ - "\nA URL to open in a new tab about how to resolve or troubleshoot the problem." - ], - "signature": [ - "string | undefined" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatus.meta", - "type": "Uncategorized", - "tags": [], - "label": "meta", - "description": [ - "\nAny JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained,\nmachine-readable information about the service status. May include status information for underlying features." - ], - "signature": [ - "Meta | undefined" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false } ], "enums": [ @@ -308,44 +205,9 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatusLevel", - "type": "Type", - "tags": [], - "label": "ServiceStatusLevel", - "description": [ - "\nA convenience type that represents the union of each value in {@link ServiceStatusLevels}." - ], - "signature": [ - "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false } ], - "objects": [ - { - "parentPluginId": "@kbn/core-base-common", - "id": "def-server.ServiceStatusLevels", - "type": "Object", - "tags": [], - "label": "ServiceStatusLevels", - "description": [ - "\nThe current \"level\" of availability of a service.\n" - ], - "signature": [ - "{ readonly available: Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }>; readonly degraded: Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }>; readonly unavailable: Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }>; readonly critical: Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>; }" - ], - "path": "packages/core/base/core-base-common/src/service_status.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ] + "objects": [] }, "common": { "classes": [], diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 2531446c382d6..4596828c8a7f7 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; @@ -21,13 +21,10 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 20 | 0 | 3 | 0 | +| 12 | 0 | 3 | 0 | ## Server -### Objects - - ### Interfaces diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 327ea656abef0..8c56c35df8037 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 141177c4ca7d8..e369bc6cf9573 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 9a72ce6e4bb89..a02516d41e635 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index cb1ecb2091a92..4cf9fbbbd4890 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 91e709440cd60..ed7cb4116ff68 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 1ece686b0cdae..d6275075f4f2d 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index b7d9a4fcabb3c..8b8b3352b3e03 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index b13ad6b1d385c..26b3493760d23 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 828ecc68dc87d..6aeec78a9de0b 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 8d21dcce86b57..cb3d85753a695 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 9dc3dc1beaa13..3f5b097166b70 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 4fd817133f4a4..c3308a653104f 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 764220009c8c7..b77b28d64156f 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 5280bca156242..a4c4c5108d54e 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 4f1b395e88650..6d190e7921922 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index f18fe6d87767e..adcb25e4e3b65 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 0ef4b6124c5a7..a2fc67402f23e 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 9a1b47c956f7b..125a8ddb62eb2 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 775dfd58560ec..490c09706c8aa 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 5ec3e9e167890..8507ca2cb3903 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 4d0b32a2df870..9aa393700f1cc 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index a7c596885d134..64475b318c506 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index e09bb332ab2e5..3b16e74a4edd2 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 13e1bfaed6534..19bafc1b112da 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index d16344d577654..2afd793f95c19 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index b09975fb0b72e..7e71e344bbf24 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index ee59dc70817d6..36cab5deb51a2 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index f6dea3c2cc1de..1a6af787887d5 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index a26edda217b05..ca61b99d2dbab 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 9fd9f56b7aab3..177fc4c26f4ae 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 8e8d4a6de07ba..4200bb38465cc 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index ecc1b11cd5720..b95726bae4f8c 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 4d2cdfc603e74..96baa43f4f803 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 1c512cf314176..9d90489b193a2 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index cb87d0d5251cb..dc43d1ef8ebe2 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 6193837154b11..72d95cc2173bb 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 90a5aabdce8e6..2604bdf1307f6 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 9d4d347bf3d57..a70c8ebdd2038 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 4a14e036c3bfd..090d7dea7fc90 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 1b84ca589506b..062468ef28b45 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 35a8438855f43..13666c7455aa7 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 5e0c00cb2c821..8f122bb2f056e 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 10a9676bf1c52..7bf289e5bff95 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index cc27eb8d1928f..d5afd2459cb6a 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index a6a4ea0f0dabb..643fc21c88e99 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 4bc5202d1ecbb..337c83d0c71ed 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index dc6fe251ed8c8..41b228a782728 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 96a1d3df89f07..23037163144c5 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 50e3d68961707..13949e3052dc5 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 5f59dd23c1b7e..35d1ab22608d6 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index d357516e5ab34..0145a71c0d7b2 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 2749fcaffc1ea..87346ec4fa5b7 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index adec64f1dfcbc..d9ba50274fda1 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index db081dbbe83b8..90e86fc55e65b 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 37bd05952de41..60f31535742b4 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index da345f5bb47c4..302e26129c712 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 6cef90a03ccc2..337f272c28fe5 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 82846fbbdc165..8d4b519f59c14 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 3eb7c726c850c..196c31459d3f2 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index bd93c7b2140cd..730e06acc610b 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 670ecb0047f88..11c953906f0dc 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 6599bf7ea1ddc..a5ba2bcf8c3cc 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 93282d37b7425..75f3667db69e4 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index d2c851b81781a..ace513c8f3fd6 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 77d6ff98aa62d..ee9a394184d4e 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 66355017e7862..5333d41e14584 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index a9b52f33294cc..4a89b92fff036 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 19f30064d6411..14247d429667c 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 352ec11d502c9..0e8f65ff9da07 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 1c62b624f27e2..52483dfa4dc6c 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 9733b1627a6f7..857037bab1093 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 7119b50426bcf..b2767486e6592 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 96f8c80736c1e..6afb7d61f7327 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index c89e256f923c2..23ae70cfd0c9f 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 0d82287ffaa81..7a3a3d003792f 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 4905bd829e23a..23db97bef04a9 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 105532e5fb3b5..f7574d68fb4aa 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 437f5d3e1d6e7..fb970d77d9a9b 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 35f9be419d40c..71eb12db08bbc 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 5102602665f5b..d84f5c5c73c7e 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index d78c3da1e3c01..93ab71cd6d0c2 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 0b40e8600a5b8..089cea7cbcd40 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 586e42b8bf893..ac8a36714f9c9 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 4f927190b7dd3..afa0e5688428d 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index ab33dc4970f31..fbecff8bedc44 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 96477a0e7c797..56315c20f6063 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 9e65224dc4a22..ae5b78204d1b8 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 5169834e61594..0b3f8c2f078bc 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 0da4633a1f7d5..e2f35211ba038 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 878e93723383b..c13d91d1c3d3b 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index b50e4f961b89e..6e430a5fa2611 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index f5b7858d33f53..06e6a2ecbe278 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 9d97b89c2f36c..f6aecda23d6f1 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 415cc81dd6cdc..5fac3e3216d8a 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.devdocs.json b/api_docs/kbn_core_status_common.devdocs.json new file mode 100644 index 0000000000000..b49607b86963e --- /dev/null +++ b/api_docs/kbn_core_status_common.devdocs.json @@ -0,0 +1,242 @@ +{ + "id": "@kbn/core-status-common", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.CoreStatus", + "type": "Interface", + "tags": [], + "label": "CoreStatus", + "description": [ + "\nStatus of core services.\n" + ], + "path": "packages/core/status/core-status-common/src/core_status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.CoreStatus.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common", + "scope": "common", + "docId": "kibKbnCoreStatusCommonPluginApi", + "section": "def-common.ServiceStatus", + "text": "ServiceStatus" + }, + "" + ], + "path": "packages/core/status/core-status-common/src/core_status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.CoreStatus.savedObjects", + "type": "Object", + "tags": [], + "label": "savedObjects", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common", + "scope": "common", + "docId": "kibKbnCoreStatusCommonPluginApi", + "section": "def-common.ServiceStatus", + "text": "ServiceStatus" + }, + "" + ], + "path": "packages/core/status/core-status-common/src/core_status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus", + "type": "Interface", + "tags": [], + "label": "ServiceStatus", + "description": [ + "\nThe current status of a service at a point in time.\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-status-common", + "scope": "common", + "docId": "kibKbnCoreStatusCommonPluginApi", + "section": "def-common.ServiceStatus", + "text": "ServiceStatus" + }, + "" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus.level", + "type": "CompoundType", + "tags": [], + "label": "level", + "description": [ + "\nThe current availability level of the service." + ], + "signature": [ + "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus.summary", + "type": "string", + "tags": [], + "label": "summary", + "description": [ + "\nA high-level summary of the service status." + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus.detail", + "type": "string", + "tags": [], + "label": "detail", + "description": [ + "\nA more detailed description of the service status." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus.documentationUrl", + "type": "string", + "tags": [], + "label": "documentationUrl", + "description": [ + "\nA URL to open in a new tab about how to resolve or troubleshoot the problem." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatus.meta", + "type": "Uncategorized", + "tags": [], + "label": "meta", + "description": [ + "\nAny JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained,\nmachine-readable information about the service status. May include status information for underlying features." + ], + "signature": [ + "Meta | undefined" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatusLevel", + "type": "Type", + "tags": [], + "label": "ServiceStatusLevel", + "description": [ + "\nA convenience type that represents the union of each value in {@link ServiceStatusLevels}." + ], + "signature": [ + "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatusLevelId", + "type": "Type", + "tags": [], + "label": "ServiceStatusLevelId", + "description": [ + "\nPossible values for the ID of a {@link ServiceStatusLevel}\n" + ], + "signature": [ + "\"critical\" | \"degraded\" | \"unavailable\" | \"available\"" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/core-status-common", + "id": "def-common.ServiceStatusLevels", + "type": "Object", + "tags": [], + "label": "ServiceStatusLevels", + "description": [ + "\nThe current \"level\" of availability of a service.\n" + ], + "signature": [ + "{ readonly available: Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }>; readonly degraded: Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }>; readonly unavailable: Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }>; readonly critical: Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>; }" + ], + "path": "packages/core/status/core-status-common/src/service_status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx new file mode 100644 index 0000000000000..1743916cfc825 --- /dev/null +++ b/api_docs/kbn_core_status_common.mdx @@ -0,0 +1,36 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreStatusCommonPluginApi +slug: /kibana-dev-docs/api/kbn-core-status-common +title: "@kbn/core-status-common" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-status-common plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] +--- +import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 12 | 0 | 2 | 0 | + +## Common + +### Objects + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_status_common_internal.devdocs.json b/api_docs/kbn_core_status_common_internal.devdocs.json new file mode 100644 index 0000000000000..39b4d63b9e25a --- /dev/null +++ b/api_docs/kbn_core_status_common_internal.devdocs.json @@ -0,0 +1,355 @@ +{ + "id": "@kbn/core-status-common-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerVersion", + "type": "Interface", + "tags": [], + "label": "ServerVersion", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerVersion.number", + "type": "string", + "tags": [], + "label": "number", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerVersion.build_hash", + "type": "string", + "tags": [], + "label": "build_hash", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerVersion.build_number", + "type": "number", + "tags": [], + "label": "build_number", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerVersion.build_snapshot", + "type": "boolean", + "tags": [], + "label": "build_snapshot", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfo", + "type": "Interface", + "tags": [], + "label": "StatusInfo", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfo.overall", + "type": "Object", + "tags": [], + "label": "overall", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + } + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfo.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + "{ elasticsearch: ", + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + "; savedObjects: ", + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + "; }" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfo.plugins", + "type": "Object", + "tags": [], + "label": "plugins", + "description": [], + "signature": [ + "{ [x: string]: ", + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + "; }" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfoServiceStatus", + "type": "Interface", + "tags": [], + "label": "StatusInfoServiceStatus", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + " extends Omit<", + "ServiceStatus", + ", \"level\">" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfoServiceStatus.level", + "type": "CompoundType", + "tags": [], + "label": "level", + "description": [], + "signature": [ + "\"critical\" | \"degraded\" | \"unavailable\" | \"available\"" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse", + "type": "Interface", + "tags": [], + "label": "StatusResponse", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse.uuid", + "type": "string", + "tags": [], + "label": "uuid", + "description": [], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse.version", + "type": "Object", + "tags": [], + "label": "version", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.ServerVersion", + "text": "ServerVersion" + } + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse.status", + "type": "Object", + "tags": [], + "label": "status", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfo", + "text": "StatusInfo" + } + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusResponse.metrics", + "type": "CompoundType", + "tags": [], + "label": "metrics", + "description": [], + "signature": [ + "Omit<", + "OpsMetrics", + ", \"collected_at\"> & { last_updated: string; collection_interval_in_millis: number; requests: { status_codes: Record; }; }" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.ServerMetrics", + "type": "Type", + "tags": [], + "label": "ServerMetrics", + "description": [], + "signature": [ + "Omit<", + "OpsMetrics", + ", \"collected_at\"> & { last_updated: string; collection_interval_in_millis: number; requests: { status_codes: Record; }; }" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-common-internal", + "id": "def-common.StatusInfoCoreStatus", + "type": "Type", + "tags": [], + "label": "StatusInfoCoreStatus", + "description": [ + "\nCopy all the services listed in CoreStatus with their specific ServiceStatus declarations\nbut overwriting the `level` to its stringified version." + ], + "signature": [ + "{ elasticsearch: ", + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + "; savedObjects: ", + { + "pluginId": "@kbn/core-status-common-internal", + "scope": "common", + "docId": "kibKbnCoreStatusCommonInternalPluginApi", + "section": "def-common.StatusInfoServiceStatus", + "text": "StatusInfoServiceStatus" + }, + "; }" + ], + "path": "packages/core/status/core-status-common-internal/src/status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx new file mode 100644 index 0000000000000..f815aa153ef38 --- /dev/null +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreStatusCommonInternalPluginApi +slug: /kibana-dev-docs/api/kbn-core-status-common-internal +title: "@kbn/core-status-common-internal" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-status-common-internal plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] +--- +import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 19 | 0 | 18 | 0 | + +## Common + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_status_server.devdocs.json b/api_docs/kbn_core_status_server.devdocs.json new file mode 100644 index 0000000000000..6438e27aa69d3 --- /dev/null +++ b/api_docs/kbn_core_status_server.devdocs.json @@ -0,0 +1,378 @@ +{ + "id": "@kbn/core-status-server", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.CoreStatus", + "type": "Interface", + "tags": [], + "label": "CoreStatus", + "description": [ + "\nStatus of core services.\n" + ], + "signature": [ + "CoreStatus" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.CoreStatus.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + "ServiceStatus", + "" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.CoreStatus.savedObjects", + "type": "Object", + "tags": [], + "label": "savedObjects", + "description": [], + "signature": [ + "ServiceStatus", + "" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus", + "type": "Interface", + "tags": [], + "label": "ServiceStatus", + "description": [ + "\nThe current status of a service at a point in time.\n" + ], + "signature": [ + "ServiceStatus", + "" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus.level", + "type": "CompoundType", + "tags": [], + "label": "level", + "description": [ + "\nThe current availability level of the service." + ], + "signature": [ + "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus.summary", + "type": "string", + "tags": [], + "label": "summary", + "description": [ + "\nA high-level summary of the service status." + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus.detail", + "type": "string", + "tags": [], + "label": "detail", + "description": [ + "\nA more detailed description of the service status." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus.documentationUrl", + "type": "string", + "tags": [], + "label": "documentationUrl", + "description": [ + "\nA URL to open in a new tab about how to resolve or troubleshoot the problem." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatus.meta", + "type": "Uncategorized", + "tags": [], + "label": "meta", + "description": [ + "\nAny JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained,\nmachine-readable information about the service status. May include status information for underlying features." + ], + "signature": [ + "Meta | undefined" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup", + "type": "Interface", + "tags": [], + "label": "StatusServiceSetup", + "description": [ + "\nAPI for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status.\n" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.core$", + "type": "Object", + "tags": [], + "label": "core$", + "description": [ + "\nCurrent status for all Core services." + ], + "signature": [ + "Observable", + "<", + "CoreStatus", + ">" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.overall$", + "type": "Object", + "tags": [], + "label": "overall$", + "description": [ + "\nOverall system status for all of Kibana.\n" + ], + "signature": [ + "Observable", + "<", + "ServiceStatus", + ">" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.set", + "type": "Function", + "tags": [], + "label": "set", + "description": [ + "\nAllows a plugin to specify a custom status dependent on its own criteria.\nCompletely overrides the default inherited status.\n" + ], + "signature": [ + "(status$: ", + "Observable", + "<", + "ServiceStatus", + ">) => void" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.set.$1", + "type": "Object", + "tags": [], + "label": "status$", + "description": [], + "signature": [ + "Observable", + "<", + "ServiceStatus", + ">" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.dependencies$", + "type": "Object", + "tags": [], + "label": "dependencies$", + "description": [ + "\nCurrent status for all plugins this plugin depends on.\nEach key of the `Record` is a plugin id." + ], + "signature": [ + "Observable", + ">>" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.derivedStatus$", + "type": "Object", + "tags": [], + "label": "derivedStatus$", + "description": [ + "\nThe status of this plugin as derived from its dependencies.\n" + ], + "signature": [ + "Observable", + "<", + "ServiceStatus", + ">" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.StatusServiceSetup.isStatusPageAnonymous", + "type": "Function", + "tags": [], + "label": "isStatusPageAnonymous", + "description": [ + "\nWhether or not the status HTTP APIs are available to unauthenticated users when an authentication provider is\npresent." + ], + "signature": [ + "() => boolean" + ], + "path": "packages/core/status/core-status-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatusLevel", + "type": "Type", + "tags": [], + "label": "ServiceStatusLevel", + "description": [ + "\nA convenience type that represents the union of each value in {@link ServiceStatusLevels}." + ], + "signature": [ + "Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }> | Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }> | Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }> | Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatusLevelId", + "type": "Type", + "tags": [], + "label": "ServiceStatusLevelId", + "description": [ + "\nPossible values for the ID of a {@link ServiceStatusLevel}\n" + ], + "signature": [ + "\"critical\" | \"degraded\" | \"unavailable\" | \"available\"" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/core-status-server", + "id": "def-server.ServiceStatusLevels", + "type": "Object", + "tags": [], + "label": "ServiceStatusLevels", + "description": [ + "\nThe current \"level\" of availability of a service.\n" + ], + "signature": [ + "{ readonly available: Readonly<{ toString: () => \"available\"; valueOf: () => 0; toJSON: () => \"available\"; }>; readonly degraded: Readonly<{ toString: () => \"degraded\"; valueOf: () => 1; toJSON: () => \"degraded\"; }>; readonly unavailable: Readonly<{ toString: () => \"unavailable\"; valueOf: () => 2; toJSON: () => \"unavailable\"; }>; readonly critical: Readonly<{ toString: () => \"critical\"; valueOf: () => 3; toJSON: () => \"critical\"; }>; }" + ], + "path": "node_modules/@types/kbn__core-status-common/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx new file mode 100644 index 0000000000000..30a90ae7a976a --- /dev/null +++ b/api_docs/kbn_core_status_server.mdx @@ -0,0 +1,36 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreStatusServerPluginApi +slug: /kibana-dev-docs/api/kbn-core-status-server +title: "@kbn/core-status-server" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-status-server plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] +--- +import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 20 | 0 | 1 | 0 | + +## Server + +### Objects + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_status_server_internal.devdocs.json b/api_docs/kbn_core_status_server_internal.devdocs.json new file mode 100644 index 0000000000000..d24cd2e143830 --- /dev/null +++ b/api_docs/kbn_core_status_server_internal.devdocs.json @@ -0,0 +1,440 @@ +{ + "id": "@kbn/core-status-server-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService", + "type": "Class", + "tags": [], + "label": "StatusService", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-server-internal", + "scope": "server", + "docId": "kibKbnCoreStatusServerInternalPluginApi", + "section": "def-server.StatusService", + "text": "StatusService" + }, + " implements ", + "CoreService", + "<", + "InternalStatusServiceSetup", + ", void>" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "coreContext", + "description": [], + "signature": [ + "CoreContext" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "({ analytics, elasticsearch, pluginDependencies, http, metrics, savedObjects, environment, coreUsageData, }: ", + { + "pluginId": "@kbn/core-status-server-internal", + "scope": "server", + "docId": "kibKbnCoreStatusServerInternalPluginApi", + "section": "def-server.StatusServiceSetupDeps", + "text": "StatusServiceSetupDeps" + }, + ") => Promise<{ core$: ", + "Observable", + "<", + "CoreStatus", + ">; coreOverall$: ", + "Observable", + "<", + "ServiceStatus", + ">; overall$: ", + "Observable", + "<", + "ServiceStatus", + ">; plugins: { set: (plugin: string, status$: ", + "Observable", + "<", + "ServiceStatus", + ">) => void; getDependenciesStatus$: (plugin: string) => ", + "Observable", + ">>; getDerivedStatus$: (plugin: string) => ", + "Observable", + "<", + "ServiceStatus", + ">; }; isStatusPageAnonymous: () => boolean; }>" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.setup.$1", + "type": "Object", + "tags": [], + "label": "{\n analytics,\n elasticsearch,\n pluginDependencies,\n http,\n metrics,\n savedObjects,\n environment,\n coreUsageData,\n }", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-status-server-internal", + "scope": "server", + "docId": "kibKbnCoreStatusServerInternalPluginApi", + "section": "def-server.StatusServiceSetupDeps", + "text": "StatusServiceSetupDeps" + } + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusService.stop", + "type": "Function", + "tags": [], + "label": "stop", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.registerStatusRoute", + "type": "Function", + "tags": [], + "label": "registerStatusRoute", + "description": [], + "signature": [ + "({ router, config, metrics, status, incrementUsageCounter, }: Deps) => void" + ], + "path": "packages/core/status/core-status-server-internal/src/routes/status.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.registerStatusRoute.$1", + "type": "Object", + "tags": [], + "label": "{\n router,\n config,\n metrics,\n status,\n incrementUsageCounter,\n}", + "description": [], + "signature": [ + "Deps" + ], + "path": "packages/core/status/core-status-server-internal/src/routes/status.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps", + "type": "Interface", + "tags": [], + "label": "StatusServiceSetupDeps", + "description": [], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.analytics", + "type": "Object", + "tags": [], + "label": "analytics", + "description": [], + "signature": [ + "{ optIn: (optInConfig: ", + "OptInConfig", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + "Observable", + "<", + "TelemetryCounter", + ">; registerEventType: (eventTypeOps: ", + "EventTypeOpts", + ") => void; registerShipper: (Shipper: ", + "ShipperClassConstructor", + ", shipperConfig: ShipperConfig, opts?: ", + "RegisterShipperOpts", + " | undefined) => void; registerContextProvider: (contextProviderOpts: ", + "ContextProviderOpts", + ") => void; removeContextProvider: (contextProviderName: string) => void; }" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + "{ status$: ", + "Observable", + "<", + "ServiceStatus", + "<", + "ElasticsearchStatusMeta", + ">>; }" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.environment", + "type": "Object", + "tags": [], + "label": "environment", + "description": [], + "signature": [ + "InternalEnvironmentServicePreboot" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.pluginDependencies", + "type": "Object", + "tags": [], + "label": "pluginDependencies", + "description": [], + "signature": [ + "ReadonlyMap" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + "InternalHttpServiceSetup" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.metrics", + "type": "Object", + "tags": [], + "label": "metrics", + "description": [], + "signature": [ + "MetricsServiceSetup" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.savedObjects", + "type": "Object", + "tags": [], + "label": "savedObjects", + "description": [], + "signature": [ + "{ status$: ", + "Observable", + "<", + "ServiceStatus", + "<", + "SavedObjectStatusMeta", + ">>; }" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusServiceSetupDeps.coreUsageData", + "type": "Object", + "tags": [], + "label": "coreUsageData", + "description": [], + "signature": [ + "{ incrementUsageCounter: ", + "CoreIncrementUsageCounter", + "; }" + ], + "path": "packages/core/status/core-status-server-internal/src/status_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.StatusConfigType", + "type": "Type", + "tags": [], + "label": "StatusConfigType", + "description": [], + "signature": [ + "{ readonly allowAnonymous: boolean; }" + ], + "path": "packages/core/status/core-status-server-internal/src/status_config.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.statusConfig", + "type": "Object", + "tags": [], + "label": "statusConfig", + "description": [], + "path": "packages/core/status/core-status-server-internal/src/status_config.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.statusConfig.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/core/status/core-status-server-internal/src/status_config.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-status-server-internal", + "id": "def-server.statusConfig.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [], + "signature": [ + "ObjectType", + "<{ allowAnonymous: ", + "Type", + "; }>" + ], + "path": "packages/core/status/core-status-server-internal/src/status_config.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx new file mode 100644 index 0000000000000..1b1bcdac498dd --- /dev/null +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -0,0 +1,42 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreStatusServerInternalPluginApi +slug: /kibana-dev-docs/api/kbn-core-status-server-internal +title: "@kbn/core-status-server-internal" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-status-server-internal plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] +--- +import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 22 | 0 | 22 | 1 | + +## Server + +### Objects + + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_status_server_mocks.devdocs.json b/api_docs/kbn_core_status_server_mocks.devdocs.json new file mode 100644 index 0000000000000..85f331d9e7e27 --- /dev/null +++ b/api_docs/kbn_core_status_server_mocks.devdocs.json @@ -0,0 +1,94 @@ +{ + "id": "@kbn/core-status-server-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/core-status-server-mocks", + "id": "def-server.statusServiceMock", + "type": "Object", + "tags": [], + "label": "statusServiceMock", + "description": [], + "path": "packages/core/status/core-status-server-mocks/src/status_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-status-server-mocks", + "id": "def-server.statusServiceMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.Mocked" + ], + "path": "packages/core/status/core-status-server-mocks/src/status_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-status-server-mocks", + "id": "def-server.statusServiceMock.createSetupContract", + "type": "Function", + "tags": [], + "label": "createSetupContract", + "description": [], + "signature": [ + "() => jest.Mocked<", + "StatusServiceSetup", + ">" + ], + "path": "packages/core/status/core-status-server-mocks/src/status_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-status-server-mocks", + "id": "def-server.statusServiceMock.createInternalSetupContract", + "type": "Function", + "tags": [], + "label": "createInternalSetupContract", + "description": [], + "signature": [ + "() => jest.Mocked<", + "InternalStatusServiceSetup", + ">" + ], + "path": "packages/core/status/core-status-server-mocks/src/status_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx new file mode 100644 index 0000000000000..d2a4f74edd929 --- /dev/null +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreStatusServerMocksPluginApi +slug: /kibana-dev-docs/api/kbn-core-status-server-mocks +title: "@kbn/core-status-server-mocks" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-status-server-mocks plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] +--- +import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 0 | + +## Server + +### Objects + + diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index aaf942ddf2b32..7fecf1916746c 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 83e3c09e6f322..794d64d7e64e9 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 62e874e46202f..f0d5725c883fa 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 9537871eeff34..65edbdba1a628 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index beebf1fdcc401..d9f583d515cfc 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index ef2df898310fa..925ad46a92610 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 09ed2bcd433de..10165c0389dd6 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 9a154dff10020..fed0b546122cd 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index b49506a87cfaa..0732415264371 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index d2da3e366390d..5052cc57dc19e 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 81d238bf3469e..79a90a07cb013 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 0a453d4f36607..60c5375cd230d 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index ceca8f916777d..34d1cab6fdcee 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 5c73a9981b76c..cc0de7274d49f 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index cc1aaa0a7f76a..59692fd76e326 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 53170339b30df..b29893225438d 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index c9ba13f62781d..a95ffcdf1e28b 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index e6e3a5e84ba39..5daa6d13cf6a3 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 964c46dd397ff..e76afe4af60ff 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index cf429b729b807..4dcf21464cd01 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -300,7 +300,7 @@ "label": "enterpriseSearch", "description": [], "signature": [ - "{ readonly apiKeys: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly crawlerGettingStarted: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly languageAnalyzers: string; readonly licenseManagement: string; readonly mailService: string; readonly start: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" + "{ readonly apiKeys: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly crawlerGettingStarted: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly mailService: string; readonly start: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 318be11d0b4be..0fde4b55f119e 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index ea3c7f329c54d..2644e4c3b5e71 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 80a62e78497f5..beefe799a31bf 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 7e7a2fc26faf1..59951f466f831 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index c66768a2aa70e..cce2805a4412c 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index fbf8f231c74b8..db4ae2018ca5b 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 737f01b338245..28ee9c2cff91c 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 4f006611e9fc3..136a0dd3d55f0 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 999bfe5e1ae72..eb5455dd7d830 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 08ba76ffa1001..40b7ac766e87a 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 0eb9adf03cae8..57561b5cbc50f 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index f2548afb38b8f..d2c265790b9b4 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 71c5afc499e71..2bf014a652c69 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index fe9f7362f92d9..c2c4c4d0337a8 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 260ed51eb714a..4049be82b4dfb 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 4831d49ebd44d..306996d8dcc16 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 38cd87cb6c7d2..e7387cd969eff 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 9165151e477f1..dbc89d69930c2 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 28459bf26edb1..943a7e8f6ee37 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index ba94c00e027fe..42d8da1b6627e 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_parser.devdocs.json b/api_docs/kbn_kibana_manifest_parser.devdocs.json deleted file mode 100644 index 2a179421fb304..0000000000000 --- a/api_docs/kbn_kibana_manifest_parser.devdocs.json +++ /dev/null @@ -1,174 +0,0 @@ -{ - "id": "@kbn/kibana-manifest-parser", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [ - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.parseKibanaManifest", - "type": "Function", - "tags": [], - "label": "parseKibanaManifest", - "description": [ - "\nParse a kibana.jsonc file from a string" - ], - "signature": [ - "(content: string) => ", - { - "pluginId": "@kbn/kibana-manifest-parser", - "scope": "server", - "docId": "kibKbnKibanaManifestParserPluginApi", - "section": "def-server.KibanaPackageManifest", - "text": "KibanaPackageManifest" - } - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.parseKibanaManifest.$1", - "type": "string", - "tags": [], - "label": "content", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.readKibanaManifest", - "type": "Function", - "tags": [], - "label": "readKibanaManifest", - "description": [ - "\nParse a kibana.jsonc file from the filesystem" - ], - "signature": [ - "(path: string) => ", - { - "pluginId": "@kbn/kibana-manifest-parser", - "scope": "server", - "docId": "kibKbnKibanaManifestParserPluginApi", - "section": "def-server.KibanaPackageManifest", - "text": "KibanaPackageManifest" - } - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.readKibanaManifest.$1", - "type": "string", - "tags": [], - "label": "path", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.validateKibanaManifest", - "type": "Function", - "tags": [], - "label": "validateKibanaManifest", - "description": [ - "\nValidate the contents of a parsed kibana.jsonc file." - ], - "signature": [ - "(parsed: unknown) => ", - { - "pluginId": "@kbn/kibana-manifest-parser", - "scope": "server", - "docId": "kibKbnKibanaManifestParserPluginApi", - "section": "def-server.KibanaPackageManifest", - "text": "KibanaPackageManifest" - } - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.validateKibanaManifest.$1", - "type": "Unknown", - "tags": [], - "label": "parsed", - "description": [], - "signature": [ - "unknown" - ], - "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [], - "enums": [], - "misc": [ - { - "parentPluginId": "@kbn/kibana-manifest-parser", - "id": "def-server.KibanaPackageManifest", - "type": "Type", - "tags": [], - "label": "KibanaPackageManifest", - "description": [], - "signature": [ - "PluginPackageManifest", - " | ", - "SharedBrowserPackageManifest", - " | ", - "BasePackageManifest" - ], - "path": "packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/kbn_kibana_manifest_parser.mdx b/api_docs/kbn_kibana_manifest_parser.mdx deleted file mode 100644 index 021c15b48ce90..0000000000000 --- a/api_docs/kbn_kibana_manifest_parser.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -#### -#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. -#### Reach out in #docs-engineering for more info. -#### -id: kibKbnKibanaManifestParserPluginApi -slug: /kibana-dev-docs/api/kbn-kibana-manifest-parser -title: "@kbn/kibana-manifest-parser" -image: https://source.unsplash.com/400x175/?github -description: API docs for the @kbn/kibana-manifest-parser plugin -date: 2022-09-08 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-parser'] ---- -import kbnKibanaManifestParserObj from './kbn_kibana_manifest_parser.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 | 4 | 3 | - -## Server - -### Functions - - -### Consts, variables and types - - diff --git a/api_docs/kbn_kibana_manifest_schema.devdocs.json b/api_docs/kbn_kibana_manifest_schema.devdocs.json index 9a2294b16a387..21bb0a106155f 100644 --- a/api_docs/kbn_kibana_manifest_schema.devdocs.json +++ b/api_docs/kbn_kibana_manifest_schema.devdocs.json @@ -873,6 +873,19 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.allowTrailingCommas", + "type": "boolean", + "tags": [], + "label": "allowTrailingCommas", + "description": [ + "// @ts-expect-error VSCode specific JSONSchema extension" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/kibana-manifest-schema", "id": "def-server.MANIFEST_V2.properties", @@ -916,6 +929,17 @@ "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.id.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false } ] }, @@ -967,10 +991,10 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.typeDependencies", + "id": "def-server.MANIFEST_V2.properties.typeDeps", "type": "Object", "tags": [], - "label": "typeDependencies", + "label": "typeDeps", "description": [], "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", "deprecated": false, @@ -978,7 +1002,7 @@ "children": [ { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.typeDependencies.type", + "id": "def-server.MANIFEST_V2.properties.typeDeps.type", "type": "string", "tags": [], "label": "type", @@ -989,7 +1013,7 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.typeDependencies.description", + "id": "def-server.MANIFEST_V2.properties.typeDeps.description", "type": "string", "tags": [], "label": "description", @@ -1000,7 +1024,7 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.typeDependencies.items", + "id": "def-server.MANIFEST_V2.properties.typeDeps.items", "type": "Object", "tags": [], "label": "items", @@ -1011,7 +1035,7 @@ "children": [ { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.typeDependencies.items.type", + "id": "def-server.MANIFEST_V2.properties.typeDeps.items.type", "type": "string", "tags": [], "label": "type", @@ -1026,10 +1050,10 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.runtimeDependencies", + "id": "def-server.MANIFEST_V2.properties.runtimeDeps", "type": "Object", "tags": [], - "label": "runtimeDependencies", + "label": "runtimeDeps", "description": [], "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", "deprecated": false, @@ -1037,7 +1061,7 @@ "children": [ { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.type", + "id": "def-server.MANIFEST_V2.properties.runtimeDeps.type", "type": "string", "tags": [], "label": "type", @@ -1048,7 +1072,7 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.description", + "id": "def-server.MANIFEST_V2.properties.runtimeDeps.description", "type": "string", "tags": [], "label": "description", @@ -1059,7 +1083,7 @@ }, { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.items", + "id": "def-server.MANIFEST_V2.properties.runtimeDeps.items", "type": "Object", "tags": [], "label": "items", @@ -1070,7 +1094,7 @@ "children": [ { "parentPluginId": "@kbn/kibana-manifest-schema", - "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.items.type", + "id": "def-server.MANIFEST_V2.properties.runtimeDeps.items.type", "type": "string", "tags": [], "label": "type", @@ -1082,6 +1106,52 @@ ] } ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.devOnly", + "type": "Object", + "tags": [], + "label": "devOnly", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.devOnly.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.devOnly.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.devOnly.default", + "type": "boolean", + "tags": [], + "label": "default", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] }, diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index ec3e2af47fc8f..dca53916d65b6 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 91 | 0 | 91 | 0 | +| 97 | 0 | 96 | 0 | ## Server diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index e09b62c0bb1b9..6ba7fb075d16d 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index c0923437c7df5..2832f3e8dae4b 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 5e5e690c99883..9860f16923015 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 7977d52647157..aff7a4c3270b2 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index ff08f4cbbad11..a162aa944dc67 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 36546dca93696..875e36a1a9991 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 5db7b02795897..9a84263750a9f 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index ff8408aff7acc..c7162db39b87c 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index de2ba0f075c74..8b0c2dce899c4 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index 8fe04c2d25f15..eff3e884b3bd7 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index aa1371dd75ae4..3fdf23cd53c02 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 8fcbf064242d9..37bb135d5c280 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index d40aceba8e1d4..a5383cbc3b530 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index df6f561e8cda6..ba7590ffbe159 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 701d2b801f102..017f82899a837 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 25cd1e62b3ecb..49f9e90702310 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 69baed8fab2fd..ebe0a2779fedf 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 8694ba391c1df..15d8fcc4196ee 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 679a6d384fd0d..a3eb896fea4c6 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 3e7bae71056d5..db43ecbf5d190 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json index 027cbb793cc9f..f75157365c883 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json @@ -363,20 +363,13 @@ }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", - "id": "def-common.ApiCallByListIdProps.filterOptions", - "type": "Array", + "id": "def-common.ApiCallByListIdProps.pagination", + "type": "Object", "tags": [], - "label": "filterOptions", + "label": "pagination", "description": [], "signature": [ - { - "pluginId": "@kbn/securitysolution-io-ts-list-types", - "scope": "common", - "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", - "section": "def-common.FilterExceptionsOptions", - "text": "FilterExceptionsOptions" - }, - "[]" + "{ page?: number | undefined; perPage?: number | undefined; total?: number | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -384,13 +377,27 @@ }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", - "id": "def-common.ApiCallByListIdProps.pagination", - "type": "Object", + "id": "def-common.ApiCallByListIdProps.search", + "type": "string", "tags": [], - "label": "pagination", + "label": "search", "description": [], "signature": [ - "{ page?: number | undefined; perPage?: number | undefined; total?: number | undefined; }" + "string | undefined" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiCallByListIdProps.filter", + "type": "string", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + "string | undefined" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -523,27 +530,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "@kbn/securitysolution-io-ts-list-types", - "id": "def-common.ApiCallFindListsItemsMemoProps.filterOptions", - "type": "Array", - "tags": [], - "label": "filterOptions", - "description": [], - "signature": [ - { - "pluginId": "@kbn/securitysolution-io-ts-list-types", - "scope": "common", - "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", - "section": "def-common.FilterExceptionsOptions", - "text": "FilterExceptionsOptions" - }, - "[]" - ], - "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ApiCallFindListsItemsMemoProps.pagination", @@ -580,6 +566,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiCallFindListsItemsMemoProps.filter", + "type": "string", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ApiCallFindListsItemsMemoProps.onError", @@ -2873,7 +2873,7 @@ "label": "FindExceptionListItemSchema", "description": [], "signature": [ - "{ list_id: string; } & { filter?: string | null | undefined; namespace_type?: string | null | undefined; page?: string | undefined; per_page?: string | undefined; sort_field?: string | undefined; sort_order?: \"asc\" | \"desc\" | undefined; }" + "{ list_id: string; } & { filter?: string | null | undefined; namespace_type?: string | null | undefined; page?: string | undefined; per_page?: string | undefined; search?: string | undefined; sort_field?: string | undefined; sort_order?: \"asc\" | \"desc\" | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts", "deprecated": false, @@ -2888,7 +2888,7 @@ "label": "FindExceptionListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ list_id: string[]; filter: string[] | undefined; namespace_type: (\"single\" | \"agnostic\")[] | undefined; page: number | undefined; per_page: number | undefined; sort_field: string | undefined; sort_order: \"asc\" | \"desc\" | undefined; }, \"filter\" | \"namespace_type\"> & { filter: string[]; namespace_type: (\"single\" | \"agnostic\")[]; }" + "Omit<{ list_id: string[]; filter: string[] | undefined; namespace_type: (\"single\" | \"agnostic\")[] | undefined; page: number | undefined; per_page: number | undefined; search: string | undefined; sort_field: string | undefined; sort_order: \"asc\" | \"desc\" | undefined; }, \"filter\" | \"namespace_type\"> & { filter: string[]; namespace_type: (\"single\" | \"agnostic\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts", "deprecated": false, @@ -3285,6 +3285,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.InternalCreateExceptionListSchema", + "type": "Type", + "tags": [], + "label": "InternalCreateExceptionListSchema", + "description": [], + "signature": [ + "{ type: \"endpoint\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; } & { list_id?: \"endpoint_trusted_apps\" | \"endpoint_event_filters\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\" | undefined; } & { description: string; name: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; } & { list_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; version?: number | undefined; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.InternalCreateExceptionListSchemaDecoded", + "type": "Type", + "tags": [], + "label": "InternalCreateExceptionListSchemaDecoded", + "description": [], + "signature": [ + "Omit<{ description: string; name: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string | undefined; meta: object | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; version: number | undefined; }, \"tags\" | \"list_id\" | \"namespace_type\" | \"os_types\"> & { tags: string[]; list_id: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; version: number; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ItemId", @@ -4095,6 +4125,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.Search", + "type": "Type", + "tags": [], + "label": "Search", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.SearchAfter", @@ -4155,6 +4200,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.SearchOrUndefined", + "type": "Type", + "tags": [], + "label": "SearchOrUndefined", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.Serializer", @@ -6641,6 +6701,8 @@ "StringToPositiveNumberC", "; per_page: ", "StringToPositiveNumberC", + "; search: ", + "StringC", "; sort_field: ", "StringC", "; sort_order: ", @@ -7603,6 +7665,66 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.internalCreateExceptionListSchema", + "type": "Object", + "tags": [], + "label": "internalCreateExceptionListSchema", + "description": [], + "signature": [ + "IntersectionC", + "<[", + "ExactC", + "<", + "TypeC", + "<{ type: ", + "KeyofC", + "<{ endpoint: null; endpoint_events: null; endpoint_host_isolation_exceptions: null; endpoint_blocklists: null; }>; }>>, ", + "ExactC", + "<", + "PartialC", + "<{ list_id: ", + "KeyofC", + "<{ endpoint_trusted_apps: null; endpoint_event_filters: null; endpoint_host_isolation_exceptions: null; endpoint_blocklists: null; }>; }>>, ", + "IntersectionC", + "<[", + "ExactC", + "<", + "TypeC", + "<{ description: ", + "StringC", + "; name: ", + "StringC", + "; type: ", + "KeyofC", + "<{ detection: null; rule_default: null; endpoint: null; endpoint_trusted_apps: null; endpoint_events: null; endpoint_host_isolation_exceptions: null; endpoint_blocklists: null; }>; }>>, ", + "ExactC", + "<", + "PartialC", + "<{ list_id: ", + "Type", + "; meta: ", + "ObjectC", + "; namespace_type: ", + "Type", + "<\"single\" | \"agnostic\", \"single\" | \"agnostic\" | undefined, unknown>; os_types: ", + "UnionC", + "<[", + "Type", + "<(\"windows\" | \"linux\" | \"macos\")[], (\"windows\" | \"linux\" | \"macos\")[] | undefined, unknown>, ", + "UndefinedC", + "]>; tags: ", + "Type", + "; version: ", + "Type", + "; }>>]>]>" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.item_id", @@ -8756,6 +8878,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.search", + "type": "Object", + "tags": [], + "label": "search", + "description": [], + "signature": [ + "StringC" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.search_after", @@ -8936,6 +9073,26 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.searchOrUndefined", + "type": "Object", + "tags": [], + "label": "searchOrUndefined", + "description": [], + "signature": [ + "UnionC", + "<[", + "StringC", + ", ", + "UndefinedC", + "]>" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.serializer", diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index fb03b23a345c5..3fa0a82da7e87 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 462 | 1 | 449 | 0 | +| 470 | 1 | 457 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index e15f7b18725a2..90ed9b37626b4 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 4846fce79df38..43dbe59c8b441 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.devdocs.json b/api_docs/kbn_securitysolution_list_api.devdocs.json index 5f06475094776..2f92e25cf8bef 100644 --- a/api_docs/kbn_securitysolution_list_api.devdocs.json +++ b/api_docs/kbn_securitysolution_list_api.devdocs.json @@ -452,7 +452,7 @@ "label": "fetchExceptionListsItemsByListIdsWithValidation", "description": [], "signature": [ - "({ filterOptions, http, listIds, namespaceTypes, pagination, signal, }: ", + "({ filter, http, listIds, namespaceTypes, pagination, search, 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\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; 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; }>" ], @@ -465,7 +465,7 @@ "id": "def-common.fetchExceptionListsItemsByListIdsWithValidation.$1", "type": "Object", "tags": [], - "label": "{\n filterOptions,\n http,\n listIds,\n namespaceTypes,\n pagination,\n signal,\n}", + "label": "{\n filter,\n http,\n listIds,\n namespaceTypes,\n pagination,\n search,\n signal,\n}", "description": [], "signature": [ "ApiCallByListIdProps" diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index fcec313dc1700..79e9918ff415a 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.devdocs.json b/api_docs/kbn_securitysolution_list_constants.devdocs.json index a4ec4fcc115c1..3b2c4d2823509 100644 --- a/api_docs/kbn_securitysolution_list_constants.devdocs.json +++ b/api_docs/kbn_securitysolution_list_constants.devdocs.json @@ -363,6 +363,32 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-list-constants", + "id": "def-common.INTERNAL_EXCEPTION_LIST_URL", + "type": "string", + "tags": [], + "label": "INTERNAL_EXCEPTION_LIST_URL", + "description": [ + "\nInternal exception list routes" + ], + "path": "packages/kbn-securitysolution-list-constants/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-list-constants", + "id": "def-common.INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL", + "type": "string", + "tags": [], + "label": "INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL", + "description": [], + "path": "packages/kbn-securitysolution-list-constants/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-list-constants", "id": "def-common.LIST_INDEX", diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 352154a93c556..4260459c9d064 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 26 | 0 | 12 | 0 | +| 28 | 0 | 13 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_hooks.devdocs.json b/api_docs/kbn_securitysolution_list_hooks.devdocs.json index ed317805a036b..05d6b763b7c16 100644 --- a/api_docs/kbn_securitysolution_list_hooks.devdocs.json +++ b/api_docs/kbn_securitysolution_list_hooks.devdocs.json @@ -341,50 +341,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-list-hooks", - "id": "def-common.useExceptionListItems", - "type": "Function", - "tags": [], - "label": "useExceptionListItems", - "description": [ - "\nHook for using to get an ExceptionList and its ExceptionListItems\n" - ], - "signature": [ - "({ http, lists, pagination, filterOptions, showDetectionsListsOnly, showEndpointListsOnly, matchFilters, onError, onSuccess, }: ", - "UseExceptionListProps", - ") => ", - { - "pluginId": "@kbn/securitysolution-list-hooks", - "scope": "common", - "docId": "kibKbnSecuritysolutionListHooksPluginApi", - "section": "def-common.ReturnExceptionListAndItems", - "text": "ReturnExceptionListAndItems" - } - ], - "path": "packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/securitysolution-list-hooks", - "id": "def-common.useExceptionListItems.$1", - "type": "Object", - "tags": [], - "label": "{\n http,\n lists,\n pagination = {\n page: 1,\n perPage: 20,\n total: 0,\n },\n filterOptions,\n showDetectionsListsOnly,\n showEndpointListsOnly,\n matchFilters,\n onError,\n onSuccess,\n}", - "description": [], - "signature": [ - "UseExceptionListProps" - ], - "path": "packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-list-hooks", "id": "def-common.useExceptionLists", @@ -998,23 +954,6 @@ "children": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-list-hooks", - "id": "def-common.ReturnExceptionListAndItems", - "type": "Type", - "tags": [], - "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\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; 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]" - ], - "path": "packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-list-hooks", "id": "def-common.ReturnExceptionLists", diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 8792a1d797618..d7da0dc87559b 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 56 | 0 | 44 | 0 | +| 53 | 0 | 42 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 9a8afda4b7b7d..93b46261b29ac 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index bb843ea8ac0bc..73955118e002c 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 1decc3b5849ea..e0d4fb5f40c7d 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 8b6696e13af38..ce83347c99d76 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 7f521d04d76d9..aaee03632d3bd 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 8ee1166df80be..72c05e4c8e597 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 04dbdc8391d26..5396d1d529cc3 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index b5c2b30967bb4..ebeb4c6b525a9 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index ac94fb4896451..af5b4f23845c2 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 110a972ba8eb2..6fbaa004f2aa1 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 308f00a75d82e..216b4ec105498 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 7456f34241042..86b17203dd3aa 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index b7ae5b0fce0f9..ba2a281401304 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 326b017b7d118..fc4bed4b1a001 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 9b1be63162af5..d0c9c5ed9392b 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index fa6513e6cb2ca..f6aa153d98fd1 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index b285bd11d9a23..967834391e6dc 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 6e3c33994da95..527b5039281e5 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 9e88289417fd5..b132757f0b152 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 5f3e0e337b804..92be11cf2c6b6 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index eb80ec1a24586..579464a3146de 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 642f5553769a6..c89f5a58f576e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index b9374f8d8a976..f7430ec4cffc9 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 59f4055671f04..770abdeea3c3d 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 37e90a8eb138c..f20bc874aa489 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.devdocs.json b/api_docs/kbn_shared_ux_router.devdocs.json new file mode 100644 index 0000000000000..b3381733e9f8f --- /dev/null +++ b/api_docs/kbn_shared_ux_router.devdocs.json @@ -0,0 +1,65 @@ +{ + "id": "@kbn/shared-ux-router", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/shared-ux-router", + "id": "def-common.Route", + "type": "Function", + "tags": [], + "label": "Route", + "description": [ + "\nThis is a wrapper around the react-router-dom Route component that inserts\nMatchPropagator in every application route. It helps track all route changes\nand send them to the execution context, later used to enrich APM\n'route-change' transactions." + ], + "signature": [ + "({ children, component: Component, render, ...rest }: ", + "RouteProps", + ") => JSX.Element" + ], + "path": "packages/shared-ux/router/impl/router.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-router", + "id": "def-common.Route.$1", + "type": "Object", + "tags": [], + "label": "{ children, component: Component, render, ...rest }", + "description": [], + "signature": [ + "RouteProps" + ], + "path": "packages/shared-ux/router/impl/router.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx new file mode 100644 index 0000000000000..c88c99fdfa8e1 --- /dev/null +++ b/api_docs/kbn_shared_ux_router.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSharedUxRouterPluginApi +slug: /kibana-dev-docs/api/kbn-shared-ux-router +title: "@kbn/shared-ux-router" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/shared-ux-router plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] +--- +import kbnSharedUxRouterObj from './kbn_shared_ux_router.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 | 1 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_shared_ux_router_mocks.devdocs.json b/api_docs/kbn_shared_ux_router_mocks.devdocs.json new file mode 100644 index 0000000000000..db66a62441697 --- /dev/null +++ b/api_docs/kbn_shared_ux_router_mocks.devdocs.json @@ -0,0 +1,45 @@ +{ + "id": "@kbn/shared-ux-router-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/shared-ux-router-mocks", + "id": "def-common.foo", + "type": "Function", + "tags": [], + "label": "foo", + "description": [], + "signature": [ + "() => string" + ], + "path": "packages/shared-ux/router/mocks/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx new file mode 100644 index 0000000000000..ad038875cef83 --- /dev/null +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSharedUxRouterMocksPluginApi +slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks +title: "@kbn/shared-ux-router-mocks" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/shared-ux-router-mocks plugin +date: 2022-09-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] +--- +import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 1 | 0 | 1 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 61e123a44519c..008ba2f53e93c 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index bf41c879e0c88..7883d6366d4f3 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index a7d109aeaa027..c7fe9875c57c6 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index bcafbf060e852..bc834ee2ff41a 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 711d3242b36c2..1dd32b08c95c5 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index f235b690509b6..a76ec2f91fad0 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 5342e38297ac1..ffd690fb8d542 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index cc0aa729278dc..6d4cee2cf48e5 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 3ac584012e863..bb0e1cfdac9b0 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.devdocs.json b/api_docs/kbn_test.devdocs.json index 3a4cf723d0775..f36daceb7cab2 100644 --- a/api_docs/kbn_test.devdocs.json +++ b/api_docs/kbn_test.devdocs.json @@ -2669,6 +2669,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/test", + "id": "def-server.CreateTestEsClusterOptions.writeLogsToPath", + "type": "string", + "tags": [], + "label": "writeLogsToPath", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-test/src/es/test_es_cluster.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/test", "id": "def-server.CreateTestEsClusterOptions.nodes", diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 0c35076ee177d..bbfa64010e3ba 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; @@ -21,7 +21,7 @@ Contact Operations for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 253 | 5 | 212 | 11 | +| 254 | 5 | 213 | 11 | ## Server diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index f69d950a6020d..762df9d42c29c 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 34eb25abce626..957ed8febfcae 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 36d4e190e9df9..98103fbb8df50 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 8322a666bd9e7..ff85afae240ef 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 7a592007512c2..9e427901caff1 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index cf0cc5cb05b93..6900408be11c0 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 10bf9f95e7101..cd577edea97f4 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 683fa55fa6a85..cbe25e33be509 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index d5a46f16e6952..e2a850694866b 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index be96bc7150be9..e09f9577b2b03 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index cf64f2eb0c977..a789dd3553362 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 9a6d98afc5194..8af7864ea4939 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index aa062aa0b26d6..cb45d6c5a4431 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 737bd6f780b0e..0de39481f8629 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 0adc33bfc6e81..1652626d9f56f 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 10aed71c09d2e..76f715bdb21c9 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -2715,7 +2715,7 @@ "section": "def-public.Visualization", "text": "Visualization" }, - " | (() => Promise<", + " | (() => Promise<", { "pluginId": "lens", "scope": "public", @@ -2723,7 +2723,7 @@ "section": "def-public.Visualization", "text": "Visualization" }, - ">)) => void" + ">)) => void" ], "path": "x-pack/plugins/lens/public/plugin.ts", "deprecated": false, @@ -2744,7 +2744,7 @@ "section": "def-public.Visualization", "text": "Visualization" }, - " | (() => Promise<", + " | (() => Promise<", { "pluginId": "lens", "scope": "public", @@ -2752,7 +2752,7 @@ "section": "def-public.Visualization", "text": "Visualization" }, - ">)" + ">)" ], "path": "x-pack/plugins/lens/public/plugin.ts", "deprecated": false, @@ -3125,6 +3125,20 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "lens", + "id": "def-public.OperationMetadata.interval", + "type": "string", + "tags": [], + "label": "interval", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "lens", "id": "def-public.OperationMetadata.dataType", @@ -4122,7 +4136,7 @@ "section": "def-public.Visualization", "text": "Visualization" }, - "" + "" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -4148,7 +4162,7 @@ "tags": [], "label": "initialize", "description": [ - "\nInitialize is allowed to modify the state stored in memory. The initialize function\nis called with a previous state in two cases:\n- Loadingn from a saved visualization\n- When using suggestions, the suggested state is passed in" + "\nInitialize is allowed to modify the state stored in memory. The initialize function\nis called with a previous state in two cases:\n- Loading from a saved visualization\n- When using suggestions, the suggested state is passed in" ], "signature": [ "(addNewLayer: () => string, state?: T | undefined, mainPalette?: ", @@ -4208,6 +4222,40 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getUsedDataViews", + "type": "Function", + "tags": [], + "label": "getUsedDataViews", + "description": [ + "\nRetrieve the used DataViews in the visualization" + ], + "signature": [ + "((state?: T | undefined) => string[]) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getUsedDataViews.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.getMainPalette", @@ -4394,6 +4442,94 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getPersistableState", + "type": "Function", + "tags": [], + "label": "getPersistableState", + "description": [ + "Visualizations can have references as well" + ], + "signature": [ + "((state: T) => { state: P; savedObjectReferences: ", + "SavedObjectReference", + "[]; }) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getPersistableState.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.fromPersistableState", + "type": "Function", + "tags": [], + "label": "fromPersistableState", + "description": [ + "Hydrate from persistable state and references to final state" + ], + "signature": [ + "((state: P, references?: ", + "SavedObjectReference", + "[] | undefined) => T) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.fromPersistableState.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "P" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.fromPersistableState.$2", + "type": "Array", + "tags": [], + "label": "references", + "description": [], + "signature": [ + "SavedObjectReference", + "[] | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.getLayerIds", @@ -4438,7 +4574,7 @@ "Reset button on each layer triggers this" ], "signature": [ - "(state: T, layerId: string) => T" + "(state: T, layerId: string, indexPatternId: string) => T" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -4473,6 +4609,21 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.clearLayer.$3", + "type": "string", + "tags": [], + "label": "indexPatternId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -4544,7 +4695,7 @@ "section": "def-common.LayerType", "text": "LayerType" }, - ", indexPatternId?: string | undefined) => T) | undefined" + ", indexPatternId: string) => T) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -4609,12 +4760,12 @@ "label": "indexPatternId", "description": [], "signature": [ - "string | undefined" + "string" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, "trackAdoption": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] @@ -5750,15 +5901,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?: Partial> | undefined) => { shortMessage: string; longMessage: React.ReactNode; }[] | undefined" + "(state: T, frame?: Pick<", + "FramePublicAPI", + ", \"dataViews\" | \"datasourceLayers\"> | undefined) => { shortMessage: string; longMessage: React.ReactNode; }[] | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -5784,18 +5929,111 @@ "id": "def-public.Visualization.getErrorMessages.$2", "type": "Object", "tags": [], - "label": "datasourceLayers", + "label": "frame", "description": [], "signature": [ - "Partial> | undefined" + "Pick<", + "FramePublicAPI", + ", \"dataViews\" | \"datasourceLayers\"> | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn", + "type": "Function", + "tags": [], + "label": "validateColumn", + "description": [], + "signature": [ + "((state: T, frame: Pick<", + "FramePublicAPI", + ", \"dataViews\">, layerId: string, columnId: string, group?: ", + "VisualizationDimensionGroupConfig", + " | undefined) => { invalid: boolean; invalidMessage?: string | undefined; }) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn.$2", + "type": "Object", + "tags": [], + "label": "frame", + "description": [], + "signature": [ + "Pick<", + "FramePublicAPI", + ", \"dataViews\">" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn.$3", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn.$4", + "type": "string", + "tags": [], + "label": "columnId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.validateColumn.$5", + "type": "CompoundType", + "tags": [], + "label": "group", + "description": [], + "signature": [ + "VisualizationDimensionGroupConfig", + " | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -6252,6 +6490,31 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "lens", + "id": "def-public.XYAnnotationLayerConfig.hide", + "type": "CompoundType", + "tags": [], + "label": "hide", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.XYAnnotationLayerConfig.indexPatternId", + "type": "string", + "tags": [], + "label": "indexPatternId", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "lens", "id": "def-public.XYAnnotationLayerConfig.simpleView", @@ -6382,21 +6645,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "lens", - "id": "def-public.XYArgs.annotationLayers", - "type": "Array", - "tags": [], - "label": "annotationLayers", - "description": [], - "signature": [ - "AnnotationLayerConfigResult", - "[]" - ], - "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "lens", "id": "def-public.XYArgs.fittingFunction", @@ -8675,30 +8923,7 @@ "path": "x-pack/plugins/lens/server/plugin.tsx", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-server.LensServerPlugin.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "initializerContext", - "description": [], - "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.PluginInitializerContext", - "text": "PluginInitializerContext" - }, - "" - ], - "path": "x-pack/plugins/lens/server/plugin.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], + "children": [], "returnComment": [] }, { @@ -9436,6 +9661,62 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "lens", + "id": "def-server.XYVisState850", + "type": "Interface", + "tags": [], + "label": "XYVisState850", + "description": [], + "path": "x-pack/plugins/lens/server/migrations/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-server.XYVisState850.layers", + "type": "Array", + "tags": [], + "label": "layers", + "description": [], + "signature": [ + "({ layerId: string; layerType: \"data\" | \"referenceLine\"; } | { layerId: string; layerType: \"annotations\"; annotations: { id: string; type: \"query\" | \"manual\"; }[]; })[]" + ], + "path": "x-pack/plugins/lens/server/migrations/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "lens", + "id": "def-server.XYVisStatePre850", + "type": "Interface", + "tags": [], + "label": "XYVisStatePre850", + "description": [], + "path": "x-pack/plugins/lens/server/migrations/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-server.XYVisStatePre850.layers", + "type": "Array", + "tags": [], + "label": "layers", + "description": [], + "signature": [ + "({ layerId: string; layerType: \"data\" | \"referenceLine\"; } | { layerId: string; layerType: \"annotations\"; annotations: { id: string; }[]; })[]" + ], + "path": "x-pack/plugins/lens/server/migrations/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "lens", "id": "def-server.XYVisualizationState830", @@ -9937,6 +10218,27 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "lens", + "id": "def-server.VisState850", + "type": "Type", + "tags": [], + "label": "VisState850", + "description": [], + "signature": [ + { + "pluginId": "lens", + "scope": "server", + "docId": "kibLensPluginApi", + "section": "def-server.XYVisState850", + "text": "XYVisState850" + } + ], + "path": "x-pack/plugins/lens/server/migrations/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "lens", "id": "def-server.VisStatePost715", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index e3bf3ec6bd2ea..6e43b6db2c76b 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 619 | 0 | 533 | 41 | +| 639 | 0 | 550 | 41 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 9cbe965feabc5..48955617613a9 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 0f41b6ae3352d..8c8b8e8c9ef66 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 49646741c77fc..1f36b3105cdc8 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.devdocs.json b/api_docs/lists.devdocs.json index 2eb1993fbe171..fe03f21a11155 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -1129,7 +1129,7 @@ "\nFinds an exception list item given a set of criteria." ], "signature": [ - "({ listId, filter, perPage, pit, page, searchAfter, sortField, sortOrder, namespaceType, }: ", + "({ listId, filter, perPage, pit, page, search, 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\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; 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>" ], @@ -1142,7 +1142,7 @@ "id": "def-server.ExceptionListClient.findExceptionListItem.$1", "type": "Object", "tags": [], - "label": "{\n listId,\n filter,\n perPage,\n pit,\n page,\n searchAfter,\n sortField,\n sortOrder,\n namespaceType,\n }", + "label": "{\n listId,\n filter,\n perPage,\n pit,\n page,\n search,\n searchAfter,\n sortField,\n sortOrder,\n namespaceType,\n }", "description": [], "signature": [ "FindExceptionListItemOptions" @@ -1167,7 +1167,7 @@ "\nFinds exception lists items given a set of criteria." ], "signature": [ - "({ listId, filter, perPage, pit, page, searchAfter, sortField, sortOrder, namespaceType, }: ", + "({ listId, filter, perPage, pit, page, search, 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\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; 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>" ], @@ -1180,7 +1180,7 @@ "id": "def-server.ExceptionListClient.findExceptionListsItem.$1", "type": "Object", "tags": [], - "label": "{\n listId,\n filter,\n perPage,\n pit,\n page,\n searchAfter,\n sortField,\n sortOrder,\n namespaceType,\n }", + "label": "{\n listId,\n filter,\n perPage,\n pit,\n page,\n search,\n searchAfter,\n sortField,\n sortOrder,\n namespaceType,\n }", "description": [], "signature": [ "FindExceptionListsItemOptions" @@ -1281,7 +1281,7 @@ "\nThis is the same as \"findExceptionList\" except it applies specifically to the endpoint list and will\nauto-call the \"createEndpointList\" for you so that you have the best chance of the endpoint\nbeing there if it did not exist before. If the list did not exist before, then creating it here should give you\na good guarantee that you will get an empty record set rather than null. I keep the null as the return value in\nthe off chance that you still might somehow not get into a race condition where the endpoint list does\nnot exist because someone deleted it in-between the initial create and then the find." ], "signature": [ - "({ filter, perPage, page, pit, searchAfter, sortField, sortOrder, }: ", + "({ filter, perPage, page, pit, search, 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\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; 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>" ], @@ -1294,7 +1294,7 @@ "id": "def-server.ExceptionListClient.findEndpointListItem.$1", "type": "Object", "tags": [], - "label": "{\n filter,\n perPage,\n page,\n pit,\n searchAfter,\n sortField,\n sortOrder,\n }", + "label": "{\n filter,\n perPage,\n page,\n pit,\n search,\n searchAfter,\n sortField,\n sortOrder,\n }", "description": [], "signature": [ "FindEndpointListItemOptions" diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 4898b58a53d7c..a726ebc34c5fb 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index c80c65e2fcc23..74513f78d1f36 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 3288a39e333ce..a5fb71bb58e91 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index d77d00574c79f..7cac92107a18c 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index a97b25d34c3b1..616e1e5a07fc7 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 61c9858dabfd4..afe72ce3daf47 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 1c108a0ab43d8..c2674d018305b 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index d54a3cbd58486..ac8837b6ca894 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 5bcbade38769b..ba58d7518c6e4 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 680c70e75c060..b37b1655b9065 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -7760,7 +7760,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { success: boolean; }, ", + ", { id: string; name: string; description: string; time_window: { duration: string; is_rolling: true; }; indicator: { type: \"slo.apm.transaction_duration\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: \"slo.apm.transaction_error_rate\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; budgeting_method: \"occurrences\"; objective: { target: number; }; settings: { destination_index?: string | undefined; }; }, ", { "pluginId": "observability", "scope": "server", @@ -7948,7 +7948,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { success: boolean; }, ", + ", { id: string; name: string; description: string; time_window: { duration: string; is_rolling: true; }; indicator: { type: \"slo.apm.transaction_duration\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: \"slo.apm.transaction_error_rate\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; budgeting_method: \"occurrences\"; objective: { target: number; }; settings: { destination_index?: string | undefined; }; }, ", { "pluginId": "observability", "scope": "server", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 346c966d5b617..c580985bc8887 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.devdocs.json b/api_docs/osquery.devdocs.json index f217e15c771ef..8d45fc77730ef 100644 --- a/api_docs/osquery.devdocs.json +++ b/api_docs/osquery.devdocs.json @@ -40,7 +40,27 @@ "label": "OsqueryAction", "description": [], "signature": [ - "((props: any) => JSX.Element) | undefined" + "((props: ", + "OsqueryActionProps", + ") => JSX.Element) | undefined" + ], + "path": "x-pack/plugins/osquery/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "osquery", + "id": "def-public.OsqueryPluginStart.LiveQueryField", + "type": "Function", + "tags": [], + "label": "LiveQueryField", + "description": [], + "signature": [ + "(({ formMethods, ...props }: ", + "LiveQueryQueryFieldProps", + " & { formMethods: ", + "UseFormReturn", + "<{ label: string; query: string; ecs_mapping: Record; }, any>; }) => JSX.Element) | undefined" ], "path": "x-pack/plugins/osquery/public/types.ts", "deprecated": false, diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index cca97c6fe10be..1e938d796a4a7 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Security asset management](https://github.com/orgs/elastic/teams/securi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 13 | 0 | +| 14 | 0 | 14 | 2 | ## Client diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 6dfa0eb3a39c1..40d73b16b018a 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 444 | 369 | 36 | +| 450 | 375 | 36 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 30582 | 180 | 20435 | 973 | +| 30722 | 180 | 20534 | 971 | ## Plugin Directory @@ -29,24 +29,24 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] |--------------|----------------|-----------|--------------|----------|---------------|--------| | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 272 | 0 | 267 | 19 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 0 | 19 | 1 | -| | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 9 | 0 | 0 | 1 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 368 | 0 | 359 | 21 | +| | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 7 | 0 | 0 | 1 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 369 | 0 | 360 | 22 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 39 | 0 | 39 | 54 | | | [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. | 80 | 1 | 71 | 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 | 84 | 0 | 68 | 29 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 261 | 2 | 246 | 9 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 264 | 2 | 249 | 9 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 24 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 18 | 0 | 2 | 3 | | | [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 | 212 | 0 | 204 | 7 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2657 | 1 | 61 | 2 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2657 | 1 | 58 | 2 | | 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 | 102 | 0 | 83 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 147 | 0 | 142 | 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. | 3131 | 34 | 2441 | 23 | +| | [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. | 3144 | 34 | 2444 | 23 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 49 | 0 | 29 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 0 | @@ -60,8 +60,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 42 | 0 | | | [Enterprise Search](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 114 | 3 | 110 | 3 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | The Event Annotation service contains expressions for event annotations | 163 | 0 | 163 | 5 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 100 | 0 | 100 | 9 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | The Event Annotation service contains expressions for event annotations | 170 | 0 | 170 | 3 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 106 | 0 | 106 | 10 | | | [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. | 57 | 0 | 57 | 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. | 105 | 0 | 101 | 3 | @@ -74,13 +74,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'revealImage' function and renderer to expressions | 14 | 0 | 14 | 3 | | | [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. | 152 | 0 | 142 | 11 | +| | [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. | 153 | 0 | 142 | 9 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds expression runtime to Kibana | 2183 | 17 | 1729 | 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 | 288 | 5 | 249 | 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 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 6 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 969 | 3 | 873 | 10 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 970 | 3 | 873 | 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 | @@ -99,7 +99,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | 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 | 418 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [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. | 619 | 0 | 533 | 41 | +| | [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. | 639 | 0 | 550 | 41 | | | [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 | @@ -114,7 +114,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [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) | - | 397 | 2 | 394 | 30 | -| | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 13 | 0 | 13 | 0 | +| | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 14 | 0 | 14 | 2 | | 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). | 243 | 2 | 187 | 12 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 4 | 0 | 4 | 0 | @@ -126,7 +126,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 16 | 0 | 16 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 130 | 0 | 117 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 78 | 0 | 72 | 3 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 91 | 0 | 46 | 1 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 94 | 0 | 47 | 1 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the definition and helper methods around saved searches, used by discover and visualizations. | 43 | 0 | 43 | 1 | | | [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 | 8 | 4 | @@ -151,8 +151,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 428 | 0 | 407 | 46 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 132 | 0 | 91 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 205 | 0 | 142 | 9 | -| | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 51 | 0 | 49 | 2 | -| | [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. | 109 | 2 | 84 | 16 | +| | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 54 | 0 | 52 | 2 | +| | [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. | 111 | 2 | 85 | 16 | | upgradeAssistant | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 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 | @@ -171,7 +171,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [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. | 53 | 0 | 50 | 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. | 417 | 12 | 389 | 15 | +| | [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. | 419 | 12 | 390 | 15 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -212,7 +212,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 12 | 0 | 12 | 0 | | | Kibana Core | - | 8 | 0 | 1 | 0 | | | Kibana Core | - | 3 | 0 | 3 | 0 | -| | Kibana Core | - | 20 | 0 | 3 | 0 | +| | Kibana Core | - | 12 | 0 | 3 | 0 | | | Kibana Core | - | 7 | 0 | 7 | 0 | | | Kibana Core | - | 3 | 0 | 3 | 0 | | | Kibana Core | - | 3 | 0 | 3 | 0 | @@ -307,6 +307,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 66 | 0 | 66 | 4 | | | Kibana Core | - | 14 | 0 | 13 | 0 | | | Kibana Core | - | 99 | 1 | 86 | 0 | +| | Kibana Core | - | 12 | 0 | 2 | 0 | +| | Kibana Core | - | 19 | 0 | 18 | 0 | +| | Kibana Core | - | 20 | 0 | 1 | 0 | +| | Kibana Core | - | 22 | 0 | 22 | 1 | +| | Kibana Core | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 11 | 0 | 9 | 0 | | | Kibana Core | - | 5 | 0 | 5 | 0 | | | Kibana Core | - | 6 | 0 | 4 | 0 | @@ -346,8 +351,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | App Services | - | 35 | 4 | 35 | 0 | | | [Owner missing] | - | 20 | 0 | 20 | 2 | | | [Owner missing] | - | 13 | 0 | 13 | 0 | -| | [Owner missing] | - | 7 | 0 | 4 | 3 | -| | [Owner missing] | - | 91 | 0 | 91 | 0 | +| | [Owner missing] | - | 97 | 0 | 96 | 0 | | | Kibana Core | - | 30 | 0 | 5 | 37 | | | Kibana Core | - | 8 | 0 | 8 | 0 | | | [Owner missing] | - | 6 | 0 | 1 | 1 | @@ -368,12 +372,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | | | [Owner missing] | Security Solution utilities for React hooks | 15 | 0 | 7 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 148 | 0 | 129 | 0 | -| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 462 | 1 | 449 | 0 | +| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 470 | 1 | 457 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 61 | 0 | 32 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 28 | 0 | 21 | 0 | | | [Owner missing] | security solution list REST API | 61 | 0 | 60 | 0 | -| | [Owner missing] | security solution list constants to use across plugins such lists, security_solution, cases, etc... | 26 | 0 | 12 | 0 | -| | [Owner missing] | Security solution list ReactJS hooks | 56 | 0 | 44 | 0 | +| | [Owner missing] | security solution list constants to use across plugins such lists, security_solution, cases, etc... | 28 | 0 | 13 | 0 | +| | [Owner missing] | Security solution list ReactJS hooks | 53 | 0 | 42 | 0 | | | [Owner missing] | security solution list utilities | 235 | 0 | 187 | 0 | | | [Owner missing] | security solution rule utilities to use across plugins | 26 | 0 | 23 | 0 | | | [Owner missing] | security solution t-grid packages will allow sharing components between timelines and security_solution plugin until we transfer all functionality to timelines plugin | 120 | 0 | 116 | 0 | @@ -399,6 +403,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 5 | 0 | 3 | 0 | | | [Owner missing] | - | 24 | 0 | 4 | 0 | | | [Owner missing] | - | 17 | 0 | 16 | 0 | +| | [Owner missing] | - | 2 | 0 | 1 | 0 | +| | [Owner missing] | - | 1 | 0 | 1 | 0 | | | [Owner missing] | - | 2 | 0 | 0 | 0 | | | [Owner missing] | - | 14 | 0 | 4 | 1 | | | [Owner missing] | - | 9 | 0 | 3 | 0 | @@ -408,7 +414,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 4 | 0 | 2 | 0 | | | Operations | - | 38 | 2 | 21 | 0 | | | Kibana Core | - | 2 | 0 | 2 | 0 | -| | Operations | - | 253 | 5 | 212 | 11 | +| | Operations | - | 254 | 5 | 213 | 11 | | | [Owner missing] | - | 135 | 8 | 103 | 2 | | | [Owner missing] | - | 72 | 0 | 55 | 0 | | | [Owner missing] | - | 8 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 3ea342705aa79..ecf895a3e0f7a 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index a3f07005a4347..9d61b29944ba9 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index d29a5518fd4dd..286139d6fe63f 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index b7b0340b07665..3a26281e749b6 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 086a42a7c8e6c..c9236042b14f0 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 4b03559bc8d9c..d04909af54896 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 581e5be0e084f..03f4861574ec8 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index d76a6d9879ca8..312293e55cc62 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 002ea2800bd02..e629a877e6a51 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index d94db5cfbcfc3..456808d926a82 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.devdocs.json b/api_docs/saved_objects_tagging_oss.devdocs.json index 82be777e2094a..31b3bccca32ed 100644 --- a/api_docs/saved_objects_tagging_oss.devdocs.json +++ b/api_docs/saved_objects_tagging_oss.devdocs.json @@ -52,6 +52,38 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "savedObjectsTaggingOss", + "id": "def-public.GetTableColumnDefinitionOptions", + "type": "Interface", + "tags": [], + "label": "GetTableColumnDefinitionOptions", + "description": [ + "\nOptions for the {@link SavedObjectsTaggingApiUi.getTableColumnDefinition | getTableColumnDefinition api}\n" + ], + "path": "src/plugins/saved_objects_tagging_oss/public/api.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "savedObjectsTaggingOss", + "id": "def-public.GetTableColumnDefinitionOptions.serverPaging", + "type": "CompoundType", + "tags": [], + "label": "serverPaging", + "description": [ + "\nBy default, the `tags` column definition will be automatically sortable\nby tag name.\n\nHowever, when paging is performed on the server, we need to remove the sorting\ncapability from the column to avoid unexpected behavior by triggering fetch request\nwhen sorting by column.\n\nShould be set to `true` when generating the definition for a table that performs\nserver-side paging.\n\nDefaults to false." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/saved_objects_tagging_oss/public/api.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "savedObjectsTaggingOss", "id": "def-public.ITagsCache", @@ -535,7 +567,15 @@ "\nReturn the column definition to be used to display the tags in a EUI table.\nThe table's items must be of the `SavedObject` type (or at least have their references available\nvia the `references` field)\n" ], "signature": [ - "() => ", + "(options?: ", + { + "pluginId": "savedObjectsTaggingOss", + "scope": "public", + "docId": "kibSavedObjectsTaggingOssPluginApi", + "section": "def-public.GetTableColumnDefinitionOptions", + "text": "GetTableColumnDefinitionOptions" + }, + " | undefined) => ", "EuiTableFieldDataColumnType", "<", "SavedObject", @@ -544,7 +584,30 @@ "path": "src/plugins/saved_objects_tagging_oss/public/api.ts", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "savedObjectsTaggingOss", + "id": "def-public.SavedObjectsTaggingApiUi.getTableColumnDefinition.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "savedObjectsTaggingOss", + "scope": "public", + "docId": "kibSavedObjectsTaggingOssPluginApi", + "section": "def-public.GetTableColumnDefinitionOptions", + "text": "GetTableColumnDefinitionOptions" + }, + " | undefined" + ], + "path": "src/plugins/saved_objects_tagging_oss/public/api.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], "returnComment": [] }, { diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index a2df3e4349d08..8dc7f9e54649e 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 91 | 0 | 46 | 1 | +| 94 | 0 | 47 | 1 | ## Client diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index b5e7da8db8850..15f259f2794d4 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 38322a98b6898..5c308f8c268e9 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 390350e543409..47f80ea4bfb32 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index a9943df56bf6d..e1d55dd7b8f5e 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 751f29fcd86f2..29a07610f1e5c 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 102df49e16bd0..9c8e4190bf491 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 2d2bb4aeb954b..6bd52414173b9 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 801a73732f10f..1e59aff6842ba 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 04a2b91499bc9..02b72e1835c09 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index a7267f334ac24..85ade27d7ba7e 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index afeaaa23f957b..696dc5d871335 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 9d26a0797fc9a..3a62678f11769 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index fa4a84d009b7e..30f763ba4488f 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index c17d37ef70fe9..f56b57fe0398c 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index c3278ea2f27b3..80abde5c72bc8 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index c2cf26e1f13b8..8e620fcd48321 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index 26500b5d7f838..72a30421386df 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -6453,7 +6453,7 @@ }, " extends Omit<", "TimelineRequestBasicOptions", - ", \"timerange\" | \"filterQuery\" | \"runtimeMappings\">" + ", \"timerange\" | \"runtimeMappings\" | \"filterQuery\">" ], "path": "x-pack/plugins/timelines/common/search_strategy/timeline/events/last_event_time/index.ts", "deprecated": false, diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 4c98cbb2f255b..020d59da436aa 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index c76bdf57b2d84..27facd7cbc791 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index d1650d2cc1758..6b6af45f6d599 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 2ca1ebc663985..68f0239219c7a 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index f498da6aab552..f9e3c5956fea5 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.devdocs.json b/api_docs/unified_field_list.devdocs.json index 43c861319fd04..e7555c2ac5a6c 100644 --- a/api_docs/unified_field_list.devdocs.json +++ b/api_docs/unified_field_list.devdocs.json @@ -57,6 +57,39 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "unifiedFieldList", + "id": "def-public.loadFieldExisting", + "type": "Function", + "tags": [], + "label": "loadFieldExisting", + "description": [], + "signature": [ + "({\n data,\n dslQuery,\n fromDate,\n toDate,\n timeFieldName,\n dataViewsService,\n uiSettingsClient,\n dataView,\n}: FetchFieldExistenceParams) => Promise<{ indexPatternTitle: string; existingFieldNames: string[]; }>" + ], + "path": "src/plugins/unified_field_list/public/services/field_existing/load_field_existing.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "unifiedFieldList", + "id": "def-public.loadFieldExisting.$1", + "type": "Object", + "tags": [], + "label": "{\n data,\n dslQuery,\n fromDate,\n toDate,\n timeFieldName,\n dataViewsService,\n uiSettingsClient,\n dataView,\n}", + "description": [], + "signature": [ + "FetchFieldExistenceParams" + ], + "path": "src/plugins/unified_field_list/public/services/field_existing/load_field_existing.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "unifiedFieldList", "id": "def-public.loadFieldStats", @@ -810,6 +843,21 @@ "interfaces": [], "enums": [], "misc": [ + { + "parentPluginId": "unifiedFieldList", + "id": "def-common.FIELD_EXISTENCE_SETTING", + "type": "string", + "tags": [], + "label": "FIELD_EXISTENCE_SETTING", + "description": [], + "signature": [ + "\"lens:useFieldExistenceSampling\"" + ], + "path": "src/plugins/unified_field_list/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "unifiedFieldList", "id": "def-common.PLUGIN_ID", diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 2fb8a110a05eb..82043a2029661 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-disco | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 51 | 0 | 49 | 2 | +| 54 | 0 | 52 | 2 | ## Client diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 0edb9ef43d436..909ff5f2d26f2 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -11,7 +11,7 @@ "label": "DataViewPicker", "description": [], "signature": [ - "({ isMissingCurrent, currentDataViewId, adHocDataViews, onChangeDataView, onAddField, onDataViewCreated, trigger, selectableProps, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, }: ", + "({ isMissingCurrent, currentDataViewId, adHocDataViews, onChangeDataView, onAddField, onDataViewCreated, trigger, selectableProps, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, isDisabled, }: ", "DataViewPickerPropsExtended", ") => JSX.Element" ], @@ -24,7 +24,7 @@ "id": "def-public.DataViewPicker.$1", "type": "Object", "tags": [], - "label": "{\n isMissingCurrent,\n currentDataViewId,\n adHocDataViews,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n}", + "label": "{\n isMissingCurrent,\n currentDataViewId,\n adHocDataViews,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n isDisabled,\n}", "description": [], "signature": [ "DataViewPickerPropsExtended" @@ -320,7 +320,7 @@ "section": "def-common.DataView", "text": "DataView" }, - "[] | undefined; isLoading?: boolean | undefined; timeHistory?: ", + "[] | undefined; isDisabled?: boolean | undefined; isLoading?: boolean | undefined; timeHistory?: ", { "pluginId": "data", "scope": "public", @@ -697,6 +697,22 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.DataViewPickerProps.isDisabled", + "type": "CompoundType", + "tags": [], + "label": "isDisabled", + "description": [ + "\nMakes the picker disabled by disabling the popover trigger" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1241,6 +1257,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QueryStringInputProps.isDisabled", + "type": "CompoundType", + "tags": [], + "label": "isDisabled", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "unifiedSearch", "id": "def-public.QueryStringInputProps.nonKqlMode", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index fc96cbb1e9cc0..5b3a5638c74ef 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 109 | 2 | 84 | 16 | +| 111 | 2 | 85 | 16 | ## Client diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 34c1ea2a1354d..bebe8e757136a 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 109 | 2 | 84 | 16 | +| 111 | 2 | 85 | 16 | ## Client diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 8a99fa786b150..ba4d70200d714 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 953cdbc32149c..eac1395483f26 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 3016625362ce1..ac07b7b6dec90 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index ff8015da299fa..27386a94c37aa 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index ff9ff105efb1a..c25eb47e0fbdc 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index f5265c4edc969..492e3f940e990 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index fc9bf5001a2ce..bd06921b3a740 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 0971c9d2d784d..32a53d10810cc 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 1601dd0ca05b0..1a0fe74391504 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 4442ae6bcf591..faaeaad6e7c4b 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 9804c7468413e..9892ae3945683 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 0f5bedc6073f3..d2862d4828d21 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index a9531484ea581..8c22d419a997c 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index dffacde50d651..352226b8a79ff 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -276,6 +276,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "visualizations", + "id": "def-public.BaseVisType.suppressWarnings", + "type": "Function", + "tags": [], + "label": "suppressWarnings", + "description": [], + "signature": [ + "(() => boolean) | undefined" + ], + "path": "src/plugins/visualizations/public/vis_types/base_vis_type.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "visualizations", "id": "def-public.BaseVisType.hasPartialRows", @@ -4785,6 +4799,24 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisTypeDefinition.suppressWarnings", + "type": "Function", + "tags": [], + "label": "suppressWarnings", + "description": [ + "\nIf returns true, no warning toasts will be shown" + ], + "signature": [ + "(() => boolean) | undefined" + ], + "path": "src/plugins/visualizations/public/vis_types/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "visualizations", "id": "def-public.VisTypeDefinition.group", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 5229d1a341157..eb856d91fcbba 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2022-09-08 +date: 2022-09-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 417 | 12 | 389 | 15 | +| 419 | 12 | 390 | 15 | ## Client diff --git a/dev_docs/contributing/code_walkthrough.mdx b/dev_docs/contributing/code_walkthrough.mdx index bae394887c20e..617b54e519f13 100644 --- a/dev_docs/contributing/code_walkthrough.mdx +++ b/dev_docs/contributing/code_walkthrough.mdx @@ -21,7 +21,7 @@ Managed by the operations team to contain Jenkins settings. Can be ignored by fo ## [.github](https://github.com/elastic/kibana/tree/main/.github) -Contains GitHub configuration settings. This file contains issue templates, and the [CODEOWNERS](https://github.com/elastic/kibana/blob/main/.github/CODEOWNERS) file. It's important for teams to keep the CODEOWNERS file up-to-date so the right team is pinged for a code owner review on PRs that edit certain files. Note that the `CODEOWNERS` file only exists on the main branch, and is not backported to other branches in the repo. +Contains GitHub configuration settings. This file contains issue templates, and the [CODEOWNERS](https://github.com/elastic/kibana/blob/main/.github/CODEOWNERS) file. It's important for teams to keep the CODEOWNERS file up-to-date so the right team is pinged for a code owner review on PRs that edit certain files. Note that the `CODEOWNERS` file only exists on the main branch, and is not backported to other branches in the repo. To aid in making sure the `CODEOWNERS` file is always up-to-date "packages" in the Kibana repo are required to have an "owner" defined, those owners are then injected into the CODEOWNERS files automatically by the `node scripts/generate codeowners` command. ## [api_docs](https://github.com/elastic/kibana/tree/main/api_docs) diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 88fc015ac8b9a..afdfbdfd02eb0 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -106,7 +106,7 @@ capturing the page with a screenshot. As a result, a download will be available, but there will likely be errors in the visualizations in the report. `xpack.screenshotting.capture.loadDelay`:: -deprecated:[8.0.0,This setting has no effect.] Specify the {time-units}[amount of time] before taking a screenshot when visualizations are not evented. All visualizations that ship with {kib} are evented, so this setting should not have much effect. If you are seeing empty images instead of visualizations, try increasing this value. Defaults to `3s`. *NOTE*: This setting exists for backwards compatibility, but is unused and therefore does not have an affect on reporting performance. +deprecated:[8.0.0,This setting has no effect.] Specify the {time-units}[amount of time] before taking a screenshot when visualizations are not evented. All visualizations that ship with {kib} are evented, so this setting should not have much effect. If you are seeing empty images instead of visualizations, try increasing this value. *NOTE*: This setting exists for backwards compatibility, but is unused and therefore does not have an affect on reporting performance. [float] [[reporting-chromium-settings]] diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx index 94cf19436c3f5..01ebd4433af10 100644 --- a/examples/search_examples/public/search/app.tsx +++ b/examples/search_examples/public/search/app.tsx @@ -107,9 +107,9 @@ export const SearchExamplesApp = ({ const [selectedBucketField, setSelectedBucketField] = useState< DataViewField | null | undefined >(); - const [request, setRequest] = useState>({}); const [isLoading, setIsLoading] = useState(false); const [currentAbortController, setAbortController] = useState(); + const [request, setRequest] = useState>({}); const [rawResponse, setRawResponse] = useState>({}); const [warningContents, setWarningContents] = useState([]); const [selectedTab, setSelectedTab] = useState(0); @@ -202,6 +202,8 @@ export const SearchExamplesApp = ({ // Submit the search request using the `data.search` service. setRequest(req.params.body); + setRawResponse({}); + setWarningContents([]); setIsLoading(true); data.search @@ -301,6 +303,8 @@ export const SearchExamplesApp = ({ searchSource.setField('aggs', ac); } setRequest(searchSource.getSearchRequestBody()); + setRawResponse({}); + setWarningContents([]); const abortController = new AbortController(); const inspector: Required = { diff --git a/kbn_pm/src/commands/run_in_packages_command.mjs b/kbn_pm/src/commands/run_in_packages_command.mjs index 78a168463e6db..7106722237e6e 100644 --- a/kbn_pm/src/commands/run_in_packages_command.mjs +++ b/kbn_pm/src/commands/run_in_packages_command.mjs @@ -41,11 +41,11 @@ export const command = { const { discoverBazelPackages } = await import('@kbn/bazel-packages'); const packages = await discoverBazelPackages(REPO_ROOT); - for (const { pkg, normalizedRepoRelativeDir } of packages) { + for (const { manifest, pkg, normalizedRepoRelativeDir } of packages) { if ( - exclude.includes(pkg.name) || - (include.length && !include.includes(pkg.name)) || - !pkg.scripts || + exclude.includes(manifest.id) || + (include.length && !include.includes(manifest.id)) || + !pkg?.scripts || !Object.hasOwn(pkg.scripts, scriptName) ) { continue; diff --git a/package.json b/package.json index 92dc1590d9a51..e53f8ed64b06b 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@dnd-kit/utilities": "^2.0.0", "@elastic/apm-rum": "^5.12.0", "@elastic/apm-rum-react": "^1.4.2", - "@elastic/charts": "48.0.0", + "@elastic/charts": "48.0.1", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.3.0-canary.1", "@elastic/ems-client": "8.3.3", @@ -140,7 +140,6 @@ "@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-synthtrace": "link:bazel-bin/packages/kbn-apm-synthtrace", "@kbn/apm-utils": "link:bazel-bin/packages/kbn-apm-utils", "@kbn/chart-icons": "link:bazel-bin/packages/kbn-chart-icons", "@kbn/coloring": "link:bazel-bin/packages/kbn-coloring", @@ -270,6 +269,11 @@ "@kbn/core-saved-objects-server-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server-internal", "@kbn/core-saved-objects-server-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server-mocks", "@kbn/core-saved-objects-utils-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-utils-server", + "@kbn/core-status-common": "link:bazel-bin/packages/core/status/core-status-common", + "@kbn/core-status-common-internal": "link:bazel-bin/packages/core/status/core-status-common-internal", + "@kbn/core-status-server": "link:bazel-bin/packages/core/status/core-status-server", + "@kbn/core-status-server-internal": "link:bazel-bin/packages/core/status/core-status-server-internal", + "@kbn/core-status-server-mocks": "link:bazel-bin/packages/core/status/core-status-server-mocks", "@kbn/core-test-helpers-deprecations-getters": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters", "@kbn/core-test-helpers-http-setup-browser": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-http-setup-browser", "@kbn/core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser", @@ -290,8 +294,6 @@ "@kbn/ebt-tools": "link:bazel-bin/packages/kbn-ebt-tools", "@kbn/es-errors": "link:bazel-bin/packages/kbn-es-errors", "@kbn/es-query": "link:bazel-bin/packages/kbn-es-query", - "@kbn/eslint-plugin-disable": "link:bazel-bin/packages/kbn-eslint-plugin-disable", - "@kbn/eslint-plugin-imports": "link:bazel-bin/packages/kbn-eslint-plugin-imports", "@kbn/field-types": "link:bazel-bin/packages/kbn-field-types", "@kbn/flot-charts": "link:bazel-bin/packages/kbn-flot-charts", "@kbn/handlebars": "link:bazel-bin/packages/kbn-handlebars", @@ -303,9 +305,6 @@ "@kbn/i18n-react": "link:bazel-bin/packages/kbn-i18n-react", "@kbn/interpreter": "link:bazel-bin/packages/kbn-interpreter", "@kbn/io-ts-utils": "link:bazel-bin/packages/kbn-io-ts-utils", - "@kbn/jsonc": "link:bazel-bin/packages/kbn-jsonc", - "@kbn/kibana-manifest-parser": "link:bazel-bin/packages/kbn-kibana-manifest-parser", - "@kbn/kibana-manifest-schema": "link:bazel-bin/packages/kbn-kibana-manifest-schema", "@kbn/logging": "link:bazel-bin/packages/kbn-logging", "@kbn/logging-mocks": "link:bazel-bin/packages/kbn-logging-mocks", "@kbn/mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl", @@ -364,7 +363,9 @@ "@kbn/shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/impl", "@kbn/shared-ux-prompt-no-data-views-mocks": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/mocks", "@kbn/shared-ux-prompt-no-data-views-types": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/types", - "@kbn/shared-ux-storybook-config": "link:bazel-bin/packages/shared-ux/storybook/config", + "@kbn/shared-ux-router-mocks": "link:bazel-bin/packages/shared-ux/router/mocks", + "@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-storybook-mock": "link:bazel-bin/packages/shared-ux/storybook/mock", "@kbn/shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility", "@kbn/std": "link:bazel-bin/packages/kbn-std", @@ -665,6 +666,7 @@ "@jest/types": "^26", "@kbn/ambient-storybook-types": "link:bazel-bin/packages/kbn-ambient-storybook-types", "@kbn/ambient-ui-types": "link:bazel-bin/packages/kbn-ambient-ui-types", + "@kbn/apm-synthtrace": "link:bazel-bin/packages/kbn-apm-synthtrace", "@kbn/axe-config": "link:bazel-bin/packages/kbn-axe-config", "@kbn/babel-plugin-synthetic-packages": "link:bazel-bin/packages/kbn-babel-plugin-synthetic-packages", "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", @@ -682,13 +684,16 @@ "@kbn/es": "link:bazel-bin/packages/kbn-es", "@kbn/es-archiver": "link:bazel-bin/packages/kbn-es-archiver", "@kbn/eslint-config": "link:bazel-bin/packages/kbn-eslint-config", + "@kbn/eslint-plugin-disable": "link:bazel-bin/packages/kbn-eslint-plugin-disable", "@kbn/eslint-plugin-eslint": "link:bazel-bin/packages/kbn-eslint-plugin-eslint", + "@kbn/eslint-plugin-imports": "link:bazel-bin/packages/kbn-eslint-plugin-imports", "@kbn/expect": "link:bazel-bin/packages/kbn-expect", "@kbn/find-used-node-modules": "link:bazel-bin/packages/kbn-find-used-node-modules", "@kbn/generate": "link:bazel-bin/packages/kbn-generate", "@kbn/get-repo-files": "link:bazel-bin/packages/kbn-get-repo-files", "@kbn/import-resolver": "link:bazel-bin/packages/kbn-import-resolver", "@kbn/jest-serializers": "link:bazel-bin/packages/kbn-jest-serializers", + "@kbn/kibana-manifest-schema": "link:bazel-bin/packages/kbn-kibana-manifest-schema", "@kbn/managed-vscode-config": "link:bazel-bin/packages/kbn-managed-vscode-config", "@kbn/managed-vscode-config-cli": "link:bazel-bin/packages/kbn-managed-vscode-config-cli", "@kbn/optimizer": "link:bazel-bin/packages/kbn-optimizer", @@ -958,6 +963,11 @@ "@types/kbn__core-saved-objects-server-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server-mocks/npm_module_types", "@types/kbn__core-saved-objects-utils-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-utils-server/npm_module_types", "@types/kbn__core-server-internal-base": "link:bazel-bin/packages/core/server/internal-base/npm_module_types", + "@types/kbn__core-status-common": "link:bazel-bin/packages/core/status/core-status-common/npm_module_types", + "@types/kbn__core-status-common-internal": "link:bazel-bin/packages/core/status/core-status-common-internal/npm_module_types", + "@types/kbn__core-status-server": "link:bazel-bin/packages/core/status/core-status-server/npm_module_types", + "@types/kbn__core-status-server-internal": "link:bazel-bin/packages/core/status/core-status-server-internal/npm_module_types", + "@types/kbn__core-status-server-mocks": "link:bazel-bin/packages/core/status/core-status-server-mocks/npm_module_types", "@types/kbn__core-test-helpers-deprecations-getters": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters/npm_module_types", "@types/kbn__core-test-helpers-http-setup-browser": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-http-setup-browser/npm_module_types", "@types/kbn__core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser/npm_module_types", @@ -1001,9 +1011,7 @@ "@types/kbn__interpreter": "link:bazel-bin/packages/kbn-interpreter/npm_module_types", "@types/kbn__io-ts-utils": "link:bazel-bin/packages/kbn-io-ts-utils/npm_module_types", "@types/kbn__jest-serializers": "link:bazel-bin/packages/kbn-jest-serializers/npm_module_types", - "@types/kbn__jsonc": "link:bazel-bin/packages/kbn-jsonc/npm_module_types", "@types/kbn__kbn-ci-stats-performance-metrics": "link:bazel-bin/packages/kbn-kbn-ci-stats-performance-metrics/npm_module_types", - "@types/kbn__kibana-manifest-parser": "link:bazel-bin/packages/kbn-kibana-manifest-parser/npm_module_types", "@types/kbn__kibana-manifest-schema": "link:bazel-bin/packages/kbn-kibana-manifest-schema/npm_module_types", "@types/kbn__logging": "link:bazel-bin/packages/kbn-logging/npm_module_types", "@types/kbn__logging-mocks": "link:bazel-bin/packages/kbn-logging-mocks/npm_module_types", @@ -1071,7 +1079,9 @@ "@types/kbn__shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/impl/npm_module_types", "@types/kbn__shared-ux-prompt-no-data-views-mocks": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/mocks/npm_module_types", "@types/kbn__shared-ux-prompt-no-data-views-types": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/types/npm_module_types", - "@types/kbn__shared-ux-storybook-config": "link:bazel-bin/packages/shared-ux/storybook/config/npm_module_types", + "@types/kbn__shared-ux-router-mocks": "link:bazel-bin/packages/shared-ux/router/mocks/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-storybook-mock": "link:bazel-bin/packages/shared-ux/storybook/mock/npm_module_types", "@types/kbn__shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility/npm_module_types", "@types/kbn__some-dev-log": "link:bazel-bin/packages/kbn-some-dev-log/npm_module_types", @@ -1307,7 +1317,7 @@ "ms-chromium-edge-driver": "^0.5.1", "mutation-observer": "^1.0.3", "nock": "12.0.3", - "node-sass": "7.0.1", + "node-sass": "^7.0.3", "null-loader": "^3.0.0", "nyc": "^15.1.0", "oboe": "^2.1.4", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index b5bd2f384d76b..b898faee5fb07 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -135,6 +135,11 @@ filegroup( "//packages/core/saved-objects/core-saved-objects-server-internal:build", "//packages/core/saved-objects/core-saved-objects-server-mocks:build", "//packages/core/saved-objects/core-saved-objects-utils-server:build", + "//packages/core/status/core-status-common:build", + "//packages/core/status/core-status-common-internal:build", + "//packages/core/status/core-status-server:build", + "//packages/core/status/core-status-server-internal:build", + "//packages/core/status/core-status-server-mocks:build", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build", "//packages/core/test-helpers/core-test-helpers-http-setup-browser:build", "//packages/core/theme/core-theme-browser:build", @@ -205,8 +210,6 @@ filegroup( "//packages/kbn-interpreter:build", "//packages/kbn-io-ts-utils:build", "//packages/kbn-jest-serializers:build", - "//packages/kbn-jsonc:build", - "//packages/kbn-kibana-manifest-parser:build", "//packages/kbn-kibana-manifest-schema:build", "//packages/kbn-logging:build", "//packages/kbn-logging-mocks:build", @@ -440,6 +443,11 @@ filegroup( "//packages/core/saved-objects/core-saved-objects-server-internal:build_types", "//packages/core/saved-objects/core-saved-objects-server-mocks:build_types", "//packages/core/saved-objects/core-saved-objects-utils-server:build_types", + "//packages/core/status/core-status-common:build_types", + "//packages/core/status/core-status-common-internal:build_types", + "//packages/core/status/core-status-server:build_types", + "//packages/core/status/core-status-server-internal:build_types", + "//packages/core/status/core-status-server-mocks:build_types", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build_types", "//packages/core/test-helpers/core-test-helpers-http-setup-browser:build_types", "//packages/core/theme/core-theme-browser:build_types", @@ -500,8 +508,6 @@ filegroup( "//packages/kbn-interpreter:build_types", "//packages/kbn-io-ts-utils:build_types", "//packages/kbn-jest-serializers:build_types", - "//packages/kbn-jsonc:build_types", - "//packages/kbn-kibana-manifest-parser:build_types", "//packages/kbn-kibana-manifest-schema:build_types", "//packages/kbn-logging:build_types", "//packages/kbn-logging-mocks:build_types", diff --git a/packages/analytics/client/kibana.jsonc b/packages/analytics/client/kibana.jsonc new file mode 100644 index 0000000000000..5f5aa11feb994 --- /dev/null +++ b/packages/analytics/client/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics-client", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/analytics/shippers/elastic_v3/browser/kibana.jsonc b/packages/analytics/shippers/elastic_v3/browser/kibana.jsonc new file mode 100644 index 0000000000000..cefab4152c994 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics-shippers-elastic-v3-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/analytics/shippers/elastic_v3/common/kibana.jsonc b/packages/analytics/shippers/elastic_v3/common/kibana.jsonc new file mode 100644 index 0000000000000..c347233693ff3 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics-shippers-elastic-v3-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/analytics/shippers/elastic_v3/server/kibana.jsonc b/packages/analytics/shippers/elastic_v3/server/kibana.jsonc new file mode 100644 index 0000000000000..11c29924f3c21 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics-shippers-elastic-v3-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/analytics/shippers/fullstory/kibana.jsonc b/packages/analytics/shippers/fullstory/kibana.jsonc new file mode 100644 index 0000000000000..5d8720fa7486c --- /dev/null +++ b/packages/analytics/shippers/fullstory/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics-shippers-fullstory", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-browser-internal/kibana.jsonc b/packages/core/analytics/core-analytics-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..45bd5d5bc041a --- /dev/null +++ b/packages/core/analytics/core-analytics-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-browser-mocks/kibana.jsonc b/packages/core/analytics/core-analytics-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..2c3ce58f95d62 --- /dev/null +++ b/packages/core/analytics/core-analytics-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-browser/kibana.jsonc b/packages/core/analytics/core-analytics-browser/kibana.jsonc new file mode 100644 index 0000000000000..a17a1e5d5e94b --- /dev/null +++ b/packages/core/analytics/core-analytics-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-server-internal/kibana.jsonc b/packages/core/analytics/core-analytics-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..1ae2d06e0fa77 --- /dev/null +++ b/packages/core/analytics/core-analytics-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-server-mocks/kibana.jsonc b/packages/core/analytics/core-analytics-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..9c49235144c4d --- /dev/null +++ b/packages/core/analytics/core-analytics-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/analytics/core-analytics-server/kibana.jsonc b/packages/core/analytics/core-analytics-server/kibana.jsonc new file mode 100644 index 0000000000000..d8faa138efc72 --- /dev/null +++ b/packages/core/analytics/core-analytics-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-analytics-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/application/core-application-browser-internal/kibana.jsonc b/packages/core/application/core-application-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..5ebb9290df118 --- /dev/null +++ b/packages/core/application/core-application-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-application-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/application/core-application-browser-mocks/kibana.jsonc b/packages/core/application/core-application-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..bdbeafdcc2652 --- /dev/null +++ b/packages/core/application/core-application-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-application-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/application/core-application-browser/kibana.jsonc b/packages/core/application/core-application-browser/kibana.jsonc new file mode 100644 index 0000000000000..6a8931fa36f74 --- /dev/null +++ b/packages/core/application/core-application-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-application-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/application/core-application-common/kibana.jsonc b/packages/core/application/core-application-common/kibana.jsonc new file mode 100644 index 0000000000000..da1cc4d8f7d9b --- /dev/null +++ b/packages/core/application/core-application-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-application-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-browser-internal/kibana.jsonc b/packages/core/base/core-base-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..7254343b42c5f --- /dev/null +++ b/packages/core/base/core-base-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-browser-mocks/kibana.jsonc b/packages/core/base/core-base-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..5911ba33ca9d4 --- /dev/null +++ b/packages/core/base/core-base-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-common-internal/kibana.jsonc b/packages/core/base/core-base-common-internal/kibana.jsonc new file mode 100644 index 0000000000000..61abd6d9a872c --- /dev/null +++ b/packages/core/base/core-base-common-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-common-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-common/index.ts b/packages/core/base/core-base-common/index.ts index b2bbc96bc6a00..d2f7daafb3341 100644 --- a/packages/core/base/core-base-common/index.ts +++ b/packages/core/base/core-base-common/index.ts @@ -9,5 +9,3 @@ export type { PluginOpaqueId, PluginName, DiscoveredPlugin } from './src/plugins'; export { PluginType } from './src/plugins'; export { EUI_STYLES_GLOBAL } from './src/eui'; -export { ServiceStatusLevels } from './src/service_status'; -export type { ServiceStatus, ServiceStatusLevel } from './src/service_status'; diff --git a/packages/core/base/core-base-common/kibana.jsonc b/packages/core/base/core-base-common/kibana.jsonc new file mode 100644 index 0000000000000..d72d5da919f1c --- /dev/null +++ b/packages/core/base/core-base-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-server-internal/kibana.jsonc b/packages/core/base/core-base-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..0a21a2b7e6384 --- /dev/null +++ b/packages/core/base/core-base-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/base/core-base-server-mocks/kibana.jsonc b/packages/core/base/core-base-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..762615e557b81 --- /dev/null +++ b/packages/core/base/core-base-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-base-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-browser-internal/kibana.jsonc b/packages/core/capabilities/core-capabilities-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..48f55a81a7a68 --- /dev/null +++ b/packages/core/capabilities/core-capabilities-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-browser-mocks/kibana.jsonc b/packages/core/capabilities/core-capabilities-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..5e6ddff3a4283 --- /dev/null +++ b/packages/core/capabilities/core-capabilities-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-common/kibana.jsonc b/packages/core/capabilities/core-capabilities-common/kibana.jsonc new file mode 100644 index 0000000000000..5349e81ad3626 --- /dev/null +++ b/packages/core/capabilities/core-capabilities-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-server-internal/kibana.jsonc b/packages/core/capabilities/core-capabilities-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..3c464af07ea43 --- /dev/null +++ b/packages/core/capabilities/core-capabilities-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-server-mocks/kibana.jsonc b/packages/core/capabilities/core-capabilities-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..5404ed714a6eb --- /dev/null +++ b/packages/core/capabilities/core-capabilities-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/capabilities/core-capabilities-server/kibana.jsonc b/packages/core/capabilities/core-capabilities-server/kibana.jsonc new file mode 100644 index 0000000000000..dc6e6ac3c1279 --- /dev/null +++ b/packages/core/capabilities/core-capabilities-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-capabilities-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc b/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..2d0a7bada7bb9 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-chrome-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc b/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..5968ce1224da8 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-chrome-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/chrome/core-chrome-browser/kibana.jsonc b/packages/core/chrome/core-chrome-browser/kibana.jsonc new file mode 100644 index 0000000000000..64eba06444507 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-chrome-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/config/core-config-server-internal/kibana.jsonc b/packages/core/config/core-config-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..a6ba80afe9590 --- /dev/null +++ b/packages/core/config/core-config-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-config-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-browser-internal/kibana.jsonc b/packages/core/deprecations/core-deprecations-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..c5bf07aa7052f --- /dev/null +++ b/packages/core/deprecations/core-deprecations-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-browser-mocks/kibana.jsonc b/packages/core/deprecations/core-deprecations-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..28424208cd582 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-browser/kibana.jsonc b/packages/core/deprecations/core-deprecations-browser/kibana.jsonc new file mode 100644 index 0000000000000..3e708f34935b5 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-common/kibana.jsonc b/packages/core/deprecations/core-deprecations-common/kibana.jsonc new file mode 100644 index 0000000000000..60494dec2502f --- /dev/null +++ b/packages/core/deprecations/core-deprecations-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-server-internal/kibana.jsonc b/packages/core/deprecations/core-deprecations-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..367adb98a89b2 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-server-mocks/kibana.jsonc b/packages/core/deprecations/core-deprecations-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..fc56bc34368f1 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/deprecations/core-deprecations-server/kibana.jsonc b/packages/core/deprecations/core-deprecations-server/kibana.jsonc new file mode 100644 index 0000000000000..96270007d4ad3 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-deprecations-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-browser-internal/kibana.jsonc b/packages/core/doc-links/core-doc-links-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..b72ad3a17021a --- /dev/null +++ b/packages/core/doc-links/core-doc-links-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-browser-mocks/kibana.jsonc b/packages/core/doc-links/core-doc-links-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..d1c5b7af28390 --- /dev/null +++ b/packages/core/doc-links/core-doc-links-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-browser/kibana.jsonc b/packages/core/doc-links/core-doc-links-browser/kibana.jsonc new file mode 100644 index 0000000000000..68ed9f30aff6b --- /dev/null +++ b/packages/core/doc-links/core-doc-links-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-server-internal/kibana.jsonc b/packages/core/doc-links/core-doc-links-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..f2158ec018209 --- /dev/null +++ b/packages/core/doc-links/core-doc-links-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-server-mocks/kibana.jsonc b/packages/core/doc-links/core-doc-links-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..5d61cf066d312 --- /dev/null +++ b/packages/core/doc-links/core-doc-links-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/doc-links/core-doc-links-server/kibana.jsonc b/packages/core/doc-links/core-doc-links-server/kibana.jsonc new file mode 100644 index 0000000000000..e0460f4da99bc --- /dev/null +++ b/packages/core/doc-links/core-doc-links-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-doc-links-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/kibana.jsonc b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..e2393e888d5de --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-elasticsearch-client-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/kibana.jsonc b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..ae267b45ddc1c --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-elasticsearch-client-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/BUILD.bazel b/packages/core/elasticsearch/core-elasticsearch-server-internal/BUILD.bazel index ab9738d8a6ca3..0609c66baced3 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/BUILD.bazel +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/BUILD.bazel @@ -70,6 +70,7 @@ TYPES_DEPS = [ "//packages/kbn-config-schema:npm_module_types", "//packages/core/base/core-base-common:npm_module_types", "//packages/core/base/core-base-server-internal:npm_module_types", + "//packages/core/status/core-status-common:npm_module_types", "//packages/core/analytics/core-analytics-server:npm_module_types", "//packages/core/http/core-http-server:npm_module_types", "//packages/core/http/core-http-server-internal:npm_module_types", diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/kibana.jsonc b/packages/core/elasticsearch/core-elasticsearch-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..34ea515ba045f --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-elasticsearch-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.test.ts index f6aec43e680c4..8889125bf1d4e 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.test.ts @@ -9,7 +9,7 @@ import { take } from 'rxjs/operators'; import { Subject, of } from 'rxjs'; -import { ServiceStatusLevels, ServiceStatusLevel, ServiceStatus } from '@kbn/core-base-common'; +import { ServiceStatusLevels, ServiceStatusLevel, ServiceStatus } from '@kbn/core-status-common'; import { calculateStatus$ } from './status'; import { NodesVersionCompatibility } from './version_check/ensure_es_version'; diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.ts index 5a38f11432b7b..fbb11824d8e83 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/status.ts @@ -8,7 +8,7 @@ import { Observable, merge, of } from 'rxjs'; import { map } from 'rxjs/operators'; -import { ServiceStatus, ServiceStatusLevels } from '@kbn/core-base-common'; +import { ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { ElasticsearchStatusMeta } from './types'; import { NodesVersionCompatibility } from './version_check/ensure_es_version'; diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts index 57f3620a7911d..8d05ad0c4cd0a 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts @@ -12,7 +12,7 @@ import type { ElasticsearchServiceStart, ElasticsearchServiceSetup, } from '@kbn/core-elasticsearch-server'; -import type { ServiceStatus } from '@kbn/core-base-common'; +import type { ServiceStatus } from '@kbn/core-status-common'; import type { NodesVersionCompatibility, NodeInfo } from './version_check/ensure_es_version'; import type { ClusterInfo } from './get_cluster_info'; diff --git a/packages/core/elasticsearch/core-elasticsearch-server-mocks/BUILD.bazel b/packages/core/elasticsearch/core-elasticsearch-server-mocks/BUILD.bazel index da292814321f7..41de319850636 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-mocks/BUILD.bazel +++ b/packages/core/elasticsearch/core-elasticsearch-server-mocks/BUILD.bazel @@ -43,7 +43,7 @@ TYPES_DEPS = [ "@npm//@types/jest", "@npm//rxjs", "//packages/kbn-utility-types:npm_module_types", - "//packages/core/base/core-base-common:npm_module_types", + "//packages/core/status/core-status-common:npm_module_types", "//packages/core/elasticsearch/core-elasticsearch-server:npm_module_types", "//packages/core/elasticsearch/core-elasticsearch-client-server-mocks:npm_module_types", "//packages/core/elasticsearch/core-elasticsearch-server-internal:npm_module_types", diff --git a/packages/core/elasticsearch/core-elasticsearch-server-mocks/kibana.jsonc b/packages/core/elasticsearch/core-elasticsearch-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..d08d5d04cbb39 --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-elasticsearch-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts b/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts index aebf2c290ea57..fcaca91264d4f 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts @@ -27,7 +27,7 @@ import type { NodesVersionCompatibility, ClusterInfo, } from '@kbn/core-elasticsearch-server-internal'; -import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-base-common'; +import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; type MockedElasticSearchServicePreboot = jest.Mocked; diff --git a/packages/core/elasticsearch/core-elasticsearch-server/kibana.jsonc b/packages/core/elasticsearch/core-elasticsearch-server/kibana.jsonc new file mode 100644 index 0000000000000..5bf72319cc41b --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-elasticsearch-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/environment/core-environment-server-internal/kibana.jsonc b/packages/core/environment/core-environment-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..9d8de1124ce0f --- /dev/null +++ b/packages/core/environment/core-environment-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-environment-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/environment/core-environment-server-mocks/kibana.jsonc b/packages/core/environment/core-environment-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..6644816727a70 --- /dev/null +++ b/packages/core/environment/core-environment-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-environment-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-browser-internal/kibana.jsonc b/packages/core/execution-context/core-execution-context-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..1771d94a14b8a --- /dev/null +++ b/packages/core/execution-context/core-execution-context-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-browser-mocks/kibana.jsonc b/packages/core/execution-context/core-execution-context-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..8d27a9ec919dd --- /dev/null +++ b/packages/core/execution-context/core-execution-context-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-browser/kibana.jsonc b/packages/core/execution-context/core-execution-context-browser/kibana.jsonc new file mode 100644 index 0000000000000..550d63fc67de1 --- /dev/null +++ b/packages/core/execution-context/core-execution-context-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-common/kibana.jsonc b/packages/core/execution-context/core-execution-context-common/kibana.jsonc new file mode 100644 index 0000000000000..d3cf33be164d2 --- /dev/null +++ b/packages/core/execution-context/core-execution-context-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-server-internal/kibana.jsonc b/packages/core/execution-context/core-execution-context-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..d344f53f34f57 --- /dev/null +++ b/packages/core/execution-context/core-execution-context-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-server-mocks/kibana.jsonc b/packages/core/execution-context/core-execution-context-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..e73d0b4f9b216 --- /dev/null +++ b/packages/core/execution-context/core-execution-context-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/execution-context/core-execution-context-server/kibana.jsonc b/packages/core/execution-context/core-execution-context-server/kibana.jsonc new file mode 100644 index 0000000000000..5de7baaaccb32 --- /dev/null +++ b/packages/core/execution-context/core-execution-context-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-execution-context-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/fatal-errors/core-fatal-errors-browser-internal/kibana.jsonc b/packages/core/fatal-errors/core-fatal-errors-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..76752593fd00c --- /dev/null +++ b/packages/core/fatal-errors/core-fatal-errors-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-fatal-errors-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/fatal-errors/core-fatal-errors-browser-mocks/kibana.jsonc b/packages/core/fatal-errors/core-fatal-errors-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..6109111801eb1 --- /dev/null +++ b/packages/core/fatal-errors/core-fatal-errors-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-fatal-errors-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/fatal-errors/core-fatal-errors-browser/kibana.jsonc b/packages/core/fatal-errors/core-fatal-errors-browser/kibana.jsonc new file mode 100644 index 0000000000000..95423568bca93 --- /dev/null +++ b/packages/core/fatal-errors/core-fatal-errors-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-fatal-errors-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-browser-internal/kibana.jsonc b/packages/core/http/core-http-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..d5855d71ca178 --- /dev/null +++ b/packages/core/http/core-http-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-browser-mocks/kibana.jsonc b/packages/core/http/core-http-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..9819977cb419d --- /dev/null +++ b/packages/core/http/core-http-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-browser/kibana.jsonc b/packages/core/http/core-http-browser/kibana.jsonc new file mode 100644 index 0000000000000..87510d65336e3 --- /dev/null +++ b/packages/core/http/core-http-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-common/kibana.jsonc b/packages/core/http/core-http-common/kibana.jsonc new file mode 100644 index 0000000000000..bdf00df353c63 --- /dev/null +++ b/packages/core/http/core-http-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-context-server-internal/kibana.jsonc b/packages/core/http/core-http-context-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..18f306d721759 --- /dev/null +++ b/packages/core/http/core-http-context-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-context-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-context-server-mocks/kibana.jsonc b/packages/core/http/core-http-context-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..323d78350596b --- /dev/null +++ b/packages/core/http/core-http-context-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-context-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-router-server-internal/kibana.jsonc b/packages/core/http/core-http-router-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..5f7482d9fa06c --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-router-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-router-server-mocks/kibana.jsonc b/packages/core/http/core-http-router-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..a1883a9c92ff7 --- /dev/null +++ b/packages/core/http/core-http-router-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-router-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-server-internal/kibana.jsonc b/packages/core/http/core-http-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..0d1d5b04eaae2 --- /dev/null +++ b/packages/core/http/core-http-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-server-mocks/kibana.jsonc b/packages/core/http/core-http-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..598898176f62c --- /dev/null +++ b/packages/core/http/core-http-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/http/core-http-server/kibana.jsonc b/packages/core/http/core-http-server/kibana.jsonc new file mode 100644 index 0000000000000..da671fec6aaec --- /dev/null +++ b/packages/core/http/core-http-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-browser-internal/kibana.jsonc b/packages/core/i18n/core-i18n-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..424e9d3dcdbcd --- /dev/null +++ b/packages/core/i18n/core-i18n-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-browser-mocks/kibana.jsonc b/packages/core/i18n/core-i18n-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..0f5b73ed3b453 --- /dev/null +++ b/packages/core/i18n/core-i18n-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-browser/kibana.jsonc b/packages/core/i18n/core-i18n-browser/kibana.jsonc new file mode 100644 index 0000000000000..dcbf951b201f1 --- /dev/null +++ b/packages/core/i18n/core-i18n-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-server-internal/kibana.jsonc b/packages/core/i18n/core-i18n-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..3b89a42976bbc --- /dev/null +++ b/packages/core/i18n/core-i18n-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-server-mocks/kibana.jsonc b/packages/core/i18n/core-i18n-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..41ef001641b57 --- /dev/null +++ b/packages/core/i18n/core-i18n-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/i18n/core-i18n-server/kibana.jsonc b/packages/core/i18n/core-i18n-server/kibana.jsonc new file mode 100644 index 0000000000000..c32d5d9cd8e7b --- /dev/null +++ b/packages/core/i18n/core-i18n-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-i18n-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/injected-metadata/core-injected-metadata-browser-internal/kibana.jsonc b/packages/core/injected-metadata/core-injected-metadata-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..d66f834c08eb3 --- /dev/null +++ b/packages/core/injected-metadata/core-injected-metadata-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-injected-metadata-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/injected-metadata/core-injected-metadata-browser-mocks/kibana.jsonc b/packages/core/injected-metadata/core-injected-metadata-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..cfbfae1b907e2 --- /dev/null +++ b/packages/core/injected-metadata/core-injected-metadata-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-injected-metadata-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/injected-metadata/core-injected-metadata-browser/kibana.jsonc b/packages/core/injected-metadata/core-injected-metadata-browser/kibana.jsonc new file mode 100644 index 0000000000000..d10647084cd47 --- /dev/null +++ b/packages/core/injected-metadata/core-injected-metadata-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-injected-metadata-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/injected-metadata/core-injected-metadata-common-internal/kibana.jsonc b/packages/core/injected-metadata/core-injected-metadata-common-internal/kibana.jsonc new file mode 100644 index 0000000000000..88943c788515f --- /dev/null +++ b/packages/core/injected-metadata/core-injected-metadata-common-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-injected-metadata-common-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/integrations/core-integrations-browser-internal/kibana.jsonc b/packages/core/integrations/core-integrations-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..fd72743c08597 --- /dev/null +++ b/packages/core/integrations/core-integrations-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-integrations-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/integrations/core-integrations-browser-mocks/kibana.jsonc b/packages/core/integrations/core-integrations-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..a4bcddaecba14 --- /dev/null +++ b/packages/core/integrations/core-integrations-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-integrations-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/logging/core-logging-server-internal/kibana.jsonc b/packages/core/logging/core-logging-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..ec5ab06a6effd --- /dev/null +++ b/packages/core/logging/core-logging-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-logging-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/logging/core-logging-server-mocks/kibana.jsonc b/packages/core/logging/core-logging-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..83793b02fca6b --- /dev/null +++ b/packages/core/logging/core-logging-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-logging-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/logging/core-logging-server/kibana.jsonc b/packages/core/logging/core-logging-server/kibana.jsonc new file mode 100644 index 0000000000000..27dadd782dcdc --- /dev/null +++ b/packages/core/logging/core-logging-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-logging-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/kibana.jsonc b/packages/core/metrics/core-metrics-collectors-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..39a1aff44dba5 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-metrics-collectors-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/metrics/core-metrics-collectors-server-mocks/kibana.jsonc b/packages/core/metrics/core-metrics-collectors-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..053d67afb5f68 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-metrics-collectors-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/metrics/core-metrics-server-internal/kibana.jsonc b/packages/core/metrics/core-metrics-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..325f7e64bbb52 --- /dev/null +++ b/packages/core/metrics/core-metrics-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-metrics-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/metrics/core-metrics-server-mocks/kibana.jsonc b/packages/core/metrics/core-metrics-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..6af29213a86ce --- /dev/null +++ b/packages/core/metrics/core-metrics-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-metrics-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/metrics/core-metrics-server/kibana.jsonc b/packages/core/metrics/core-metrics-server/kibana.jsonc new file mode 100644 index 0000000000000..64136f9466cb7 --- /dev/null +++ b/packages/core/metrics/core-metrics-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-metrics-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/mount-utils/core-mount-utils-browser-internal/kibana.jsonc b/packages/core/mount-utils/core-mount-utils-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..c0853a96b395a --- /dev/null +++ b/packages/core/mount-utils/core-mount-utils-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-mount-utils-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/mount-utils/core-mount-utils-browser/kibana.jsonc b/packages/core/mount-utils/core-mount-utils-browser/kibana.jsonc new file mode 100644 index 0000000000000..8f8977af53327 --- /dev/null +++ b/packages/core/mount-utils/core-mount-utils-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-mount-utils-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/node/core-node-server-internal/kibana.jsonc b/packages/core/node/core-node-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..dedee6005b483 --- /dev/null +++ b/packages/core/node/core-node-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-node-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/node/core-node-server-mocks/kibana.jsonc b/packages/core/node/core-node-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..7070f0218b1c9 --- /dev/null +++ b/packages/core/node/core-node-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-node-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/node/core-node-server/kibana.jsonc b/packages/core/node/core-node-server/kibana.jsonc new file mode 100644 index 0000000000000..a2322cffe8ac4 --- /dev/null +++ b/packages/core/node/core-node-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-node-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/notifications/core-notifications-browser-internal/kibana.jsonc b/packages/core/notifications/core-notifications-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..03ad251d65c7b --- /dev/null +++ b/packages/core/notifications/core-notifications-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-notifications-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/notifications/core-notifications-browser-mocks/kibana.jsonc b/packages/core/notifications/core-notifications-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..d1c1d8f58f935 --- /dev/null +++ b/packages/core/notifications/core-notifications-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-notifications-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/notifications/core-notifications-browser/kibana.jsonc b/packages/core/notifications/core-notifications-browser/kibana.jsonc new file mode 100644 index 0000000000000..ae6140a86a200 --- /dev/null +++ b/packages/core/notifications/core-notifications-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-notifications-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/overlays/core-overlays-browser-internal/kibana.jsonc b/packages/core/overlays/core-overlays-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..8890a3c132d66 --- /dev/null +++ b/packages/core/overlays/core-overlays-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-overlays-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/overlays/core-overlays-browser-mocks/kibana.jsonc b/packages/core/overlays/core-overlays-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..61b14d5cbc8b0 --- /dev/null +++ b/packages/core/overlays/core-overlays-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-overlays-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/overlays/core-overlays-browser/kibana.jsonc b/packages/core/overlays/core-overlays-browser/kibana.jsonc new file mode 100644 index 0000000000000..de43c7689f1fc --- /dev/null +++ b/packages/core/overlays/core-overlays-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-overlays-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/preboot/core-preboot-server-internal/kibana.jsonc b/packages/core/preboot/core-preboot-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..9a2eadb716ea8 --- /dev/null +++ b/packages/core/preboot/core-preboot-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-preboot-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/preboot/core-preboot-server-mocks/kibana.jsonc b/packages/core/preboot/core-preboot-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..87a035b99530e --- /dev/null +++ b/packages/core/preboot/core-preboot-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-preboot-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/preboot/core-preboot-server/kibana.jsonc b/packages/core/preboot/core-preboot-server/kibana.jsonc new file mode 100644 index 0000000000000..e529cfdd65685 --- /dev/null +++ b/packages/core/preboot/core-preboot-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-preboot-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/rendering/core-rendering-browser-internal/kibana.jsonc b/packages/core/rendering/core-rendering-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..aaca72f8b4843 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-rendering-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/rendering/core-rendering-browser-mocks/kibana.jsonc b/packages/core/rendering/core-rendering-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..82b891ad721d3 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-rendering-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-api-browser/kibana.jsonc new file mode 100644 index 0000000000000..dc9bc275a4cb1 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-api-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-api-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..afef1f03740a0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-api-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-api-server-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-api-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..beb632af28c08 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-api-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-api-server/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-api-server/kibana.jsonc new file mode 100644 index 0000000000000..08ebe81051b65 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-api-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-base-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..a14d74263ec95 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-base-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-base-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..deab59887d747 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-base-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..ec1e45b0ffb38 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..cf0623a36b708 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-browser/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-browser/kibana.jsonc new file mode 100644 index 0000000000000..923add888b03e --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-common/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-common/kibana.jsonc new file mode 100644 index 0000000000000..d82a9bf67d323 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..eabb9e670baf3 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-import-export-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-import-export-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..174960bd1aadc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-import-export-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-migration-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..4a04817a139c2 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-migration-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-migration-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..b27f6951cb0d0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-migration-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-server-internal/BUILD.bazel index 5c5474ce0737a..d071fd819314d 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/BUILD.bazel +++ b/packages/core/saved-objects/core-saved-objects-server-internal/BUILD.bazel @@ -39,6 +39,7 @@ RUNTIME_DEPS = [ "@npm//json-stable-stringify", "//packages/kbn-config-schema", "//packages/core/base/core-base-common", + "//packages/core/status/core-status-common", "//packages/core/saved-objects/core-saved-objects-base-server-internal", "//packages/core/saved-objects/core-saved-objects-api-server-internal", "//packages/core/saved-objects/core-saved-objects-migration-server-internal", @@ -52,6 +53,7 @@ TYPES_DEPS = [ "//packages/kbn-config-schema:npm_module_types", "//packages/kbn-logging:npm_module_types", "//packages/core/base/core-base-common:npm_module_types", + "//packages/core/status/core-status-common:npm_module_types", "//packages/core/deprecations/core-deprecations-common:npm_module_types", "//packages/core/http/core-http-server:npm_module_types", "//packages/core/http/core-http-server-internal:npm_module_types", diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..43cadd207f914 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts index c158bb758f624..8036a997d0d51 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts @@ -9,7 +9,7 @@ import { Subject, Observable, firstValueFrom } from 'rxjs'; import { filter, take, switchMap } from 'rxjs/operators'; import type { Logger } from '@kbn/logging'; -import type { ServiceStatus } from '@kbn/core-base-common'; +import type { ServiceStatus } from '@kbn/core-status-common'; import type { CoreContext, CoreService } from '@kbn/core-base-server-internal'; import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; import type { KibanaRequest } from '@kbn/core-http-server'; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts index 3565ab406feb3..a6c07ee6ea1ba 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts @@ -7,7 +7,7 @@ */ import { of, Observable } from 'rxjs'; -import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-base-common'; +import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { calculateStatus$ } from './status'; import { take } from 'rxjs/operators'; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.ts index 65c7b21874881..ab00eeab78888 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.ts @@ -8,7 +8,7 @@ import { Observable, combineLatest } from 'rxjs'; import { startWith, map } from 'rxjs/operators'; -import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-base-common'; +import { type ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import type { SavedObjectStatusMeta } from '@kbn/core-saved-objects-server'; import type { KibanaMigratorStatus } from '@kbn/core-saved-objects-base-server-internal'; diff --git a/packages/core/saved-objects/core-saved-objects-server-mocks/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-server-mocks/BUILD.bazel index 2720f07e77aa9..717a2bfa6087e 100644 --- a/packages/core/saved-objects/core-saved-objects-server-mocks/BUILD.bazel +++ b/packages/core/saved-objects/core-saved-objects-server-mocks/BUILD.bazel @@ -36,7 +36,7 @@ NPM_MODULE_EXTRA_FILES = [ RUNTIME_DEPS = [ "@npm//rxjs", - "//packages/core/base/core-base-common", + "//packages/core/status/core-status-common", "//packages/core/saved-objects/core-saved-objects-api-server-mocks", "//packages/core/saved-objects/core-saved-objects-base-server-mocks", "//packages/core/saved-objects/core-saved-objects-import-export-server-mocks", @@ -48,7 +48,7 @@ TYPES_DEPS = [ "@npm//@types/jest", "@npm//rxjs", "//packages/kbn-utility-types:npm_module_types", - "//packages/core/base/core-base-common:npm_module_types", + "//packages/core/status/core-status-common:npm_module_types", "//packages/core/saved-objects/core-saved-objects-server:npm_module_types", "//packages/core/saved-objects/core-saved-objects-server-internal:npm_module_types", "//packages/core/saved-objects/core-saved-objects-api-server-mocks:npm_module_types", diff --git a/packages/core/saved-objects/core-saved-objects-server-mocks/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..c9cb96751b210 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts b/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts index 3b06cc2c6f3c9..9939e5d3240e9 100644 --- a/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts @@ -8,7 +8,7 @@ import { BehaviorSubject } from 'rxjs'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import { ServiceStatusLevels } from '@kbn/core-base-common'; +import { ServiceStatusLevels } from '@kbn/core-status-common'; import type { SavedObjectsServiceSetup, SavedObjectsServiceStart, diff --git a/packages/core/saved-objects/core-saved-objects-server/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-server/kibana.jsonc new file mode 100644 index 0000000000000..8e1a56b92f1f2 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/kibana.jsonc b/packages/core/saved-objects/core-saved-objects-utils-server/kibana.jsonc new file mode 100644 index 0000000000000..61d87e1fe9bdc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-saved-objects-utils-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/status/core-status-common-internal/BUILD.bazel b/packages/core/status/core-status-common-internal/BUILD.bazel new file mode 100644 index 0000000000000..db1d2ac2b6278 --- /dev/null +++ b/packages/core/status/core-status-common-internal/BUILD.bazel @@ -0,0 +1,115 @@ +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 = "core-status-common-internal" +PKG_REQUIRE_NAME = "@kbn/core-status-common-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/core/status/core-status-common:npm_module_types", + "//packages/core/metrics/core-metrics-server: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, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + 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/core/status/core-status-common-internal/README.md b/packages/core/status/core-status-common-internal/README.md new file mode 100644 index 0000000000000..f4e4af7fd3b3a --- /dev/null +++ b/packages/core/status/core-status-common-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-status-common-internal + +This package contains the common internal types for Core's `status` domain. diff --git a/packages/kbn-kibana-manifest-parser/index.ts b/packages/core/status/core-status-common-internal/index.ts similarity index 65% rename from packages/kbn-kibana-manifest-parser/index.ts rename to packages/core/status/core-status-common-internal/index.ts index f89c1c8124cce..1d77cb10e166d 100644 --- a/packages/kbn-kibana-manifest-parser/index.ts +++ b/packages/core/status/core-status-common-internal/index.ts @@ -6,9 +6,11 @@ * Side Public License, v 1. */ -export { - parseKibanaManifest, - readKibanaManifest, - validateKibanaManifest, -} from './src/parse_kibana_manifest'; -export type { KibanaPackageManifest } from './src/kibana_manifest'; +export type { + StatusInfoCoreStatus, + StatusInfoServiceStatus, + StatusInfo, + StatusResponse, + ServerVersion, + ServerMetrics, +} from './src'; diff --git a/packages/core/status/core-status-common-internal/jest.config.js b/packages/core/status/core-status-common-internal/jest.config.js new file mode 100644 index 0000000000000..f03233015a797 --- /dev/null +++ b/packages/core/status/core-status-common-internal/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/core/status/core-status-common-internal'], +}; diff --git a/packages/core/status/core-status-common-internal/kibana.jsonc b/packages/core/status/core-status-common-internal/kibana.jsonc new file mode 100644 index 0000000000000..3ce3b2bfbcbdb --- /dev/null +++ b/packages/core/status/core-status-common-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-status-common-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/status/core-status-common-internal/package.json b/packages/core/status/core-status-common-internal/package.json new file mode 100644 index 0000000000000..7d101cfc7c766 --- /dev/null +++ b/packages/core/status/core-status-common-internal/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-status-common-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/status/core-status-common-internal/src/index.ts b/packages/core/status/core-status-common-internal/src/index.ts new file mode 100644 index 0000000000000..4d2ad5cc82485 --- /dev/null +++ b/packages/core/status/core-status-common-internal/src/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 and the Server Side Public License, v 1; you may 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 { + StatusInfo, + StatusInfoCoreStatus, + StatusInfoServiceStatus, + StatusResponse, + ServerVersion, + ServerMetrics, +} from './status'; diff --git a/src/core/types/status.ts b/packages/core/status/core-status-common-internal/src/status.ts similarity index 57% rename from src/core/types/status.ts rename to packages/core/status/core-status-common-internal/src/status.ts index ad5c5b13c9a3a..7c94080671fd1 100644 --- a/src/core/types/status.ts +++ b/packages/core/status/core-status-common-internal/src/status.ts @@ -6,29 +6,19 @@ * Side Public License, v 1. */ -import type { - CoreStatus as CoreStatusFromServer, - ServiceStatus as ServiceStatusFromServer, - ServiceStatusLevel as ServiceStatusLevelFromServer, - OpsMetrics, -} from '../server'; +import type { ServiceStatusLevelId, ServiceStatus, CoreStatus } from '@kbn/core-status-common'; +import type { OpsMetrics } from '@kbn/core-metrics-server'; -/** - * We need this type to convert the object `ServiceStatusLevel` to a union of the possible strings. - * This is because of the "stringification" that occurs when serving HTTP requests. - */ -export type ServiceStatusLevel = ReturnType; - -export interface ServiceStatus extends Omit { - level: ServiceStatusLevel; +export interface StatusInfoServiceStatus extends Omit { + level: ServiceStatusLevelId; } /** * Copy all the services listed in CoreStatus with their specific ServiceStatus declarations * but overwriting the `level` to its stringified version. */ -export type CoreStatus = { - [ServiceName in keyof CoreStatusFromServer]: ServiceStatus; +export type StatusInfoCoreStatus = { + [ServiceName in keyof CoreStatus]: StatusInfoServiceStatus; }; export type ServerMetrics = Omit & { @@ -47,9 +37,9 @@ export interface ServerVersion { } export interface StatusInfo { - overall: ServiceStatus; - core: CoreStatus; - plugins: Record; + overall: StatusInfoServiceStatus; + core: StatusInfoCoreStatus; + plugins: Record; } export interface StatusResponse { diff --git a/packages/kbn-kibana-manifest-parser/tsconfig.json b/packages/core/status/core-status-common-internal/tsconfig.json similarity index 72% rename from packages/kbn-kibana-manifest-parser/tsconfig.json rename to packages/core/status/core-status-common-internal/tsconfig.json index ad1611b40c04e..26b4c7aca3a67 100644 --- a/packages/kbn-kibana-manifest-parser/tsconfig.json +++ b/packages/core/status/core-status-common-internal/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../../tsconfig.bazel.json", + "extends": "../../../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, "declarationMap": true, "emitDeclarationOnly": true, - "allowJs": true, - "checkJs": true, "outDir": "target_types", "stripInternal": false, "types": [ @@ -14,7 +12,7 @@ ] }, "include": [ - "**/*.js", "**/*.ts", + "**/*.tsx", ] } diff --git a/packages/core/status/core-status-common/BUILD.bazel b/packages/core/status/core-status-common/BUILD.bazel new file mode 100644 index 0000000000000..f13519217c685 --- /dev/null +++ b/packages/core/status/core-status-common/BUILD.bazel @@ -0,0 +1,116 @@ +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 = "core-status-common" +PKG_REQUIRE_NAME = "@kbn/core-status-common" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//react", + "//packages/kbn-std", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-std: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, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + 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/core/status/core-status-common/README.md b/packages/core/status/core-status-common/README.md new file mode 100644 index 0000000000000..7a13938f39c10 --- /dev/null +++ b/packages/core/status/core-status-common/README.md @@ -0,0 +1,3 @@ +# @kbn/core-status-common + +This package contains the common public types for Core's `status` domain. diff --git a/packages/core/status/core-status-common/index.ts b/packages/core/status/core-status-common/index.ts new file mode 100644 index 0000000000000..78e04a1aa7936 --- /dev/null +++ b/packages/core/status/core-status-common/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 { ServiceStatusLevels } from './src'; +export type { ServiceStatus, ServiceStatusLevel, ServiceStatusLevelId, CoreStatus } from './src'; diff --git a/packages/core/status/core-status-common/jest.config.js b/packages/core/status/core-status-common/jest.config.js new file mode 100644 index 0000000000000..f03233015a797 --- /dev/null +++ b/packages/core/status/core-status-common/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/status/core-status-common-internal'], +}; diff --git a/packages/core/status/core-status-common/kibana.jsonc b/packages/core/status/core-status-common/kibana.jsonc new file mode 100644 index 0000000000000..13d67c52659ca --- /dev/null +++ b/packages/core/status/core-status-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-status-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/status/core-status-common/package.json b/packages/core/status/core-status-common/package.json new file mode 100644 index 0000000000000..19a21ce0125ec --- /dev/null +++ b/packages/core/status/core-status-common/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-status-common", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/status/core-status-common/src/core_status.ts b/packages/core/status/core-status-common/src/core_status.ts new file mode 100644 index 0000000000000..0d852b7f1e3e4 --- /dev/null +++ b/packages/core/status/core-status-common/src/core_status.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 type { ServiceStatus } from './service_status'; + +/** + * Status of core services. + * + * @internalRemarks + * Only contains entries for backend services that could have a non-available `status`. + * For example, `context` cannot possibly be broken, so it is not included. + * + * @public + */ +export interface CoreStatus { + elasticsearch: ServiceStatus; + savedObjects: ServiceStatus; +} diff --git a/packages/core/status/core-status-common/src/index.ts b/packages/core/status/core-status-common/src/index.ts new file mode 100644 index 0000000000000..64b3cf7733524 --- /dev/null +++ b/packages/core/status/core-status-common/src/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 { ServiceStatusLevels } from './service_status'; +export type { ServiceStatus, ServiceStatusLevel, ServiceStatusLevelId } from './service_status'; +export type { CoreStatus } from './core_status'; diff --git a/packages/core/base/core-base-common/src/service_status.ts b/packages/core/status/core-status-common/src/service_status.ts similarity index 94% rename from packages/core/base/core-base-common/src/service_status.ts rename to packages/core/status/core-status-common/src/service_status.ts index 4c9539f015d86..e976bd16c765d 100644 --- a/packages/core/base/core-base-common/src/service_status.ts +++ b/packages/core/status/core-status-common/src/service_status.ts @@ -39,6 +39,13 @@ export interface ServiceStatus | unknown = unkn meta?: Meta; } +/** + * Possible values for the ID of a {@link ServiceStatusLevel} + * + * @public + */ +export type ServiceStatusLevelId = 'available' | 'degraded' | 'unavailable' | 'critical'; + /** * The current "level" of availability of a service. * diff --git a/packages/kbn-jsonc/tsconfig.json b/packages/core/status/core-status-common/tsconfig.json similarity index 71% rename from packages/kbn-jsonc/tsconfig.json rename to packages/core/status/core-status-common/tsconfig.json index 7057d75ce4952..26b4c7aca3a67 100644 --- a/packages/kbn-jsonc/tsconfig.json +++ b/packages/core/status/core-status-common/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../../tsconfig.bazel.json", + "extends": "../../../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, "declarationMap": true, "emitDeclarationOnly": true, - "allowJs": true, - "checkJs": true, "outDir": "target_types", "stripInternal": false, "types": [ @@ -14,6 +12,7 @@ ] }, "include": [ - "**/*.js" + "**/*.ts", + "**/*.tsx", ] } diff --git a/packages/core/status/core-status-server-internal/BUILD.bazel b/packages/core/status/core-status-server-internal/BUILD.bazel new file mode 100644 index 0000000000000..7b199378c4881 --- /dev/null +++ b/packages/core/status/core-status-server-internal/BUILD.bazel @@ -0,0 +1,134 @@ +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 = "core-status-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-status-server-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//rxjs", + "@npm//lodash", + "//packages/kbn-config-schema", + "//packages/kbn-std", + "//packages/kbn-i18n", + "//packages/core/status/core-status-common", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "@npm//lodash", + "//packages/kbn-config-schema:npm_module_types", + "//packages/kbn-config:npm_module_types", + "//packages/kbn-std:npm_module_types", + "//packages/kbn-logging:npm_module_types", + "//packages/kbn-i18n:npm_module_types", + "//packages/analytics/client:npm_module_types", + "//packages/core/base/core-base-common:npm_module_types", + "//packages/core/base/core-base-server-internal:npm_module_types", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/http/core-http-server-internal:npm_module_types", + "//packages/core/metrics/core-metrics-server:npm_module_types", + "//packages/core/metrics/core-metrics-server-internal:npm_module_types", + "//packages/core/usage-data/core-usage-data-server:npm_module_types", + "//packages/core/usage-data/core-usage-data-server-internal:npm_module_types", + "//packages/core/analytics/core-analytics-server:npm_module_types", + "//packages/core/environment/core-environment-server-internal:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-server-internal:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-server-internal:npm_module_types", + "//packages/core/status/core-status-server:npm_module_types", + "//packages/core/status/core-status-common:npm_module_types", + "//packages/core/status/core-status-common-internal: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, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + 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/core/status/core-status-server-internal/README.md b/packages/core/status/core-status-server-internal/README.md new file mode 100644 index 0000000000000..cdc068f6bead1 --- /dev/null +++ b/packages/core/status/core-status-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-status-server-internal + +This package contains the internal types and implementation for Core's server-side `status` service. diff --git a/packages/core/status/core-status-server-internal/index.ts b/packages/core/status/core-status-server-internal/index.ts new file mode 100644 index 0000000000000..19876f99f4a86 --- /dev/null +++ b/packages/core/status/core-status-server-internal/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { statusConfig, StatusService } from './src'; +export type { StatusConfigType, InternalStatusServiceSetup, StatusServiceSetupDeps } from './src'; + +// exported only for integration tests +export { registerStatusRoute } from './src/routes/status'; diff --git a/packages/core/status/core-status-server-internal/jest.config.js b/packages/core/status/core-status-server-internal/jest.config.js new file mode 100644 index 0000000000000..2bb0f4715da10 --- /dev/null +++ b/packages/core/status/core-status-server-internal/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/core/status/core-status-server-internal'], +}; diff --git a/packages/core/status/core-status-server-internal/kibana.jsonc b/packages/core/status/core-status-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..9ed3627ce73ed --- /dev/null +++ b/packages/core/status/core-status-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-status-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/status/core-status-server-internal/package.json b/packages/core/status/core-status-server-internal/package.json new file mode 100644 index 0000000000000..495d8c56d3815 --- /dev/null +++ b/packages/core/status/core-status-server-internal/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-status-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/status/cached_plugins_status.ts b/packages/core/status/core-status-server-internal/src/cached_plugins_status.ts similarity index 94% rename from src/core/server/status/cached_plugins_status.ts rename to packages/core/status/core-status-server-internal/src/cached_plugins_status.ts index 4f574a6382106..20cc30d83176e 100644 --- a/src/core/server/status/cached_plugins_status.ts +++ b/packages/core/status/core-status-server-internal/src/cached_plugins_status.ts @@ -6,10 +6,9 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; - +import type { Observable } from 'rxjs'; import type { PluginName } from '@kbn/core-base-common'; -import { type ServiceStatus } from './types'; +import type { ServiceStatus } from '@kbn/core-status-common'; import { type Deps, PluginsStatusService as BasePluginsStatusService } from './plugins_status'; diff --git a/src/core/server/status/get_summary_status.test.ts b/packages/core/status/core-status-server-internal/src/get_summary_status.test.ts similarity index 98% rename from src/core/server/status/get_summary_status.test.ts rename to packages/core/status/core-status-server-internal/src/get_summary_status.test.ts index 2c91aa8c7b16a..2bb692bfc311b 100644 --- a/src/core/server/status/get_summary_status.test.ts +++ b/packages/core/status/core-status-server-internal/src/get_summary_status.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ServiceStatus, ServiceStatusLevels } from './types'; +import { ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { getSummaryStatus } from './get_summary_status'; describe('getSummaryStatus', () => { diff --git a/src/core/server/status/get_summary_status.ts b/packages/core/status/core-status-server-internal/src/get_summary_status.ts similarity index 95% rename from src/core/server/status/get_summary_status.ts rename to packages/core/status/core-status-server-internal/src/get_summary_status.ts index 1dc939ce3f80c..083364fad2fd2 100644 --- a/src/core/server/status/get_summary_status.ts +++ b/packages/core/status/core-status-server-internal/src/get_summary_status.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from './types'; +import { + ServiceStatusLevels, + type ServiceStatus, + type ServiceStatusLevel, +} from '@kbn/core-status-common'; /** * Returns a single {@link ServiceStatus} that summarizes the most severe status level from a group of statuses. diff --git a/packages/core/status/core-status-server-internal/src/index.ts b/packages/core/status/core-status-server-internal/src/index.ts new file mode 100644 index 0000000000000..0604754f5c1de --- /dev/null +++ b/packages/core/status/core-status-server-internal/src/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { StatusService } from './status_service'; +export type { StatusServiceSetupDeps } from './status_service'; +export { statusConfig } from './status_config'; +export type { StatusConfigType } from './status_config'; +export type { InternalStatusServiceSetup } from './types'; diff --git a/src/core/server/status/legacy_status.test.ts b/packages/core/status/core-status-server-internal/src/legacy_status.test.ts similarity index 97% rename from src/core/server/status/legacy_status.test.ts rename to packages/core/status/core-status-server-internal/src/legacy_status.test.ts index 61c356e514e5f..f20a571786430 100644 --- a/src/core/server/status/legacy_status.test.ts +++ b/packages/core/status/core-status-server-internal/src/legacy_status.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ServiceStatus, ServiceStatusLevels } from './types'; +import { ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { calculateLegacyStatus } from './legacy_status'; const available: ServiceStatus = { level: ServiceStatusLevels.available, summary: 'Available' }; diff --git a/src/core/server/status/legacy_status.ts b/packages/core/status/core-status-server-internal/src/legacy_status.ts similarity index 97% rename from src/core/server/status/legacy_status.ts rename to packages/core/status/core-status-server-internal/src/legacy_status.ts index b9ae0461d91d7..f154374d5470e 100644 --- a/src/core/server/status/legacy_status.ts +++ b/packages/core/status/core-status-server-internal/src/legacy_status.ts @@ -9,9 +9,8 @@ import { pick } from 'lodash'; import { i18n } from '@kbn/i18n'; import { deepFreeze } from '@kbn/std'; - import type { PluginName } from '@kbn/core-base-common'; -import { ServiceStatusLevels, ServiceStatus, CoreStatus } from './types'; +import { ServiceStatusLevels, type ServiceStatus, type CoreStatus } from '@kbn/core-status-common'; interface Deps { overall: ServiceStatus; diff --git a/src/core/server/status/log_overall_status.test.ts b/packages/core/status/core-status-server-internal/src/log_overall_status.test.ts similarity index 97% rename from src/core/server/status/log_overall_status.test.ts rename to packages/core/status/core-status-server-internal/src/log_overall_status.test.ts index b656b16a9e00b..f74fb472d08f6 100644 --- a/src/core/server/status/log_overall_status.test.ts +++ b/packages/core/status/core-status-server-internal/src/log_overall_status.test.ts @@ -7,7 +7,7 @@ */ import { TestScheduler } from 'rxjs/testing'; -import { ServiceStatus, ServiceStatusLevels } from './types'; +import { ServiceStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { getOverallStatusChanges } from './log_overall_status'; const getTestScheduler = () => diff --git a/src/core/server/status/log_overall_status.ts b/packages/core/status/core-status-server-internal/src/log_overall_status.ts similarity index 94% rename from src/core/server/status/log_overall_status.ts rename to packages/core/status/core-status-server-internal/src/log_overall_status.ts index c77f634046be1..e69fad24c12e0 100644 --- a/src/core/server/status/log_overall_status.ts +++ b/packages/core/status/core-status-server-internal/src/log_overall_status.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; import { distinctUntilChanged, pairwise, startWith, takeUntil, map } from 'rxjs/operators'; -import { ServiceStatus } from './types'; +import type { ServiceStatus } from '@kbn/core-status-common'; export const getOverallStatusChanges = ( overall$: Observable, diff --git a/src/core/server/status/plugins_status.test.ts b/packages/core/status/core-status-server-internal/src/plugins_status.test.ts similarity index 99% rename from src/core/server/status/plugins_status.test.ts rename to packages/core/status/core-status-server-internal/src/plugins_status.test.ts index eaab8841563b5..b84151fb9da83 100644 --- a/src/core/server/status/plugins_status.test.ts +++ b/packages/core/status/core-status-server-internal/src/plugins_status.test.ts @@ -9,9 +9,9 @@ import type { PluginName } from '@kbn/core-base-common'; import { PluginsStatusService } from './plugins_status'; import { of, Observable, BehaviorSubject, ReplaySubject } from 'rxjs'; -import { ServiceStatusLevels, CoreStatus, ServiceStatus } from './types'; +import { ServiceStatusLevels, CoreStatus, ServiceStatus } from '@kbn/core-status-common'; import { first, skip } from 'rxjs/operators'; -import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; +import { ServiceStatusLevelSnapshotSerializer } from './test_helpers'; expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); diff --git a/src/core/server/status/plugins_status.ts b/packages/core/status/core-status-server-internal/src/plugins_status.ts similarity index 99% rename from src/core/server/status/plugins_status.ts rename to packages/core/status/core-status-server-internal/src/plugins_status.ts index a3e5294157780..58272636bfe85 100644 --- a/src/core/server/status/plugins_status.ts +++ b/packages/core/status/core-status-server-internal/src/plugins_status.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 { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs'; import { map, @@ -16,9 +17,8 @@ import { } from 'rxjs/operators'; import { sortBy } from 'lodash'; import { isDeepStrictEqual } from 'util'; - import type { PluginName } from '@kbn/core-base-common'; -import { type ServiceStatus, type CoreStatus, ServiceStatusLevels } from './types'; +import { ServiceStatusLevels, type CoreStatus, type ServiceStatus } from '@kbn/core-status-common'; import { getSummaryStatus } from './get_summary_status'; const STATUS_TIMEOUT_MS = 30 * 1000; // 30 seconds @@ -43,6 +43,7 @@ interface PluginData { derivedStatus: ServiceStatus; }; } + interface PluginStatus { [name: PluginName]: ServiceStatus; } diff --git a/src/core/server/status/routes/index.ts b/packages/core/status/core-status-server-internal/src/routes/index.ts similarity index 100% rename from src/core/server/status/routes/index.ts rename to packages/core/status/core-status-server-internal/src/routes/index.ts diff --git a/src/core/server/status/routes/status.ts b/packages/core/status/core-status-server-internal/src/routes/status.ts similarity index 97% rename from src/core/server/status/routes/status.ts rename to packages/core/status/core-status-server-internal/src/routes/status.ts index 5033f2e9beace..34a5a9b4dcd20 100644 --- a/src/core/server/status/routes/status.ts +++ b/packages/core/status/core-status-server-internal/src/routes/status.ts @@ -8,15 +8,14 @@ import { Observable, combineLatest, ReplaySubject, firstValueFrom } from 'rxjs'; import { schema } from '@kbn/config-schema'; -import { PackageInfo } from '@kbn/config'; - +import type { PackageInfo } from '@kbn/config'; import type { PluginName } from '@kbn/core-base-common'; import type { IRouter } from '@kbn/core-http-server'; import type { MetricsServiceSetup } from '@kbn/core-metrics-server'; import type { CoreIncrementUsageCounter } from '@kbn/core-usage-data-server'; -import { ServiceStatus, CoreStatus, ServiceStatusLevels } from '../types'; +import type { StatusResponse } from '@kbn/core-status-common-internal'; +import { ServiceStatus, CoreStatus, ServiceStatusLevels } from '@kbn/core-status-common'; import { calculateLegacyStatus, LegacyStatusInfo } from '../legacy_status'; -import { StatusResponse } from '../../../types/status'; const SNAPSHOT_POSTFIX = /-SNAPSHOT$/; diff --git a/src/core/server/status/status_config.ts b/packages/core/status/core-status-server-internal/src/status_config.ts similarity index 90% rename from src/core/server/status/status_config.ts rename to packages/core/status/core-status-server-internal/src/status_config.ts index 09e987a4e8d37..fd08fb1ad8b25 100644 --- a/src/core/server/status/status_config.ts +++ b/packages/core/status/core-status-server-internal/src/status_config.ts @@ -15,7 +15,7 @@ const statusConfigSchema = schema.object({ export type StatusConfigType = TypeOf; -export const config: ServiceConfigDescriptor = { +export const statusConfig: ServiceConfigDescriptor = { path: 'status', schema: statusConfigSchema, }; diff --git a/src/core/server/status/status_service.test.ts b/packages/core/status/core-status-server-internal/src/status_service.test.ts similarity index 97% rename from src/core/server/status/status_service.test.ts rename to packages/core/status/core-status-server-internal/src/status_service.test.ts index a3ca69ca4f243..cbd811ebdb72b 100644 --- a/src/core/server/status/status_service.test.ts +++ b/packages/core/status/core-status-server-internal/src/status_service.test.ts @@ -8,24 +8,20 @@ import { of, BehaviorSubject, firstValueFrom } from 'rxjs'; -import { - ServiceStatus, - ServiceStatusLevels, - CoreStatus, - InternalStatusServiceSetup, -} from './types'; -import { StatusService } from './status_service'; +import { ServiceStatus, ServiceStatusLevels, CoreStatus } from '@kbn/core-status-common'; +import { InternalStatusServiceSetup } from './types'; +import { StatusService, StatusServiceSetupDeps } from './status_service'; import { first, take, toArray } from 'rxjs/operators'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { environmentServiceMock } from '@kbn/core-environment-server-mocks'; import { mockRouter, RouterMock } from '@kbn/core-http-router-server-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; -import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; +import { ServiceStatusLevelSnapshotSerializer } from './test_helpers'; import { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; import { configServiceMock } from '@kbn/config-mocks'; import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { analyticsServiceMock } from '@kbn/core-analytics-server-mocks'; -import { AnalyticsServiceSetup } from '..'; +import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); @@ -51,8 +47,7 @@ describe('StatusService', () => { summary: 'This is critical!', }; - type SetupDeps = Parameters[0]; - const setupDeps = (overrides: Partial): SetupDeps => { + const setupDeps = (overrides: Partial): StatusServiceSetupDeps => { return { analytics: analyticsServiceMock.createAnalyticsServiceSetup(), elasticsearch: { diff --git a/src/core/server/status/status_service.ts b/packages/core/status/core-status-server-internal/src/status_service.ts similarity index 95% rename from src/core/server/status/status_service.ts rename to packages/core/status/core-status-server-internal/src/status_service.ts index 8d036177c6e06..4eb96e73e1b1d 100644 --- a/src/core/server/status/status_service.ts +++ b/packages/core/status/core-status-server-internal/src/status_service.ts @@ -19,7 +19,7 @@ import { map, distinctUntilChanged, shareReplay, debounceTime, takeUntil } from import { isDeepStrictEqual } from 'util'; import type { RootSchema } from '@kbn/analytics-client'; -import { Logger, LogMeta } from '@kbn/logging'; +import type { Logger, LogMeta } from '@kbn/logging'; import type { CoreContext, CoreService } from '@kbn/core-base-server-internal'; import type { PluginName } from '@kbn/core-base-common'; import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; @@ -29,10 +29,11 @@ import type { InternalElasticsearchServiceSetup } from '@kbn/core-elasticsearch- import type { InternalMetricsServiceSetup } from '@kbn/core-metrics-server-internal'; import type { InternalSavedObjectsServiceSetup } from '@kbn/core-saved-objects-server-internal'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; +import type { ServiceStatus, CoreStatus } from '@kbn/core-status-common'; import { registerStatusRoute } from './routes'; -import { config, StatusConfigType } from './status_config'; -import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types'; +import { statusConfig as config, StatusConfigType } from './status_config'; +import type { InternalStatusServiceSetup } from './types'; import { getSummaryStatus } from './get_summary_status'; import { PluginsStatusService } from './cached_plugins_status'; import { getOverallStatusChanges } from './log_overall_status'; @@ -46,7 +47,7 @@ interface StatusAnalyticsPayload { overall_status_summary: string; } -export interface SetupDeps { +export interface StatusServiceSetupDeps { analytics: AnalyticsServiceSetup; elasticsearch: Pick; environment: InternalEnvironmentServiceSetup; @@ -80,7 +81,7 @@ export class StatusService implements CoreService { savedObjects, environment, coreUsageData, - }: SetupDeps) { + }: StatusServiceSetupDeps) { const statusConfig = await firstValueFrom(this.config$); const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); @@ -200,7 +201,7 @@ export class StatusService implements CoreService { private setupCoreStatus({ elasticsearch, savedObjects, - }: Pick): Observable { + }: Pick): Observable { return combineLatest([elasticsearch.status$, savedObjects.status$]).pipe( map(([elasticsearchStatus, savedObjectsStatus]) => ({ elasticsearch: elasticsearchStatus, diff --git a/src/core/server/status/index.ts b/packages/core/status/core-status-server-internal/src/test_helpers/index.ts similarity index 75% rename from src/core/server/status/index.ts rename to packages/core/status/core-status-server-internal/src/test_helpers/index.ts index beb20d57b8582..27bc0a877cce9 100644 --- a/src/core/server/status/index.ts +++ b/packages/core/status/core-status-server-internal/src/test_helpers/index.ts @@ -6,6 +6,4 @@ * Side Public License, v 1. */ -export { StatusService } from './status_service'; -export { config } from './status_config'; -export * from './types'; +export { ServiceStatusLevelSnapshotSerializer } from './test_utils'; diff --git a/src/core/server/status/test_utils.ts b/packages/core/status/core-status-server-internal/src/test_helpers/test_utils.ts similarity index 87% rename from src/core/server/status/test_utils.ts rename to packages/core/status/core-status-server-internal/src/test_helpers/test_utils.ts index e75ce9412de74..4b46ac23f5c8d 100644 --- a/src/core/server/status/test_utils.ts +++ b/packages/core/status/core-status-server-internal/src/test_helpers/test_utils.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ServiceStatusLevels, ServiceStatusLevel } from './types'; +import { ServiceStatusLevels, ServiceStatusLevel } from '@kbn/core-status-common'; export const ServiceStatusLevelSnapshotSerializer: jest.SnapshotSerializerPlugin = { test: (val: any) => Object.values(ServiceStatusLevels).includes(val), diff --git a/packages/core/status/core-status-server-internal/src/types.ts b/packages/core/status/core-status-server-internal/src/types.ts new file mode 100644 index 0000000000000..520a313bc9786 --- /dev/null +++ b/packages/core/status/core-status-server-internal/src/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Observable } from 'rxjs'; +import type { PluginName } from '@kbn/core-base-common'; +import type { ServiceStatus } from '@kbn/core-status-common'; +import type { StatusServiceSetup } from '@kbn/core-status-server'; + +/** @internal */ +export interface InternalStatusServiceSetup + extends Pick { + /** + * Overall status of core's service. + */ + coreOverall$: Observable; + + // Namespaced under `plugins` key to improve clarity that these are APIs for plugins specifically. + plugins: { + set(plugin: PluginName, status$: Observable): void; + getDependenciesStatus$(plugin: PluginName): Observable>; + getDerivedStatus$(plugin: PluginName): Observable; + }; +} diff --git a/packages/core/status/core-status-server-internal/tsconfig.json b/packages/core/status/core-status-server-internal/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/status/core-status-server-internal/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/status/core-status-server-mocks/BUILD.bazel b/packages/core/status/core-status-server-mocks/BUILD.bazel new file mode 100644 index 0000000000000..54472c706546b --- /dev/null +++ b/packages/core/status/core-status-server-mocks/BUILD.bazel @@ -0,0 +1,110 @@ +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 = "core-status-server-mocks" +PKG_REQUIRE_NAME = "@kbn/core-status-server-mocks" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//rxjs", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/kbn-utility-types:npm_module_types", + "//packages/core/base/core-base-common:npm_module_types", + "//packages/core/status/core-status-server:npm_module_types", + "//packages/core/status/core-status-server-internal: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, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + 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/core/status/core-status-server-mocks/README.md b/packages/core/status/core-status-server-mocks/README.md new file mode 100644 index 0000000000000..1505430a6c719 --- /dev/null +++ b/packages/core/status/core-status-server-mocks/README.md @@ -0,0 +1,4 @@ +# @kbn/core-status-server-mocks + +This package contains mocks types for Core's server-side `status` service. +- `statusServiceMock` \ No newline at end of file diff --git a/packages/core/status/core-status-server-mocks/index.ts b/packages/core/status/core-status-server-mocks/index.ts new file mode 100644 index 0000000000000..581214f5ddb0c --- /dev/null +++ b/packages/core/status/core-status-server-mocks/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 { statusServiceMock } from './src'; diff --git a/packages/kbn-kibana-manifest-parser/jest.config.js b/packages/core/status/core-status-server-mocks/jest.config.js similarity index 81% rename from packages/kbn-kibana-manifest-parser/jest.config.js rename to packages/core/status/core-status-server-mocks/jest.config.js index f50552e3e36e5..02beffdf16678 100644 --- a/packages/kbn-kibana-manifest-parser/jest.config.js +++ b/packages/core/status/core-status-server-mocks/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-kibana-manifest-parser'], + rootDir: '../../../..', + roots: ['/packages/core/status/core-status-server-mocks'], }; diff --git a/packages/core/status/core-status-server-mocks/kibana.jsonc b/packages/core/status/core-status-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..04f0e29eedf72 --- /dev/null +++ b/packages/core/status/core-status-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-status-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/status/core-status-server-mocks/package.json b/packages/core/status/core-status-server-mocks/package.json new file mode 100644 index 0000000000000..406cc5eae71aa --- /dev/null +++ b/packages/core/status/core-status-server-mocks/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-status-server-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/status/core-status-server-mocks/src/index.ts b/packages/core/status/core-status-server-mocks/src/index.ts new file mode 100644 index 0000000000000..1b2913d19aa50 --- /dev/null +++ b/packages/core/status/core-status-server-mocks/src/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 { statusServiceMock } from './status_service.mock'; diff --git a/src/core/server/status/status_service.mock.ts b/packages/core/status/core-status-server-mocks/src/status_service.mock.ts similarity index 88% rename from src/core/server/status/status_service.mock.ts rename to packages/core/status/core-status-server-mocks/src/status_service.mock.ts index 7241bb2f0479e..824b853b342fc 100644 --- a/src/core/server/status/status_service.mock.ts +++ b/packages/core/status/core-status-server-mocks/src/status_service.mock.ts @@ -6,16 +6,11 @@ * Side Public License, v 1. */ -import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { StatusService } from './status_service'; -import { - InternalStatusServiceSetup, - StatusServiceSetup, - ServiceStatusLevels, - ServiceStatus, - CoreStatus, -} from './types'; import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import { ServiceStatusLevels, ServiceStatus } from '@kbn/core-status-common'; +import type { StatusService, InternalStatusServiceSetup } from '@kbn/core-status-server-internal'; +import type { StatusServiceSetup, CoreStatus } from '@kbn/core-status-server'; const available: ServiceStatus = { level: ServiceStatusLevels.available, diff --git a/packages/core/status/core-status-server-mocks/tsconfig.json b/packages/core/status/core-status-server-mocks/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/status/core-status-server-mocks/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/status/core-status-server/BUILD.bazel b/packages/core/status/core-status-server/BUILD.bazel new file mode 100644 index 0000000000000..e48b21c23dc2f --- /dev/null +++ b/packages/core/status/core-status-server/BUILD.bazel @@ -0,0 +1,108 @@ +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 = "core-status-server" +PKG_REQUIRE_NAME = "@kbn/core-status-server" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/core/status/core-status-common", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/core/status/core-status-common: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, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + 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/core/status/core-status-server/README.md b/packages/core/status/core-status-server/README.md new file mode 100644 index 0000000000000..056512bf03080 --- /dev/null +++ b/packages/core/status/core-status-server/README.md @@ -0,0 +1,3 @@ +# @kbn/core-status-server + +This package contains the public types for Core's server-side `status` service. diff --git a/packages/core/status/core-status-server/index.ts b/packages/core/status/core-status-server/index.ts new file mode 100644 index 0000000000000..69e36d4f503c0 --- /dev/null +++ b/packages/core/status/core-status-server/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 and the Server Side Public License, v 1; you may 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 { + ServiceStatusLevel, + ServiceStatus, + CoreStatus, + ServiceStatusLevelId, +} from '@kbn/core-status-common'; +export { ServiceStatusLevels } from '@kbn/core-status-common'; +export type { StatusServiceSetup } from './src'; diff --git a/packages/kbn-jsonc/jest.config.js b/packages/core/status/core-status-server/jest.config.js similarity index 82% rename from packages/kbn-jsonc/jest.config.js rename to packages/core/status/core-status-server/jest.config.js index c958647e1c154..ca1edb6516461 100644 --- a/packages/kbn-jsonc/jest.config.js +++ b/packages/core/status/core-status-server/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-jsonc'], + rootDir: '../../../..', + roots: ['/packages/core/status/core-status-server'], }; diff --git a/packages/core/status/core-status-server/kibana.jsonc b/packages/core/status/core-status-server/kibana.jsonc new file mode 100644 index 0000000000000..3021a52ce4c17 --- /dev/null +++ b/packages/core/status/core-status-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-status-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-kibana-manifest-parser/package.json b/packages/core/status/core-status-server/package.json similarity index 66% rename from packages/kbn-kibana-manifest-parser/package.json rename to packages/core/status/core-status-server/package.json index e2ab7ac2417e9..93dd8920d03a7 100644 --- a/packages/kbn-kibana-manifest-parser/package.json +++ b/packages/core/status/core-status-server/package.json @@ -1,7 +1,8 @@ { - "name": "@kbn/kibana-manifest-parser", + "name": "@kbn/core-status-server", "private": true, "version": "1.0.0", "main": "./target_node/index.js", + "author": "Kibana Core", "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/src/core/server/status/types.ts b/packages/core/status/core-status-server/src/contracts.ts similarity index 78% rename from src/core/server/status/types.ts rename to packages/core/status/core-status-server/src/contracts.ts index 8217b29463700..9e4b261305950 100644 --- a/src/core/server/status/types.ts +++ b/packages/core/status/core-status-server/src/contracts.ts @@ -6,25 +6,8 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; -import type { PluginName, ServiceStatus } from '@kbn/core-base-common'; - -export type { ServiceStatusLevel, ServiceStatus } from '@kbn/core-base-common'; -export { ServiceStatusLevels } from '@kbn/core-base-common'; - -/** - * Status of core services. - * - * @internalRemarks - * Only contains entries for backend services that could have a non-available `status`. - * For example, `context` cannot possibly be broken, so it is not included. - * - * @public - */ -export interface CoreStatus { - elasticsearch: ServiceStatus; - savedObjects: ServiceStatus; -} +import type { Observable } from 'rxjs'; +import type { ServiceStatus, CoreStatus } from '@kbn/core-status-common'; /** * API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. @@ -138,19 +121,3 @@ export interface StatusServiceSetup { */ isStatusPageAnonymous: () => boolean; } - -/** @internal */ -export interface InternalStatusServiceSetup - extends Pick { - /** - * Overall status of core's service. - */ - coreOverall$: Observable; - - // Namespaced under `plugins` key to improve clarity that these are APIs for plugins specifically. - plugins: { - set(plugin: PluginName, status$: Observable): void; - getDependenciesStatus$(plugin: PluginName): Observable>; - getDerivedStatus$(plugin: PluginName): Observable; - }; -} diff --git a/packages/core/status/core-status-server/src/index.ts b/packages/core/status/core-status-server/src/index.ts new file mode 100644 index 0000000000000..53c4a1d5e8c98 --- /dev/null +++ b/packages/core/status/core-status-server/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { StatusServiceSetup } from './contracts'; diff --git a/packages/core/status/core-status-server/tsconfig.json b/packages/core/status/core-status-server/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/status/core-status-server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/test-helpers/core-test-helpers-deprecations-getters/kibana.jsonc b/packages/core/test-helpers/core-test-helpers-deprecations-getters/kibana.jsonc new file mode 100644 index 0000000000000..1c245768d3f7d --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-deprecations-getters/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-test-helpers-deprecations-getters", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/test-helpers/core-test-helpers-http-setup-browser/kibana.jsonc b/packages/core/test-helpers/core-test-helpers-http-setup-browser/kibana.jsonc new file mode 100644 index 0000000000000..f5e257dd883a5 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-http-setup-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-test-helpers-http-setup-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/theme/core-theme-browser-internal/kibana.jsonc b/packages/core/theme/core-theme-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..36842b069548b --- /dev/null +++ b/packages/core/theme/core-theme-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-theme-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/theme/core-theme-browser-mocks/kibana.jsonc b/packages/core/theme/core-theme-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..e46f0193c4068 --- /dev/null +++ b/packages/core/theme/core-theme-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-theme-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/theme/core-theme-browser/kibana.jsonc b/packages/core/theme/core-theme-browser/kibana.jsonc new file mode 100644 index 0000000000000..9dbe039d70640 --- /dev/null +++ b/packages/core/theme/core-theme-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-theme-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/ui-settings/core-ui-settings-browser-internal/kibana.jsonc b/packages/core/ui-settings/core-ui-settings-browser-internal/kibana.jsonc new file mode 100644 index 0000000000000..9a46e97ec89af --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-browser-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-ui-settings-browser-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/ui-settings/core-ui-settings-browser-mocks/kibana.jsonc b/packages/core/ui-settings/core-ui-settings-browser-mocks/kibana.jsonc new file mode 100644 index 0000000000000..f6906835b648f --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-browser-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-ui-settings-browser-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/ui-settings/core-ui-settings-browser/kibana.jsonc b/packages/core/ui-settings/core-ui-settings-browser/kibana.jsonc new file mode 100644 index 0000000000000..9129ef435fb67 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-ui-settings-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/ui-settings/core-ui-settings-common/kibana.jsonc b/packages/core/ui-settings/core-ui-settings-common/kibana.jsonc new file mode 100644 index 0000000000000..4d9b575423696 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-common/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-ui-settings-common", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/usage-data/core-usage-data-base-server-internal/kibana.jsonc b/packages/core/usage-data/core-usage-data-base-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..d35d6c2bbd6d7 --- /dev/null +++ b/packages/core/usage-data/core-usage-data-base-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-usage-data-base-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/usage-data/core-usage-data-server-internal/kibana.jsonc b/packages/core/usage-data/core-usage-data-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..30bf6865b5bb4 --- /dev/null +++ b/packages/core/usage-data/core-usage-data-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-usage-data-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/usage-data/core-usage-data-server-mocks/kibana.jsonc b/packages/core/usage-data/core-usage-data-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..f12bd25bee867 --- /dev/null +++ b/packages/core/usage-data/core-usage-data-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-usage-data-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/core/usage-data/core-usage-data-server/kibana.jsonc b/packages/core/usage-data/core-usage-data-server/kibana.jsonc new file mode 100644 index 0000000000000..a785db8090713 --- /dev/null +++ b/packages/core/usage-data/core-usage-data-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-usage-data-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/home/sample_data_card/kibana.jsonc b/packages/home/sample_data_card/kibana.jsonc new file mode 100644 index 0000000000000..2dd9813151b2c --- /dev/null +++ b/packages/home/sample_data_card/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/home-sample-data-card", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/home/sample_data_tab/kibana.jsonc b/packages/home/sample_data_tab/kibana.jsonc new file mode 100644 index 0000000000000..d734b947444a9 --- /dev/null +++ b/packages/home/sample_data_tab/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/home-sample-data-tab", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/home/sample_data_types/kibana.jsonc b/packages/home/sample_data_types/kibana.jsonc new file mode 100644 index 0000000000000..9b7458fe54946 --- /dev/null +++ b/packages/home/sample_data_types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/home-sample-data-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ace/kibana.jsonc b/packages/kbn-ace/kibana.jsonc new file mode 100644 index 0000000000000..25da4fe177ee2 --- /dev/null +++ b/packages/kbn-ace/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ace", + "owner": "@elastic/platform-deployment-management", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-alerts/kibana.jsonc b/packages/kbn-alerts/kibana.jsonc new file mode 100644 index 0000000000000..93b42c4ef86bf --- /dev/null +++ b/packages/kbn-alerts/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/alerts", + "owner": "@elastic/security-solution", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ambient-storybook-types/kibana.jsonc b/packages/kbn-ambient-storybook-types/kibana.jsonc new file mode 100644 index 0000000000000..d04a5a93e2960 --- /dev/null +++ b/packages/kbn-ambient-storybook-types/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/ambient-storybook-types", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ambient-storybook-types/package.json b/packages/kbn-ambient-storybook-types/package.json index 301d28d5e585c..9a29b8f60ccd1 100644 --- a/packages/kbn-ambient-storybook-types/package.json +++ b/packages/kbn-ambient-storybook-types/package.json @@ -2,8 +2,5 @@ "name": "@kbn/ambient-storybook-types", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-ambient-ui-types/kibana.jsonc b/packages/kbn-ambient-ui-types/kibana.jsonc new file mode 100644 index 0000000000000..1837bcfbd5619 --- /dev/null +++ b/packages/kbn-ambient-ui-types/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/ambient-ui-types", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ambient-ui-types/package.json b/packages/kbn-ambient-ui-types/package.json index ddeb5082cb41f..9ff278bf1dbe2 100644 --- a/packages/kbn-ambient-ui-types/package.json +++ b/packages/kbn-ambient-ui-types/package.json @@ -2,8 +2,5 @@ "name": "@kbn/ambient-ui-types", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } \ No newline at end of file diff --git a/packages/kbn-analytics/kibana.jsonc b/packages/kbn-analytics/kibana.jsonc new file mode 100644 index 0000000000000..06320d851340b --- /dev/null +++ b/packages/kbn-analytics/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/analytics", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-apm-config-loader/kibana.jsonc b/packages/kbn-apm-config-loader/kibana.jsonc new file mode 100644 index 0000000000000..d817476c0b6db --- /dev/null +++ b/packages/kbn-apm-config-loader/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/apm-config-loader", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-apm-synthtrace/kibana.jsonc b/packages/kbn-apm-synthtrace/kibana.jsonc new file mode 100644 index 0000000000000..0bde4f9d7715a --- /dev/null +++ b/packages/kbn-apm-synthtrace/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/apm-synthtrace", + "devOnly": true, + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-apm-utils/kibana.jsonc b/packages/kbn-apm-utils/kibana.jsonc new file mode 100644 index 0000000000000..3db7022ea44c5 --- /dev/null +++ b/packages/kbn-apm-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/apm-utils", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-axe-config/kibana.jsonc b/packages/kbn-axe-config/kibana.jsonc new file mode 100644 index 0000000000000..f2444755f9096 --- /dev/null +++ b/packages/kbn-axe-config/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/axe-config", + "devOnly": true, + "owner": "@elastic/kibana-qa", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-axe-config/package.json b/packages/kbn-axe-config/package.json index c5b929478c3de..62dd325c3ca2f 100644 --- a/packages/kbn-axe-config/package.json +++ b/packages/kbn-axe-config/package.json @@ -4,8 +4,5 @@ "version": "1.0.0", "main": "./target_node/index.js", "browser": "./target_web/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-babel-plugin-synthetic-packages/kibana.jsonc b/packages/kbn-babel-plugin-synthetic-packages/kibana.jsonc new file mode 100644 index 0000000000000..a426d7bec6a2b --- /dev/null +++ b/packages/kbn-babel-plugin-synthetic-packages/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/babel-plugin-synthetic-packages", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-babel-plugin-synthetic-packages/package.json b/packages/kbn-babel-plugin-synthetic-packages/package.json index f488064fc6100..89de157a11724 100644 --- a/packages/kbn-babel-plugin-synthetic-packages/package.json +++ b/packages/kbn-babel-plugin-synthetic-packages/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./babel_plugin_synthetic_packages.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-babel-preset/kibana.jsonc b/packages/kbn-babel-preset/kibana.jsonc new file mode 100644 index 0000000000000..fa4ca725c56de --- /dev/null +++ b/packages/kbn-babel-preset/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/babel-preset", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index e0b90d9e41ccd..1ff04ec1df122 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -2,8 +2,5 @@ "name": "@kbn/babel-preset", "version": "1.0.0", "private": true, - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-bazel-packages/index.js b/packages/kbn-bazel-packages/index.js index da3e620e14ef9..5849bd151c8ea 100644 --- a/packages/kbn-bazel-packages/index.js +++ b/packages/kbn-bazel-packages/index.js @@ -7,13 +7,26 @@ */ /** @typedef {import('./src/bazel_package').BazelPackage} BazelPackage */ +/** @typedef {import('./src/types').KibanaPackageManifest} KibanaPackageManifest */ +/** @typedef {import('./src/types').KibanaPackageType} KibanaPackageType */ +/** @typedef {import('./src/types').ParsedPackageJson} ParsedPackageJson */ const { BAZEL_PACKAGE_DIRS, getAllBazelPackageDirs } = require('./src/bazel_package_dirs'); -const { discoverBazelPackageLocations, discoverBazelPackages } = require('./src/discover_packages'); +const { discoverPackageManifestPaths, discoverBazelPackages } = require('./src/discover_packages'); +const { + parsePackageManifest, + readPackageManifest, + validatePackageManifest, +} = require('./src/parse_package_manifest'); +const Jsonc = require('./src/jsonc'); module.exports = { BAZEL_PACKAGE_DIRS, getAllBazelPackageDirs, - discoverBazelPackageLocations, + discoverPackageManifestPaths, discoverBazelPackages, + parsePackageManifest, + readPackageManifest, + validatePackageManifest, + Jsonc, }; diff --git a/packages/kbn-bazel-packages/kibana.jsonc b/packages/kbn-bazel-packages/kibana.jsonc new file mode 100644 index 0000000000000..fc373ccad73ad --- /dev/null +++ b/packages/kbn-bazel-packages/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/bazel-packages", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-bazel-packages/package.json b/packages/kbn-bazel-packages/package.json index 085f0bb4510f0..fabf8b6cbbc14 100644 --- a/packages/kbn-bazel-packages/package.json +++ b/packages/kbn-bazel-packages/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-bazel-packages/src/bazel_package.js b/packages/kbn-bazel-packages/src/bazel_package.js index c6a31ccc0dbc1..4a6d1b69bd8b2 100644 --- a/packages/kbn-bazel-packages/src/bazel_package.js +++ b/packages/kbn-bazel-packages/src/bazel_package.js @@ -10,8 +10,8 @@ const { inspect } = require('util'); const Path = require('path'); const Fsp = require('fs/promises'); -/** @typedef {import('./types').ParsedPackageJson} ParsedPackageJson */ const { readPackageJson } = require('./parse_package_json'); +const { readPackageManifest } = require('./parse_package_manifest'); const BUILD_RULE_NAME = /(^|\s)name\s*=\s*"build"/; const BUILD_TYPES_RULE_NAME = /(^|\s)name\s*=\s*"build_types"/; @@ -20,7 +20,8 @@ const BUILD_TYPES_RULE_NAME = /(^|\s)name\s*=\s*"build_types"/; * Representation of a Bazel Package in the Kibana repository * @class * @property {string} normalizedRepoRelativeDir - * @property {import('./types').ParsedPackageJson} pkg + * @property {import('./types').KibanaPackageManifest} manifest + * @property {import('./types').ParsedPackageJson | undefined} pkg * @property {string | undefined} buildBazelContent */ class BazelPackage { @@ -28,10 +29,11 @@ class BazelPackage { * Create a BazelPackage object from a package directory. Reads some files from the package and returns * a Promise for a BazelPackage instance. * @param {string} repoRoot - * @param {string} dir + * @param {string} path */ - static async fromDir(repoRoot, dir) { - const pkg = readPackageJson(Path.resolve(dir, 'package.json')); + static async fromManifest(repoRoot, path) { + const manifest = readPackageManifest(path); + const dir = Path.dirname(path); let buildBazelContent; try { @@ -40,7 +42,29 @@ class BazelPackage { throw new Error(`unable to read BUILD.bazel file in [${dir}]: ${error.message}`); } - return new BazelPackage(Path.relative(repoRoot, dir), pkg, buildBazelContent); + return new BazelPackage( + Path.relative(repoRoot, dir), + manifest, + readPackageJson(Path.resolve(dir, 'package.json')), + buildBazelContent + ); + } + + /** + * Sort a list of bazek packages + * @param {BazelPackage[]} pkgs + */ + static sort(pkgs) { + return pkgs.slice().sort(BazelPackage.sorter); + } + + /** + * Sort an array of bazel packages + * @param {BazelPackage} a + * @param {BazelPackage} b + */ + static sorter(a, b) { + return a.normalizedRepoRelativeDir.localeCompare(b.normalizedRepoRelativeDir); } constructor( @@ -49,9 +73,14 @@ class BazelPackage { * @type {string} */ normalizedRepoRelativeDir, + /** + * Parsed kibana.jsonc manifest from the package + * @type {import('./types').KibanaPackageManifest} + */ + manifest, /** * Parsed package.json file from the package - * @type {import('./types').ParsedPackageJson} + * @type {import('./types').ParsedPackageJson | undefined} */ pkg, /** @@ -61,6 +90,7 @@ class BazelPackage { buildBazelContent = undefined ) { this.normalizedRepoRelativeDir = normalizedRepoRelativeDir; + this.manifest = manifest; this.pkg = pkg; this.buildBazelContent = buildBazelContent; } @@ -83,7 +113,7 @@ class BazelPackage { * Returns true if the package is not intended to be in the build */ isDevOnly() { - return !!this.pkg.kibana?.devOnly; + return !!this.manifest.devOnly; } /** diff --git a/packages/kbn-bazel-packages/src/bazel_package.test.ts b/packages/kbn-bazel-packages/src/bazel_package.test.ts index 70d540e43f06a..16a06dab7e08e 100644 --- a/packages/kbn-bazel-packages/src/bazel_package.test.ts +++ b/packages/kbn-bazel-packages/src/bazel_package.test.ts @@ -10,39 +10,51 @@ import Fs from 'fs'; import Path from 'path'; import { BazelPackage } from './bazel_package'; +import { KibanaPackageManifest, ParsedPackageJson } from './types'; const OWN_BAZEL_BUILD_FILE = Fs.readFileSync(Path.resolve(__dirname, '../BUILD.bazel'), 'utf8'); +const pkgJson: ParsedPackageJson = { + name: 'foo', +}; +const manifest: KibanaPackageManifest = { + type: 'shared-common', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + runtimeDeps: [], + typeDeps: [], +}; + describe('hasBuildRule()', () => { it('returns true if there is a rule with the name "build"', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }, OWN_BAZEL_BUILD_FILE); + const pkg = new BazelPackage('foo', manifest, pkgJson, OWN_BAZEL_BUILD_FILE); expect(pkg.hasBuildRule()).toBe(true); }); it('returns false if there is no rule with name "build"', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }, ``); + const pkg = new BazelPackage('foo', manifest, pkgJson, ``); expect(pkg.hasBuildRule()).toBe(false); }); it('returns false if there is no BUILD.bazel file', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }); + const pkg = new BazelPackage('foo', manifest, pkgJson); expect(pkg.hasBuildRule()).toBe(false); }); }); describe('hasBuildTypesRule()', () => { it('returns true if there is a rule with the name "build_types"', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }, OWN_BAZEL_BUILD_FILE); + const pkg = new BazelPackage('foo', manifest, pkgJson, OWN_BAZEL_BUILD_FILE); expect(pkg.hasBuildTypesRule()).toBe(true); }); it('returns false if there is no rule with name "build_types"', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }, ``); + const pkg = new BazelPackage('foo', manifest, pkgJson, ``); expect(pkg.hasBuildTypesRule()).toBe(false); }); it('returns false if there is no BUILD.bazel file', () => { - const pkg = new BazelPackage('foo', { name: 'foo' }); + const pkg = new BazelPackage('foo', manifest, pkgJson); expect(pkg.hasBuildTypesRule()).toBe(false); }); }); diff --git a/packages/kbn-bazel-packages/src/discover_packages.js b/packages/kbn-bazel-packages/src/discover_packages.js index 17678115c7452..5532205404d26 100644 --- a/packages/kbn-bazel-packages/src/discover_packages.js +++ b/packages/kbn-bazel-packages/src/discover_packages.js @@ -6,39 +6,34 @@ * Side Public License, v 1. */ -const Path = require('path'); - const { BazelPackage } = require('./bazel_package'); const { getAllBazelPackageDirs } = require('./bazel_package_dirs'); const { findPackages } = require('./find_files'); const { asyncMapWithLimit } = require('./async'); /** + * Returns an array of all the package manifest paths in the repository * @param {string} repoRoot */ -function discoverBazelPackageLocations(repoRoot) { - const packagesWithBuildBazel = getAllBazelPackageDirs(repoRoot) - .flatMap((packageDir) => findPackages(packageDir, 'BUILD.bazel')) - .map((path) => Path.dirname(path)); - - // NOTE: only return as discovered packages with a package.json + BUILD.bazel file. - // In the future we should change this to only discover the ones with kibana.jsonc. +function discoverPackageManifestPaths(repoRoot) { return getAllBazelPackageDirs(repoRoot) - .flatMap((packageDir) => findPackages(packageDir, 'package.json')) - .map((path) => Path.dirname(path)) - .filter((pkg) => packagesWithBuildBazel.includes(pkg)) + .flatMap((packageDir) => findPackages(packageDir, 'kibana.jsonc')) .sort((a, b) => a.localeCompare(b)); } /** + * Resolves to an array of BazelPackage instances which parse the manifest files, + * package.json files, and provide useful metadata about each package. * @param {string} repoRoot */ async function discoverBazelPackages(repoRoot) { - return await asyncMapWithLimit( - discoverBazelPackageLocations(repoRoot), - 100, - async (dir) => await BazelPackage.fromDir(repoRoot, dir) + return BazelPackage.sort( + await asyncMapWithLimit( + discoverPackageManifestPaths(repoRoot), + 100, + async (path) => await BazelPackage.fromManifest(repoRoot, path) + ) ); } -module.exports = { discoverBazelPackageLocations, discoverBazelPackages }; +module.exports = { discoverPackageManifestPaths, discoverBazelPackages }; diff --git a/packages/kbn-jsonc/index.js b/packages/kbn-bazel-packages/src/jsonc.js similarity index 87% rename from packages/kbn-jsonc/index.js rename to packages/kbn-bazel-packages/src/jsonc.js index 4788833f0f4d7..54d43b2e666d8 100644 --- a/packages/kbn-jsonc/index.js +++ b/packages/kbn-bazel-packages/src/jsonc.js @@ -6,10 +6,11 @@ * Side Public License, v 1. */ -const { stripJsonComments } = require('./src/strip_json_comments'); +const { stripJsonComments } = require('./strip_json_comments'); /** * @param {string} jsonWithComments + * @returns {unknown} */ function parse(jsonWithComments) { return JSON.parse( diff --git a/packages/kbn-bazel-packages/src/parse_package_json.js b/packages/kbn-bazel-packages/src/parse_package_json.js index 27a78f91a4466..adcf176ea6853 100644 --- a/packages/kbn-bazel-packages/src/parse_package_json.js +++ b/packages/kbn-bazel-packages/src/parse_package_json.js @@ -22,8 +22,12 @@ function isObj(v) { * @returns {asserts v is import('./types').ParsedPackageJson} */ function validateParsedPackageJson(v) { - if (!isObj(v) || typeof v.name !== 'string') { - throw new Error('Expected at least a "name" property'); + if (!isObj(v)) { + throw new Error('Expected package.json to be a JSON object'); + } + + if (typeof v.name !== 'string') { + throw new Error('Expected package.json to have a "name"'); } if (v.dependencies && !isObj(v.dependencies)) { @@ -49,7 +53,7 @@ function validateParsedPackageJson(v) { /** * Reads a given package.json file from disk and parses it * @param {string} path - * @returns {import('./types').ParsedPackageJson} + * @returns {import('./types').ParsedPackageJson | undefined} */ function readPackageJson(path) { let pkg; @@ -57,7 +61,9 @@ function readPackageJson(path) { pkg = JSON.parse(Fs.readFileSync(path, 'utf8')); validateParsedPackageJson(pkg); } catch (error) { - throw new Error(`unable to parse package.json at [${path}]: ${error.message}`); + if (error.code !== 'ENOENT') { + throw new Error(`unable to parse package.json at [${path}]: ${error.message}`); + } } return pkg; } diff --git a/packages/kbn-bazel-packages/src/parse_package_manifest.js b/packages/kbn-bazel-packages/src/parse_package_manifest.js new file mode 100644 index 0000000000000..42acbf3e60366 --- /dev/null +++ b/packages/kbn-bazel-packages/src/parse_package_manifest.js @@ -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 and the Server Side Public License, v 1; you may 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 Fs = require('fs'); +const { inspect } = require('util'); + +const { + isObj, + isValidId, + isValidPkgType, + isArrOfIds, + isArrOfStrings, + PACKAGE_TYPES, +} = require('./parse_utils'); +const { parse } = require('./jsonc'); + +/** + * @param {string} key + * @param {unknown} value + * @param {string} msg + * @returns {Error} + */ +const err = (key, value, msg) => { + const dbg = ['string', 'number', 'boolean'].includes(typeof value) ? value : inspect(value); + return new Error(`invalid package "${key}" [${dbg}], ${msg}`); +}; + +/** + * @param {unknown} plugin + * @returns {import('./types').PluginPackageManifest['plugin']} + */ +function validatePackageManifestPlugin(plugin) { + if (!isObj(plugin)) { + throw err(`plugin`, plugin, `must be an object`); + } + + const { + id, + configPath, + requiredPlugins, + optionalPlugins, + description, + enabledOnAnonymousPages, + serviceFolders, + ...extra + } = plugin; + + const extraKeys = Object.keys(extra); + if (extraKeys.length) { + throw new Error(`unexpected keys in "plugin" of package [${extraKeys.join(', ')}]`); + } + + if (typeof id !== 'string' || !isValidId(id)) { + throw err(`plugin.id`, id, `must be a string in camel or snake case`); + } + + if (configPath !== undefined && !isArrOfIds(configPath)) { + throw err( + `plugin.configPath`, + configPath, + `must be an array of strings in camel or snake case` + ); + } + + if (requiredPlugins !== undefined && !isArrOfIds(requiredPlugins)) { + throw err( + `plugin.requiredPlugins`, + requiredPlugins, + `must be an array of strings in camel or snake case` + ); + } + + if (optionalPlugins !== undefined && !isArrOfIds(optionalPlugins)) { + throw err( + `plugin.requiredPlugins`, + optionalPlugins, + `must be an array of strings in camel or snake case` + ); + } + + if (description !== undefined && typeof description !== 'string') { + throw err(`plugin.description`, description, `must be a string`); + } + + if (enabledOnAnonymousPages !== undefined && typeof enabledOnAnonymousPages !== 'boolean') { + throw err(`plugin.enabledOnAnonymousPages`, enabledOnAnonymousPages, `must be a boolean`); + } + + if (serviceFolders !== undefined && !isArrOfStrings(serviceFolders)) { + throw err(`plugin.serviceFolders`, serviceFolders, `must be an array of strings`); + } + + return { + id, + configPath, + requiredPlugins, + optionalPlugins, + description, + enabledOnAnonymousPages, + serviceFolders, + }; +} + +/** + * Validate the contents of a parsed kibana.jsonc file. + * @param {unknown} parsed + * @returns {import('./types').KibanaPackageManifest} + */ +function validatePackageManifest(parsed) { + if (!isObj(parsed)) { + throw new Error('expected manifest root to be an object'); + } + + const { type, id, owner, typeDeps, runtimeDeps, devOnly, plugin, sharedBrowserBundle, ...extra } = + parsed; + + const extraKeys = Object.keys(extra); + if (extraKeys.length) { + throw new Error(`unexpected keys in package manifest [${extraKeys.join(', ')}]`); + } + + if (!isValidPkgType(type)) { + throw err(`type`, type, `options are [${PACKAGE_TYPES.join(', ')}]`); + } + + if (typeof id !== 'string' || !id.startsWith('@kbn/')) { + throw err(`id`, id, `must be a string that starts with @kbn/`); + } + + if (typeof owner !== 'string' || !owner.startsWith('@')) { + throw err(`owner`, owner, `must be a valid Github team handle starting with @`); + } + + if (!isArrOfStrings(typeDeps)) { + throw err(`typeDeps`, typeDeps, `must be an array of strings`); + } + + if (!isArrOfStrings(runtimeDeps)) { + throw err(`runtimeDeps`, runtimeDeps, `must be an array of strings`); + } + + if (devOnly !== undefined && typeof devOnly !== 'boolean') { + throw err(`devOnly`, devOnly, `must be a boolean when defined`); + } + + const base = { + id, + owner, + typeDeps, + runtimeDeps, + devOnly, + }; + + // return if this is one of the more basic types of package types + if (type === 'shared-server' || type === 'functional-tests' || type === 'test-helper') { + return { + type, + ...base, + }; + } + + // handle the plugin field for plugin-* types + if (type === 'plugin-browser' || type === 'plugin-server') { + return { + type, + ...base, + plugin: validatePackageManifestPlugin(plugin), + }; + } + + // parse the sharedBrowserBundle for shared-browser and shared-common types + if (sharedBrowserBundle !== undefined && typeof sharedBrowserBundle !== 'boolean') { + throw err(`sharedBrowserBundle`, sharedBrowserBundle, `must be a boolean when defined`); + } + return { + type, + ...base, + sharedBrowserBundle, + }; +} + +/** + * Parse a kibana.jsonc file from the filesystem + * @param {string} path + */ +function readPackageManifest(path) { + let content; + try { + content = Fs.readFileSync(path, 'utf8'); + } catch (error) { + if (error.code === 'ENOENT') { + throw new Error(`Missing kibana.jsonc file at ${path}`); + } + + throw error; + } + + try { + return parsePackageManifest(content); + } catch (error) { + throw new Error(`Unable to parse [${path}]: ${error.message}`); + } +} + +/** + * Parse a kibana.jsonc file from a string + * @param {string} content + */ +function parsePackageManifest(content) { + let parsed; + try { + parsed = parse(content); + } catch (error) { + throw new Error(`Invalid JSONc: ${error.message}`); + } + + return validatePackageManifest(parsed); +} + +module.exports = { parsePackageManifest, readPackageManifest, validatePackageManifest }; diff --git a/packages/kbn-bazel-packages/src/parse_utils.js b/packages/kbn-bazel-packages/src/parse_utils.js new file mode 100644 index 0000000000000..639f5b2391580 --- /dev/null +++ b/packages/kbn-bazel-packages/src/parse_utils.js @@ -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. + */ + +/** @type {{ [k in import('./types').KibanaPackageType]: true }} */ +const PACKAGE_TYPE_MAP = { + 'functional-tests': true, + 'plugin-browser': true, + 'plugin-server': true, + 'shared-browser': true, + 'shared-common': true, + 'shared-scss': true, + 'shared-server': true, + 'test-helper': true, +}; + +const PACKAGE_TYPES = /** @type {Array} */ ( + /** @type {unknown} */ (Object.keys(PACKAGE_TYPE_MAP)) +); + +const ID_PATTERN = /^[a-z][a-zA-Z_]*$/; + +/** + * @param {unknown} v + * @returns {v is Record} + */ +function isObj(v) { + return typeof v === 'object' && v !== null; +} + +/** @param {unknown} v */ +function isValidId(v) { + return typeof v === 'string' && ID_PATTERN.test(v); +} + +/** + * @param {unknown} v + * @returns {v is import('./types').KibanaPackageType} + */ +function isValidPkgType(v) { + return typeof v === 'string' && Object.hasOwn(PACKAGE_TYPE_MAP, v); +} + +/** + * @param {unknown} v + * @returns {v is string[]} + */ +function isArrOfStrings(v) { + return Array.isArray(v) && v.every((i) => typeof i === 'string'); +} + +/** + * @param {unknown} v + * @returns {v is string[]} + */ +function isArrOfIds(v) { + return Array.isArray(v) && v.every(isValidId); +} + +module.exports = { + PACKAGE_TYPES, + isObj, + isValidId, + isValidPkgType, + isArrOfIds, + isArrOfStrings, +}; diff --git a/packages/kbn-jsonc/src/strip_json_comments.js b/packages/kbn-bazel-packages/src/strip_json_comments.js similarity index 100% rename from packages/kbn-jsonc/src/strip_json_comments.js rename to packages/kbn-bazel-packages/src/strip_json_comments.js diff --git a/packages/kbn-bazel-packages/src/types.ts b/packages/kbn-bazel-packages/src/types.ts index dc77d35bc206a..c4b445adfd851 100644 --- a/packages/kbn-bazel-packages/src/types.ts +++ b/packages/kbn-bazel-packages/src/types.ts @@ -29,3 +29,87 @@ export interface ParsedPackageJson { /** All other fields in the package.json are typed as unknown as we don't care what they are */ [key: string]: unknown; } + +export type KibanaPackageType = + | 'plugin-browser' + | 'plugin-server' + | 'shared-browser' + | 'shared-server' + | 'shared-common' + | 'shared-scss' + | 'functional-tests' + | 'test-helper'; + +interface PackageManifestBaseFields { + /** + * The type of this package. Package types define how a package can and should + * be used/built. Some package types also change the way that packages are + * interpreted. + */ + type: KibanaPackageType; + /** + * Module ID for this package. This must be globbally unique amoungst all + * packages and should include the most important information about how this + * package should be used. Avoid generic names to aid in disambiguation. + */ + id: string; + /** + * Github handle for the person or team who is responsible for this package. + * This owner will be used in the codeowners files for this package. + * + * For additional codeowners, you add manually add entries to the codeowners file. + */ + owner: string; + /** + * Packages which are required for the source code in the package to be type- + * checked. This list is updated automatically by the package linter + */ + typeDeps: string[]; + /** + * Packages which are required for the source code of the package to run. This + * list is updated automatically by the package linter. + */ + runtimeDeps: string[]; + /** + * A devOnly package can be used by other devOnly packages (and only + * other devOnly packages) and will never be included in the distributable + */ + devOnly?: boolean; +} + +export interface PluginPackageManifest extends PackageManifestBaseFields { + type: 'plugin-browser' | 'plugin-server'; + /** + * Details about the plugin which is contained within this package. + */ + plugin: { + id: string; + configPath?: string[]; + requiredPlugins?: string[]; + optionalPlugins?: string[]; + description?: string; + enabledOnAnonymousPages?: boolean; + serviceFolders?: string[]; + }; +} + +export interface SharedBrowserPackageManifest extends PackageManifestBaseFields { + type: 'shared-browser' | 'shared-common'; + /** + * When a package is used by many other packages in the browser, or requires some + * specific state in the module scope (though we highly recommend against this) you + * can set `sharedBrowserBundle` to true so this package to load in a separate async + * bundle request rather than being copied into the bundles of each package which + * use it. (not yet implemented) + */ + sharedBrowserBundle?: boolean; +} + +export interface BasePackageManifest extends PackageManifestBaseFields { + type: 'shared-server' | 'functional-tests' | 'test-helper' | 'shared-scss'; +} + +export type KibanaPackageManifest = + | PluginPackageManifest + | SharedBrowserPackageManifest + | BasePackageManifest; diff --git a/packages/kbn-bazel-runner/kibana.jsonc b/packages/kbn-bazel-runner/kibana.jsonc new file mode 100644 index 0000000000000..b313e99f5b9cc --- /dev/null +++ b/packages/kbn-bazel-runner/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/bazel-runner", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-bazel-runner/package.json b/packages/kbn-bazel-runner/package.json index d8fe010f0e4c3..540dfbac4a037 100644 --- a/packages/kbn-bazel-runner/package.json +++ b/packages/kbn-bazel-runner/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-chart-icons/kibana.jsonc b/packages/kbn-chart-icons/kibana.jsonc new file mode 100644 index 0000000000000..1d1de945c95de --- /dev/null +++ b/packages/kbn-chart-icons/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/chart-icons", + "owner": "@elastic/kibana-vis-editors", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ci-stats-core/kibana.jsonc b/packages/kbn-ci-stats-core/kibana.jsonc new file mode 100644 index 0000000000000..9140ec71ef912 --- /dev/null +++ b/packages/kbn-ci-stats-core/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/ci-stats-core", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ci-stats-core/package.json b/packages/kbn-ci-stats-core/package.json index d1141ec3c55a8..fc56e2e3213ea 100644 --- a/packages/kbn-ci-stats-core/package.json +++ b/packages/kbn-ci-stats-core/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-ci-stats-performance-metrics/kibana.jsonc b/packages/kbn-ci-stats-performance-metrics/kibana.jsonc new file mode 100644 index 0000000000000..3c4b4a440a98f --- /dev/null +++ b/packages/kbn-ci-stats-performance-metrics/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/ci-stats-performance-metrics", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ci-stats-performance-metrics/package.json b/packages/kbn-ci-stats-performance-metrics/package.json index e8da3dcde3dca..0801174ab4a02 100644 --- a/packages/kbn-ci-stats-performance-metrics/package.json +++ b/packages/kbn-ci-stats-performance-metrics/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-ci-stats-reporter/kibana.jsonc b/packages/kbn-ci-stats-reporter/kibana.jsonc new file mode 100644 index 0000000000000..9991f55a342f9 --- /dev/null +++ b/packages/kbn-ci-stats-reporter/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/ci-stats-reporter", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ci-stats-reporter/package.json b/packages/kbn-ci-stats-reporter/package.json index 01743bced98d5..1393c08cddac4 100644 --- a/packages/kbn-ci-stats-reporter/package.json +++ b/packages/kbn-ci-stats-reporter/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-cli-dev-mode/kibana.jsonc b/packages/kbn-cli-dev-mode/kibana.jsonc new file mode 100644 index 0000000000000..18c9cb7ba46a0 --- /dev/null +++ b/packages/kbn-cli-dev-mode/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/cli-dev-mode", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json index 80076a9851087..6113b1deef073 100644 --- a/packages/kbn-cli-dev-mode/package.json +++ b/packages/kbn-cli-dev-mode/package.json @@ -3,8 +3,5 @@ "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true, - "kibana": { - "devOnly": true - } + "private": true } \ No newline at end of file diff --git a/packages/kbn-coloring/kibana.jsonc b/packages/kbn-coloring/kibana.jsonc new file mode 100644 index 0000000000000..0b1ff8a449286 --- /dev/null +++ b/packages/kbn-coloring/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/coloring", + "owner": "@elastic/kibana-vis-editors", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-config-mocks/kibana.jsonc b/packages/kbn-config-mocks/kibana.jsonc new file mode 100644 index 0000000000000..de1d13289e8ff --- /dev/null +++ b/packages/kbn-config-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/config-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-config-schema/kibana.jsonc b/packages/kbn-config-schema/kibana.jsonc new file mode 100644 index 0000000000000..c889555708675 --- /dev/null +++ b/packages/kbn-config-schema/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/config-schema", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-config/kibana.jsonc b/packages/kbn-config/kibana.jsonc new file mode 100644 index 0000000000000..e3bac638520bd --- /dev/null +++ b/packages/kbn-config/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/config", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-crypto-browser/kibana.jsonc b/packages/kbn-crypto-browser/kibana.jsonc new file mode 100644 index 0000000000000..9faf12c5f05b0 --- /dev/null +++ b/packages/kbn-crypto-browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/crypto-browser", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-crypto/kibana.jsonc b/packages/kbn-crypto/kibana.jsonc new file mode 100644 index 0000000000000..21f9cbbc81c74 --- /dev/null +++ b/packages/kbn-crypto/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/crypto", + "owner": "@elastic/kibana-security", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-datemath/kibana.jsonc b/packages/kbn-datemath/kibana.jsonc new file mode 100644 index 0000000000000..85522ceb11b9e --- /dev/null +++ b/packages/kbn-datemath/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/datemath", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-dev-cli-errors/kibana.jsonc b/packages/kbn-dev-cli-errors/kibana.jsonc new file mode 100644 index 0000000000000..66a63cdce3074 --- /dev/null +++ b/packages/kbn-dev-cli-errors/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/dev-cli-errors", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-dev-cli-errors/package.json b/packages/kbn-dev-cli-errors/package.json index 62e1465933a52..e4757b7ad9b38 100644 --- a/packages/kbn-dev-cli-errors/package.json +++ b/packages/kbn-dev-cli-errors/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-dev-cli-runner/kibana.jsonc b/packages/kbn-dev-cli-runner/kibana.jsonc new file mode 100644 index 0000000000000..43e1b39ab17f2 --- /dev/null +++ b/packages/kbn-dev-cli-runner/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/dev-cli-runner", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-dev-cli-runner/package.json b/packages/kbn-dev-cli-runner/package.json index 779d6f48a4fb7..12670190159af 100644 --- a/packages/kbn-dev-cli-runner/package.json +++ b/packages/kbn-dev-cli-runner/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-dev-proc-runner/kibana.jsonc b/packages/kbn-dev-proc-runner/kibana.jsonc new file mode 100644 index 0000000000000..e028b7e7d795d --- /dev/null +++ b/packages/kbn-dev-proc-runner/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/dev-proc-runner", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-dev-proc-runner/package.json b/packages/kbn-dev-proc-runner/package.json index 2de282a948eda..38907397d2c52 100644 --- a/packages/kbn-dev-proc-runner/package.json +++ b/packages/kbn-dev-proc-runner/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-dev-proc-runner/src/proc.ts b/packages/kbn-dev-proc-runner/src/proc.ts index ffe7cb6464123..d30a893ae4c75 100644 --- a/packages/kbn-dev-proc-runner/src/proc.ts +++ b/packages/kbn-dev-proc-runner/src/proc.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import { statSync } from 'fs'; +import Fs from 'fs'; +import Path from 'path'; import { promisify } from 'util'; +import stripAnsi from 'strip-ansi'; import execa from 'execa'; import * as Rx from 'rxjs'; @@ -29,6 +31,7 @@ export interface ProcOptions { cwd: string; env?: Record; stdin?: string; + writeLogsToPath?: string; } async function withTimeout( @@ -44,13 +47,21 @@ export type Proc = ReturnType; export function startProc(name: string, options: ProcOptions, log: ToolingLog) { const { cmd, args, cwd, env, stdin } = options; - log.info('[%s] > %s', name, cmd === process.execPath ? 'node' : cmd, args.join(' ')); + let stdioTarget: undefined | NodeJS.WritableStream; + if (!options.writeLogsToPath) { + log.info('starting [%s] > %s', name, cmd === process.execPath ? 'node' : cmd, args.join(' ')); + } else { + stdioTarget = Fs.createWriteStream(options.writeLogsToPath, 'utf8'); + const exec = cmd === process.execPath ? 'node' : cmd; + const relOut = Path.relative(process.cwd(), options.writeLogsToPath); + log.info(`starting [${name}] and writing output to ${relOut} > ${exec} ${args.join(' ')}`); + } // spawn fails with ENOENT when either the // cmd or cwd don't exist, so we check for the cwd // ahead of time so that the error is less ambiguous try { - if (!statSync(cwd).isDirectory()) { + if (!Fs.statSync(cwd).isDirectory()) { throw new Error(`cwd "${cwd}" exists but is not a directory`); } } catch (err) { @@ -104,7 +115,20 @@ export function startProc(name: string, options: ProcOptions, log: ToolingLog) { observeLines(childProcess.stdout!), // TypeScript note: As long as the proc stdio[1] is 'pipe', then stdout will not be null observeLines(childProcess.stderr!) // TypeScript note: As long as the proc stdio[1] is 'pipe', then stderr will not be null ).pipe( - tap((line) => log.write(` ${chalk.gray('proc')} [${chalk.gray(name)}] ${line}`)), + tap({ + next(line) { + if (stdioTarget) { + stdioTarget.write(stripAnsi(line) + '\n'); + } else { + log.write(` ${chalk.gray('proc')} [${chalk.gray(name)}] ${line}`); + } + }, + complete() { + if (stdioTarget) { + stdioTarget.end(); + } + }, + }), share() ); diff --git a/packages/kbn-dev-proc-runner/src/proc_runner.ts b/packages/kbn-dev-proc-runner/src/proc_runner.ts index 56a6ee48c3150..1226cbeb3eef1 100644 --- a/packages/kbn-dev-proc-runner/src/proc_runner.ts +++ b/packages/kbn-dev-proc-runner/src/proc_runner.ts @@ -36,12 +36,12 @@ export class ProcRunner { private procs: Proc[] = []; private signalUnsubscribe: () => void; - constructor(private log: ToolingLog) { + constructor(private readonly log: ToolingLog) { this.log = log.withType('ProcRunner'); this.signalUnsubscribe = exitHook(() => { this.teardown().catch((error) => { - log.error(`ProcRunner teardown error: ${error.stack}`); + this.log.error(`ProcRunner teardown error: ${error.stack}`); }); }); } @@ -58,6 +58,7 @@ export class ProcRunner { waitTimeout = 15 * MINUTE, env = process.env, onEarlyExit, + writeLogsToPath, } = options; const cmd = options.cmd === 'node' ? process.execPath : options.cmd; @@ -79,6 +80,7 @@ export class ProcRunner { cwd, env, stdin, + writeLogsToPath, }); if (onEarlyExit) { diff --git a/packages/kbn-dev-utils/kibana.jsonc b/packages/kbn-dev-utils/kibana.jsonc new file mode 100644 index 0000000000000..7a9e4e644dcf0 --- /dev/null +++ b/packages/kbn-dev-utils/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/dev-utils", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index ab4f489e7d345..1316319286a96 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "./target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-doc-links/kibana.jsonc b/packages/kbn-doc-links/kibana.jsonc new file mode 100644 index 0000000000000..a3af199f6c32e --- /dev/null +++ b/packages/kbn-doc-links/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/doc-links", + "owner": "@elastic/kibana-docs", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index ecef81171b241..7b7c57ff4f7f2 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -126,6 +126,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`, crawlerOverview: `${ENTERPRISE_SEARCH_DOCS}crawler.html`, languageAnalyzers: `${ELASTICSEARCH_DOCS}analysis-lang-analyzer.html`, + languageClients: `${ENTERPRISE_SEARCH_DOCS}programming-language-clients.html`, licenseManagement: `${ENTERPRISE_SEARCH_DOCS}license-management.html`, mailService: `${ENTERPRISE_SEARCH_DOCS}mailer-configuration.html`, start: `${ENTERPRISE_SEARCH_DOCS}start.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 99396183fc64f..fb934f4aa4f2a 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -112,6 +112,7 @@ export interface DocLinks { readonly crawlerManaging: string; readonly crawlerOverview: string; readonly languageAnalyzers: string; + readonly languageClients: string; readonly licenseManagement: string; readonly mailService: string; readonly start: string; diff --git a/packages/kbn-docs-utils/kibana.jsonc b/packages/kbn-docs-utils/kibana.jsonc new file mode 100644 index 0000000000000..5e0ea2367d531 --- /dev/null +++ b/packages/kbn-docs-utils/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/docs-utils", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-docs-utils/package.json b/packages/kbn-docs-utils/package.json index 84fc3ccb0cded..d75a79ed44b22 100644 --- a/packages/kbn-docs-utils/package.json +++ b/packages/kbn-docs-utils/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": "true", - "main": "target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-ebt-tools/kibana.jsonc b/packages/kbn-ebt-tools/kibana.jsonc new file mode 100644 index 0000000000000..f9fde6d48f046 --- /dev/null +++ b/packages/kbn-ebt-tools/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ebt-tools", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-es-archiver/kibana.jsonc b/packages/kbn-es-archiver/kibana.jsonc new file mode 100644 index 0000000000000..cc2b4530ea552 --- /dev/null +++ b/packages/kbn-es-archiver/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/es-archiver", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-es-archiver/package.json b/packages/kbn-es-archiver/package.json index bff3990a0c1bc..ddd55875664e3 100644 --- a/packages/kbn-es-archiver/package.json +++ b/packages/kbn-es-archiver/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": "true", - "main": "target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "target_node/index.js" } diff --git a/packages/kbn-es-errors/kibana.jsonc b/packages/kbn-es-errors/kibana.jsonc new file mode 100644 index 0000000000000..3a121caaf95cc --- /dev/null +++ b/packages/kbn-es-errors/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/es-errors", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-es-query/kibana.jsonc b/packages/kbn-es-query/kibana.jsonc new file mode 100644 index 0000000000000..2bd959eec53eb --- /dev/null +++ b/packages/kbn-es-query/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/es-query", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-es/kibana.jsonc b/packages/kbn-es/kibana.jsonc new file mode 100644 index 0000000000000..6407107c3639c --- /dev/null +++ b/packages/kbn-es/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/es", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index ee8d8e89030e1..38c138d688362 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -3,8 +3,5 @@ "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true, - "kibana": { - "devOnly": true - } + "private": true } \ No newline at end of file diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js index 5c410523d70ca..a027db201b002 100644 --- a/packages/kbn-es/src/cluster.js +++ b/packages/kbn-es/src/cluster.js @@ -6,10 +6,12 @@ * Side Public License, v 1. */ +const fs = require('fs'); const fsp = require('fs/promises'); const execa = require('execa'); const chalk = require('chalk'); const path = require('path'); +const Rx = require('rxjs'); const { Client } = require('@elastic/elasticsearch'); const { downloadSnapshot, installSnapshot, installSource, installArchive } = require('./install'); const { ES_BIN, ES_PLUGIN_BIN, ES_KEYSTORE_BIN } = require('./paths'); @@ -315,6 +317,7 @@ exports.Cluster = class Cluster { startTime, skipReadyCheck, readyTimeout, + writeLogsToPath, ...options } = opts; @@ -322,7 +325,19 @@ exports.Cluster = class Cluster { throw new Error('ES has already been started'); } - this._log.info(chalk.bold('Starting')); + /** @type {NodeJS.WritableStream | undefined} */ + let stdioTarget; + + if (writeLogsToPath) { + stdioTarget = fs.createWriteStream(writeLogsToPath, 'utf8'); + this._log.info( + chalk.bold('Starting'), + `and writing logs to ${path.relative(process.cwd(), writeLogsToPath)}` + ); + } else { + this._log.info(chalk.bold('Starting')); + } + this._log.indent(4); const esArgs = new Map([ @@ -428,7 +443,8 @@ exports.Cluster = class Cluster { let reportSent = false; // parse and forward es stdout to the log this._process.stdout.on('data', (data) => { - const lines = parseEsLog(data.toString()); + const chunk = data.toString(); + const lines = parseEsLog(chunk); lines.forEach((line) => { if (!reportSent && line.message.includes('publish_address')) { reportSent = true; @@ -436,12 +452,36 @@ exports.Cluster = class Cluster { success: true, }); } - this._log.info(line.formattedMessage); + + if (stdioTarget) { + stdioTarget.write(chunk); + } else { + this._log.info(line.formattedMessage); + } }); }); // forward es stderr to the log - this._process.stderr.on('data', (data) => this._log.error(chalk.red(data.toString()))); + this._process.stderr.on('data', (data) => { + const chunk = data.toString(); + if (stdioTarget) { + stdioTarget.write(chunk); + } else { + this._log.error(chalk.red()); + } + }); + + // close the stdio target if we have one defined + if (stdioTarget) { + Rx.combineLatest([ + Rx.fromEvent(this._process.stderr, 'end'), + Rx.fromEvent(this._process.stdout, 'end'), + ]) + .pipe(Rx.first()) + .subscribe(() => { + stdioTarget.end(); + }); + } // observe the exit code of the process and reflect in _outcome promies const exitCode = new Promise((resolve) => this._process.once('exit', resolve)); diff --git a/packages/kbn-eslint-config/kibana.jsonc b/packages/kbn-eslint-config/kibana.jsonc new file mode 100644 index 0000000000000..3afe0461c4861 --- /dev/null +++ b/packages/kbn-eslint-config/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-config", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-eslint-config/package.json b/packages/kbn-eslint-config/package.json index eb9f7a4b08246..76082ec00ee44 100644 --- a/packages/kbn-eslint-config/package.json +++ b/packages/kbn-eslint-config/package.json @@ -7,9 +7,6 @@ "type": "git", "url": "git+https://github.com/elastic/kibana.git" }, - "kibana": { - "devOnly": true - }, "keywords": [], "author": "Spencer Alger ", "license": "Apache-2.0", diff --git a/packages/kbn-eslint-plugin-disable/kibana.jsonc b/packages/kbn-eslint-plugin-disable/kibana.jsonc new file mode 100644 index 0000000000000..43a4d8f8f90bb --- /dev/null +++ b/packages/kbn-eslint-plugin-disable/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-plugin-disable", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-eslint-plugin-eslint/kibana.jsonc b/packages/kbn-eslint-plugin-eslint/kibana.jsonc new file mode 100644 index 0000000000000..fc9c384c41562 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-plugin-eslint", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index ffce2cbefe743..c1f126223b27c 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -2,8 +2,5 @@ "name": "@kbn/eslint-plugin-eslint", "version": "1.0.0", "private": true, - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-eslint-plugin-imports/kibana.jsonc b/packages/kbn-eslint-plugin-imports/kibana.jsonc new file mode 100644 index 0000000000000..658733d10535b --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-plugin-imports", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-expect/kibana.jsonc b/packages/kbn-expect/kibana.jsonc new file mode 100644 index 0000000000000..53f789961dd33 --- /dev/null +++ b/packages/kbn-expect/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/expect", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-expect/package.json b/packages/kbn-expect/package.json index 2040683c539e2..fb732269fef97 100644 --- a/packages/kbn-expect/package.json +++ b/packages/kbn-expect/package.json @@ -4,8 +4,5 @@ "typings": "./expect.d.ts", "version": "1.0.0", "license": "MIT", - "private": true, - "kibana": { - "devOnly": true - } + "private": true } diff --git a/packages/kbn-field-types/kibana.jsonc b/packages/kbn-field-types/kibana.jsonc new file mode 100644 index 0000000000000..5bcf174037008 --- /dev/null +++ b/packages/kbn-field-types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/field-types", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-find-used-node-modules/kibana.jsonc b/packages/kbn-find-used-node-modules/kibana.jsonc new file mode 100644 index 0000000000000..d5c72f5927737 --- /dev/null +++ b/packages/kbn-find-used-node-modules/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/find-used-node-modules", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-find-used-node-modules/package.json b/packages/kbn-find-used-node-modules/package.json index c5da8d1f898c1..138a77f3ed286 100644 --- a/packages/kbn-find-used-node-modules/package.json +++ b/packages/kbn-find-used-node-modules/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-flot-charts/kibana.jsonc b/packages/kbn-flot-charts/kibana.jsonc new file mode 100644 index 0000000000000..ad96bcf118b1b --- /dev/null +++ b/packages/kbn-flot-charts/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/flot-charts", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-generate/kibana.jsonc b/packages/kbn-generate/kibana.jsonc new file mode 100644 index 0000000000000..d7d38e4822cb7 --- /dev/null +++ b/packages/kbn-generate/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/generate", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-generate/package.json b/packages/kbn-generate/package.json index aacf8c6aab817..8413023c99a2d 100644 --- a/packages/kbn-generate/package.json +++ b/packages/kbn-generate/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "./target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-generate/src/cli.ts b/packages/kbn-generate/src/cli.ts index 81e7a8cb85b55..d3d1366682fea 100644 --- a/packages/kbn-generate/src/cli.ts +++ b/packages/kbn-generate/src/cli.ts @@ -12,6 +12,7 @@ import { Render } from './lib/render'; import { ContextExtensions } from './generate_command'; import { PackageCommand } from './commands/package_command'; +import { CodeownersCommand } from './commands/codeowners_command'; import { PackagesBuildManifestCommand } from './commands/packages_build_manifest_command'; /** @@ -27,6 +28,6 @@ export function runGenerateCli() { }; }, }, - [PackageCommand, PackagesBuildManifestCommand] + [PackageCommand, PackagesBuildManifestCommand, CodeownersCommand] ).execute(); } diff --git a/packages/kbn-generate/src/commands/codeowners_command.ts b/packages/kbn-generate/src/commands/codeowners_command.ts new file mode 100644 index 0000000000000..7478f342c0d9a --- /dev/null +++ b/packages/kbn-generate/src/commands/codeowners_command.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 Fsp from 'fs/promises'; +import Path from 'path'; + +import { REPO_ROOT } from '@kbn/utils'; +import { discoverBazelPackages } from '@kbn/bazel-packages'; + +import type { GenerateCommand } from '../generate_command'; + +const REL = '.github/CODEOWNERS'; + +const GENERATED_START = ` + +#### +## Everything below this comment is automatically generated based on kibana.jsonc +## "owner" fields. This file is automatically updated by CI or can be updated locally +## by running \`node scripts/generate codeowners\`. +#### + +`; + +export const CodeownersCommand: GenerateCommand = { + name: 'codeowners', + description: 'Update the codeowners file based on the package manifest files', + usage: 'node scripts/generate codeowners', + async run({ log }) { + const coPath = Path.resolve(REPO_ROOT, REL); + const codeowners = await Fsp.readFile(coPath, 'utf8'); + const pkgs = await discoverBazelPackages(REPO_ROOT); + + let genStart = codeowners.indexOf(GENERATED_START); + if (genStart === -1) { + genStart = codeowners.length; + log.warning(`${REL} doesn't include the expected start-marker for injecting generated text`); + } + + const newCodeowners = `${codeowners.slice(0, genStart)}${GENERATED_START}${pkgs + .map((pkg) => `${pkg.normalizedRepoRelativeDir} ${pkg.manifest.owner}`) + .join('\n')}\n`; + + if (codeowners === newCodeowners) { + log.success(`${REL} is already up-to-date`); + return; + } + + await Fsp.writeFile(coPath, newCodeowners); + log.info(`${REL} updated`); + }, +}; diff --git a/packages/kbn-generate/src/commands/package_command.ts b/packages/kbn-generate/src/commands/package_command.ts index 32d5ff008c79f..ecfd2eb5f3d21 100644 --- a/packages/kbn-generate/src/commands/package_command.ts +++ b/packages/kbn-generate/src/commands/package_command.ts @@ -19,16 +19,21 @@ import { discoverBazelPackages, BAZEL_PACKAGE_DIRS } from '@kbn/bazel-packages'; import { createFailError, createFlagError, isFailError } from '@kbn/dev-cli-errors'; import { sortPackageJson } from '@kbn/sort-package-json'; +import { validateElasticTeam } from '../lib/validate_elastic_team'; import { TEMPLATE_DIR, ROOT_PKG_DIR, PKG_TEMPLATE_DIR } from '../paths'; import type { GenerateCommand } from '../generate_command'; +import { ask } from '../lib/ask'; + +const validPkgId = (id: unknown): id is string => + typeof id === 'string' && id.startsWith('@kbn/') && !id.includes(' '); export const PackageCommand: GenerateCommand = { name: 'package', description: 'Generate a basic package', - usage: 'node scripts/generate package [name]', + usage: 'node scripts/generate package [pkgId]', flags: { boolean: ['web', 'force', 'dev'], - string: ['dir'], + string: ['dir', 'owner'], help: ` --dev Generate a package which is intended for dev-only use and can access things like devDependencies --web Build webpack-compatible version of sources for this package. If your package is intended to be @@ -39,24 +44,37 @@ export const PackageCommand: GenerateCommand = { ${BAZEL_PACKAGE_DIRS.map((dir) => ` ./${dir}/*\n`).join('')} defaults to [./packages/{kebab-case-version-of-name}] --force If the --dir already exists, delete it before generation + --owner Github username of the owner for this package, if this is not specified then you will be asked for + this value interactively. `, }, async run({ log, flags, render }) { - const [name] = flags._; - if (!name) { - throw createFlagError(`missing package name`); - } - if (!name.startsWith('@kbn/')) { - throw createFlagError(`package name must start with @kbn/`); + const pkgId = + flags._[0] || + (await ask({ + question: `What should the package id be? (Must start with @kbn/ and have no spaces)`, + async validate(input) { + if (validPkgId(input)) { + return input; + } + + return { + err: `"${input}" must start with @kbn/ and have no spaces`, + }; + }, + })); + + if (!validPkgId(pkgId)) { + throw createFlagError(`package id must start with @kbn/ and have no spaces`); } - const typePkgName = `@types/${name.slice(1).replace('/', '__')}`; + const typePkgName = `@types/${pkgId.slice(1).replace('/', '__')}`; const web = !!flags.web; const dev = !!flags.dev; const packageDir = flags.dir ? Path.resolve(`${flags.dir}`) - : Path.resolve(ROOT_PKG_DIR, name.slice(1).replace('/', '-')); + : Path.resolve(ROOT_PKG_DIR, pkgId.slice(1).replace('/', '-')); const relContainingDir = Path.relative(REPO_ROOT, Path.dirname(packageDir)); if (!micromatch.isMatch(relContainingDir, BAZEL_PACKAGE_DIRS)) { throw createFlagError( @@ -82,13 +100,29 @@ ${BAZEL_PACKAGE_DIRS.map((dir) => ` ./${dir}/*\n`).join } } + const owner = + flags.owner || + (await ask({ + question: 'Which Elastic team should own this package? (Must start with "@elastic/")', + async validate(input) { + try { + return await validateElasticTeam(input); + } catch (error) { + log.error(`failed to validate team: ${error.message}`); + return input; + } + }, + })); + if (typeof owner !== 'string' || !owner.startsWith('@')) { + throw createFlagError(`expected --owner to be a string starting with an @ symbol`); + } + const templateFiles = await globby('**/*', { cwd: PKG_TEMPLATE_DIR, absolute: false, dot: true, onlyFiles: true, }); - if (!templateFiles.length) { throw new Error('unable to find package template files'); } @@ -119,9 +153,10 @@ ${BAZEL_PACKAGE_DIRS.map((dir) => ` ./${dir}/*\n`).join await render.toFile(src, dest, { pkg: { - name, + id: pkgId, web, dev, + owner, directoryName: Path.basename(normalizedRepoRelativeDir), normalizedRepoRelativeDir, }, @@ -146,8 +181,8 @@ ${BAZEL_PACKAGE_DIRS.map((dir) => ` ./${dir}/*\n`).join ? [packageJson.devDependencies, packageJson.dependencies] : [packageJson.dependencies, packageJson.devDependencies]; - addDeps[name] = `link:bazel-bin/${normalizedRepoRelativeDir}`; - delete removeDeps[name]; + addDeps[pkgId] = `link:bazel-bin/${normalizedRepoRelativeDir}`; + delete removeDeps[pkgId]; // for @types packages always remove from deps and add to devDeps packageJson.devDependencies[ @@ -167,6 +202,6 @@ ${BAZEL_PACKAGE_DIRS.map((dir) => ` ./${dir}/*\n`).join ); log.info('Updated packages/BUILD.bazel'); - log.success(`Generated ${name}! Please bootstrap to make sure it works.`); + log.success(`Generated ${pkgId}! Please bootstrap to make sure it works.`); }, }; diff --git a/packages/kbn-generate/src/commands/packages_build_manifest_command.ts b/packages/kbn-generate/src/commands/packages_build_manifest_command.ts index 9b05c1aed332d..4e27753479853 100644 --- a/packages/kbn-generate/src/commands/packages_build_manifest_command.ts +++ b/packages/kbn-generate/src/commands/packages_build_manifest_command.ts @@ -14,7 +14,6 @@ import { discoverBazelPackages } from '@kbn/bazel-packages'; import { TEMPLATE_DIR } from '../paths'; import { GenerateCommand } from '../generate_command'; -import { validateFile } from '../lib/validate_file'; const USAGE = `node scripts/generate packages_build_manifest`; @@ -22,15 +21,7 @@ export const PackagesBuildManifestCommand: GenerateCommand = { name: 'packages_build_manifest', usage: USAGE, description: 'Generate the packages/BUILD.bazel file', - flags: { - boolean: ['validate'], - help: ` - --validate Rather than writing the generated output to disk, validate that the content on disk is in sync with the - `, - }, - async run({ log, render, flags }) { - const validate = !!flags.validate; - + async run({ log, render }) { const packages = await discoverBazelPackages(REPO_ROOT); const dest = Path.resolve(REPO_ROOT, 'packages/BUILD.bazel'); const relDest = Path.relative(process.cwd(), dest); @@ -38,17 +29,22 @@ export const PackagesBuildManifestCommand: GenerateCommand = { const content = await render.toString( Path.join(TEMPLATE_DIR, 'packages_BUILD.bazel.ejs'), dest, - { - packages, - } + { packages } ); - if (validate) { - await validateFile(log, USAGE, dest, content); + let existing; + try { + existing = await Fsp.readFile(dest, 'utf8'); + } catch { + // noop + } + + if (existing === content) { + log.success(relDest, 'is already updated'); return; } await Fsp.writeFile(dest, content); - log.success('Wrote', relDest); + log.info(relDest, 'updated'); }, }; diff --git a/packages/kbn-generate/src/lib/ansi.ts b/packages/kbn-generate/src/lib/ansi.ts new file mode 100644 index 0000000000000..4fe360b510b3c --- /dev/null +++ b/packages/kbn-generate/src/lib/ansi.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. + */ + +// https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 +export const CLEAR_LINE_AND_MOVE_LEFT = `\x1B[2K\x1B[0G`; diff --git a/packages/kbn-generate/src/lib/ask.ts b/packages/kbn-generate/src/lib/ask.ts new file mode 100644 index 0000000000000..e5e33e8df058c --- /dev/null +++ b/packages/kbn-generate/src/lib/ask.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Chalk from 'chalk'; +import Readline from 'readline'; +import * as Rx from 'rxjs'; +import Util from 'util'; + +import * as Ansi from './ansi'; + +export type ValidationResult = string | { err: string }; + +interface Options { + question: string; + validate(input: string): Promise; +} + +export async function ask(options: Options) { + if (!process.stderr.isTTY) { + return undefined; + } + + const int = Readline.createInterface({ + input: process.stdin, + output: process.stderr, + }); + + const q = Util.promisify(int.question) as unknown as (q: string) => Promise; + + try { + return await Rx.firstValueFrom( + Rx.race( + Rx.fromEvent(int, 'error').pipe( + Rx.map((err) => { + throw err; + }) + ), + Rx.defer(() => q.call(int, `${Chalk.blueBright('?')} ${options.question} > `)).pipe( + Rx.mergeMap(async (answer) => { + process.stderr.write('validating...'); + try { + return await options.validate(answer); + } finally { + process.stderr.write(Ansi.CLEAR_LINE_AND_MOVE_LEFT); + } + }), + Rx.map((valid) => { + if (typeof valid === 'string') { + return valid; + } + + const label = `Error:`; + const indent = ' '.repeat(label.length + 1); + const indented = valid.err + .split('\n') + .map((l, i) => (i === 0 ? l : `${indent}${l}`)) + .join('\n'); + + process.stderr.write(`${Chalk.bgRed.white(label)} ${indented}\n`); + + throw new Error('retry'); + }), + Rx.retry({ + delay: (err) => { + if (typeof err === 'object' && err && err.message === 'retry') { + return Rx.of(1); + } else { + throw err; + } + }, + }) + ) + ) + ); + } finally { + int.close(); + } +} diff --git a/packages/kbn-generate/src/lib/validate_elastic_team.ts b/packages/kbn-generate/src/lib/validate_elastic_team.ts new file mode 100644 index 0000000000000..89138d59641c7 --- /dev/null +++ b/packages/kbn-generate/src/lib/validate_elastic_team.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Axios from 'axios'; +import { ValidationResult } from './ask'; + +interface Body { + match?: string; + suggestions: string[]; +} + +export async function validateElasticTeam(owner: string): Promise { + const slug = owner.startsWith('@') ? owner.slice(1) : owner; + + const res = await Axios.get('https://ci-stats.kibana.dev/v1/_validate_kibana_team', { + params: { + slug, + }, + timeout: 5000, + }); + + if (res.data.match) { + return `@${res.data.match}`; + } + + const err = `"${owner}" doesn't match any @elastic team, to override with another valid Github user pass the value with the --owner flag`; + + if (!res.data.suggestions?.length) { + return { err }; + } + + const list = res.data.suggestions.map((l) => ` @${l}`); + return { + err: `${err}\n Did you mean one of these?\n${list.join('\n')}`, + }; +} diff --git a/packages/kbn-generate/src/lib/validate_file.ts b/packages/kbn-generate/src/lib/validate_file.ts deleted file mode 100644 index 342a25e3d193d..0000000000000 --- a/packages/kbn-generate/src/lib/validate_file.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import Fsp from 'fs/promises'; -import Path from 'path'; - -import { diffStrings } from '@kbn/dev-utils'; -import { createFailError } from '@kbn/dev-cli-errors'; -import { ToolingLog } from '@kbn/tooling-log'; - -export async function validateFile(log: ToolingLog, usage: string, path: string, expected: string) { - const relPath = Path.relative(process.cwd(), path); - - let current; - try { - current = await Fsp.readFile(path, 'utf8'); - } catch (error) { - if (error && error.code === 'ENOENT') { - throw createFailError(`${relPath} is missing, please run "${usage}" and commit the result`); - } - - throw error; - } - - if (current !== expected) { - log.error(`${relPath} is outdated:\n${diffStrings(expected, current)}`); - throw createFailError(`${relPath} is outdated, please run "${usage}" and commit the result`); - } - - log.success(`${relPath} is valid`); -} diff --git a/packages/kbn-generate/templates/package/BUILD.bazel.ejs b/packages/kbn-generate/templates/package/BUILD.bazel.ejs index c66f67e93b15c..cb1d250f468e9 100644 --- a/packages/kbn-generate/templates/package/BUILD.bazel.ejs +++ b/packages/kbn-generate/templates/package/BUILD.bazel.ejs @@ -3,7 +3,7 @@ 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 = <%- json(pkg.directoryName) %> -PKG_REQUIRE_NAME = <%- json(pkg.name) %> +PKG_REQUIRE_NAME = <%- json(pkg.id) %> SOURCE_FILES = glob( [ diff --git a/packages/kbn-generate/templates/package/README.md.ejs b/packages/kbn-generate/templates/package/README.md.ejs index 769f536648a6d..826a90cb80987 100644 --- a/packages/kbn-generate/templates/package/README.md.ejs +++ b/packages/kbn-generate/templates/package/README.md.ejs @@ -1,3 +1,3 @@ -# <%- pkg.name %> +# <%- pkg.id %> Empty package generated by @kbn/generate diff --git a/packages/kbn-generate/templates/package/kibana.jsonc.ejs b/packages/kbn-generate/templates/package/kibana.jsonc.ejs new file mode 100644 index 0000000000000..f39655160f82f --- /dev/null +++ b/packages/kbn-generate/templates/package/kibana.jsonc.ejs @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": <%- json(pkg.id) %>, + "owner": <%- json(pkg.owner) %>,<% if (pkg.dev) { %> + "devOnly": true,<% } %> + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/kbn-generate/templates/package/package.json.ejs b/packages/kbn-generate/templates/package/package.json.ejs index 93b2813daffa1..44f53c0a1324c 100644 --- a/packages/kbn-generate/templates/package/package.json.ejs +++ b/packages/kbn-generate/templates/package/package.json.ejs @@ -1,5 +1,5 @@ { - "name": <%- json(pkg.name) %>, + "name": <%- json(pkg.id) %>, "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", @@ -7,9 +7,4 @@ <%_ if (pkg.web) { %>, "browser": "./target_web/index.js" <%_ } %> - <% if (pkg.dev) { %>, - "kibana": { - "devOnly": true - } - <% } %> } diff --git a/packages/kbn-get-repo-files/kibana.jsonc b/packages/kbn-get-repo-files/kibana.jsonc new file mode 100644 index 0000000000000..44ee4e026ba7e --- /dev/null +++ b/packages/kbn-get-repo-files/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/get-repo-files", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-get-repo-files/package.json b/packages/kbn-get-repo-files/package.json index 8cd19049fd197..21aa7c24d2b82 100644 --- a/packages/kbn-get-repo-files/package.json +++ b/packages/kbn-get-repo-files/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-handlebars/kibana.jsonc b/packages/kbn-handlebars/kibana.jsonc new file mode 100644 index 0000000000000..64249345bce8e --- /dev/null +++ b/packages/kbn-handlebars/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/handlebars", + "owner": "@elastic/kibana-security", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-hapi-mocks/kibana.jsonc b/packages/kbn-hapi-mocks/kibana.jsonc new file mode 100644 index 0000000000000..9a2632c95d814 --- /dev/null +++ b/packages/kbn-hapi-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/hapi-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-i18n-react/kibana.jsonc b/packages/kbn-i18n-react/kibana.jsonc new file mode 100644 index 0000000000000..296e7295e52b6 --- /dev/null +++ b/packages/kbn-i18n-react/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/i18n-react", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-i18n/kibana.jsonc b/packages/kbn-i18n/kibana.jsonc new file mode 100644 index 0000000000000..cd5613bc493c0 --- /dev/null +++ b/packages/kbn-i18n/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/i18n", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-import-resolver/kibana.jsonc b/packages/kbn-import-resolver/kibana.jsonc new file mode 100644 index 0000000000000..9e05986966210 --- /dev/null +++ b/packages/kbn-import-resolver/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/import-resolver", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-import-resolver/package.json b/packages/kbn-import-resolver/package.json index d8a6e72792399..a809d48bc2410 100644 --- a/packages/kbn-import-resolver/package.json +++ b/packages/kbn-import-resolver/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-import-resolver/src/import_resolver.ts b/packages/kbn-import-resolver/src/import_resolver.ts index 05b69f299a798..74e2ed52ca453 100644 --- a/packages/kbn-import-resolver/src/import_resolver.ts +++ b/packages/kbn-import-resolver/src/import_resolver.ts @@ -7,12 +7,12 @@ */ import Path from 'path'; -import Fs from 'fs'; import Resolve from 'resolve'; +import { readPackageManifest } from '@kbn/bazel-packages'; import { REPO_ROOT } from '@kbn/utils'; import normalizePath from 'normalize-path'; -import { discoverBazelPackageLocations } from '@kbn/bazel-packages'; +import { discoverPackageManifestPaths } from '@kbn/bazel-packages'; import { readPackageMap, PackageMap } from '@kbn/synthetic-package-map'; import { safeStat, readFileSync } from './helpers/fs'; @@ -25,18 +25,10 @@ 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(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)); + for (const manifestPath of discoverPackageManifestPaths(REPO_ROOT)) { + const relativeBazelPackageDir = Path.relative(REPO_ROOT, Path.dirname(manifestPath)); + const pkg = readPackageManifest(manifestPath); + pkgMap.set(pkg.id, normalizePath(relativeBazelPackageDir)); } return new ImportResolver(repoRoot, pkgMap, readPackageMap()); diff --git a/packages/kbn-interpreter/kibana.jsonc b/packages/kbn-interpreter/kibana.jsonc new file mode 100644 index 0000000000000..5439509b2c0c7 --- /dev/null +++ b/packages/kbn-interpreter/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/interpreter", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-io-ts-utils/kibana.jsonc b/packages/kbn-io-ts-utils/kibana.jsonc new file mode 100644 index 0000000000000..f903e878366b2 --- /dev/null +++ b/packages/kbn-io-ts-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/io-ts-utils", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-jest-serializers/kibana.jsonc b/packages/kbn-jest-serializers/kibana.jsonc new file mode 100644 index 0000000000000..2742ade92e31a --- /dev/null +++ b/packages/kbn-jest-serializers/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/jest-serializers", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-jest-serializers/package.json b/packages/kbn-jest-serializers/package.json index c049ef6725157..1f6642f0557fd 100644 --- a/packages/kbn-jest-serializers/package.json +++ b/packages/kbn-jest-serializers/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-jsonc/README.mdx b/packages/kbn-jsonc/README.mdx deleted file mode 100644 index 79ab8fc52bd92..0000000000000 --- a/packages/kbn-jsonc/README.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: kibDevDocsOpsJsonc -slug: /kibana-dev-docs/ops/jsonc -title: "@kbn/jsonc" -description: A package for parsing jsonc -date: 2022-05-24 -tags: ['kibana', 'dev', 'contributor', 'operations', 'json', 'jsonc'] ---- - -This package exposes a simple `parse(jsonc: string)` function for parsing JSON-C content. JSON-C is a variant of JSON which supports both block and line comments, which are incredibly useful for configuration files that are committed to the repository and would benefit from the context of comments. - -Additionally supported in JSON-C... TRAILING COMMAS! - -VSCode and TypeScript use jsonc for their config files, so we're already using it in a lot of places, but we're going to start using it in more places too. This package is implemented in vanilla JS with a vendored copy of [`strip-json-comments`](https://github.com/sindresorhus/strip-json-comments) so that we can use this code from kbn_pm without node modules installed. - -## API - -### parse(jsonc: string): any - -Parses a JSON-C string into the value defined by the content. \ No newline at end of file diff --git a/packages/kbn-kibana-manifest-parser/README.mdx b/packages/kbn-kibana-manifest-parser/README.mdx deleted file mode 100644 index 52467e94cdcec..0000000000000 --- a/packages/kbn-kibana-manifest-parser/README.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: kibDevDocsOpsKibanaManifestParser -slug: /kibana-dev-docs/ops/kibana-manifest-parser -title: "@kbn/kibana-manifest-parser" -description: A package for parsing Kibana package manifest files -date: 2022-05-24 -tags: ['kibana', 'dev', 'contributor', 'operations', 'package', 'kibana.json', 'kibana.jsonc'] ---- - -This package exposes functions and types for parsing Kibana manifest files (in the new `kibana.jsonc` format) - -## API - -### `parseKibanaManifest(jsonc: string): KibanaPackageManifest` - -Parses a JSON-C string into a valid `KibanaPackageManifest` object. If the manifest is invalid an error is thrown. - -### `readKibanaManifest(path: string): KibanaPackageManifest` - -Read a Kibana manifest from disk and parse it, returning a KibanaPackageManifest. If the file doesn't exist or is invalid in some way an error is thrown. - -### `validateKibanaManifest(value: unknown): KibanaPackageManifest` - -Validate a parsed Kibana manifest. If the manifest is invalid an error is thrown. diff --git a/packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts b/packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts deleted file mode 100644 index 2f99b0293d8a2..0000000000000 --- a/packages/kbn-kibana-manifest-parser/src/kibana_manifest.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 and the Server Side Public License, v 1; you may 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 KibanaPackageType = - | 'plugin-browser' - | 'plugin-server' - | 'shared-browser' - | 'shared-server' - | 'shared-common' - | 'shared-scss' - | 'functional-tests' - | 'test-helper'; - -interface PackageManifestBaseFields { - type: KibanaPackageType; - id: string; - owner: string; - typeDeps: string[]; - runtimeDeps: string[]; -} - -export interface PluginPackageManifest extends PackageManifestBaseFields { - type: 'plugin-browser' | 'plugin-server'; - plugin: { - id: string; - configPath?: string[]; - requiredPlugins?: string[]; - optionalPlugins?: string[]; - description?: string; - enabledOnAnonymousPages?: boolean; - serviceFolders?: string[]; - }; -} - -export interface SharedBrowserPackageManifest extends PackageManifestBaseFields { - type: 'shared-browser' | 'shared-common'; - sharedBrowserBundle?: boolean; -} - -export interface BasePackageManifest extends PackageManifestBaseFields { - type: 'shared-server' | 'functional-tests' | 'test-helper' | 'shared-scss'; -} - -export type KibanaPackageManifest = - | PluginPackageManifest - | SharedBrowserPackageManifest - | BasePackageManifest; diff --git a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts deleted file mode 100644 index 4743adb1f5e48..0000000000000 --- a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts +++ /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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { validateKibanaManifest } from './parse_kibana_manifest'; - -const BASE_FIELDS = { - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [], -}; - -describe('validateKibanaManifest', () => { - it('requires valid type', () => { - expect(() => validateKibanaManifest({})).toThrowErrorMatchingInlineSnapshot( - `"invalid package \\"type\\", options are [functional-tests, plugin-browser, plugin-server, shared-browser, shared-common, shared-server, test-helper, shared-scss]"` - ); - }); - - it('requires valid id', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid package \\"id\\", must be a string that starts with @kbn/"` - ); - }); - - it('requires valid owner', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid package \\"owner\\", must be a valid Github team handle starting with @"` - ); - }); - - it('requires valid typeDeps', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - }) - ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); - - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: false, - }) - ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); - - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [1], - }) - ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); - }); - - it('requires valid runtimeDeps', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid \\"runtimeDeps\\", must be an array of strings"` - ); - - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: false, - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid \\"runtimeDeps\\", must be an array of strings"` - ); - - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [1], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid \\"runtimeDeps\\", must be an array of strings"` - ); - }); - - it('validates base types', () => { - expect( - validateKibanaManifest({ - type: 'shared-server', - ...BASE_FIELDS, - }) - ).toMatchInlineSnapshot(` - Object { - "id": "@kbn/foo", - "owner": "@elastic/kibana-operations", - "runtimeDeps": Array [], - "type": "shared-server", - "typeDeps": Array [], - } - `); - expect( - validateKibanaManifest({ - type: 'functional-tests', - ...BASE_FIELDS, - }) - ).toMatchInlineSnapshot(` - Object { - "id": "@kbn/foo", - "owner": "@elastic/kibana-operations", - "runtimeDeps": Array [], - "type": "functional-tests", - "typeDeps": Array [], - } - `); - expect( - validateKibanaManifest({ - type: 'test-helper', - ...BASE_FIELDS, - }) - ).toMatchInlineSnapshot(` - Object { - "id": "@kbn/foo", - "owner": "@elastic/kibana-operations", - "runtimeDeps": Array [], - "type": "test-helper", - "typeDeps": Array [], - } - `); - }); - - describe('plugin-* types', () => { - it('requires valid plugin for plugin-* types', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [], - }) - ).toThrowErrorMatchingInlineSnapshot(`"invalid package \\"plugin\\", must be an object"`); - }); - - it('requires "id" in plugins', () => { - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [], - plugin: {}, - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid \\"plugin.id\\", must be a string in camel or snake case"` - ); - - expect(() => - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [], - plugin: { - id: 'not-camel-case', - }, - }) - ).toThrowErrorMatchingInlineSnapshot( - `"invalid \\"plugin.id\\", must be a string in camel or snake case"` - ); - - expect( - validateKibanaManifest({ - type: 'plugin-browser', - id: '@kbn/foo', - owner: '@elastic/kibana-operations', - typeDeps: [], - runtimeDeps: [], - plugin: { - id: 'camelCase', - }, - }) - ).toMatchInlineSnapshot(` - Object { - "id": "@kbn/foo", - "owner": "@elastic/kibana-operations", - "plugin": Object { - "configPath": undefined, - "description": undefined, - "enabledOnAnonymousPages": undefined, - "id": "camelCase", - "optionalPlugins": undefined, - "requiredPlugins": undefined, - "serviceFolders": undefined, - }, - "runtimeDeps": Array [], - "type": "plugin-browser", - "typeDeps": Array [], - } - `); - }); - }); -}); diff --git a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts deleted file mode 100644 index aeccd08e6d8c0..0000000000000 --- a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import Fs from 'fs'; - -import { parse } from '@kbn/jsonc'; -import { PluginPackageManifest, KibanaPackageManifest } from './kibana_manifest'; - -import { - isObj, - isValidId, - isValidPkgType, - isArrOfIds, - isArrOfStrings, - PACKAGE_TYPES, -} from './util'; - -const err = (msg: string) => new Error(msg); - -function validateKibanaManifestPlugin(plugin: unknown): PluginPackageManifest['plugin'] { - if (!isObj(plugin)) { - throw err(`invalid package "plugin", must be an object`); - } - - const { - id, - configPath, - requiredPlugins, - optionalPlugins, - description, - enabledOnAnonymousPages, - serviceFolders, - ...extra - } = plugin; - - const extraKeys = Object.keys(extra); - if (extraKeys.length) { - throw err(`unexpected keys in "plugin" of package [${extraKeys.join(', ')}]`); - } - - if (typeof id !== 'string' || !isValidId(id)) { - throw err(`invalid "plugin.id", must be a string in camel or snake case`); - } - - if (configPath !== undefined && !isArrOfIds(configPath)) { - throw err(`invalid "plugin.configPath", must be an array of strings in camel or snake case`); - } - - if (requiredPlugins !== undefined && !isArrOfIds(requiredPlugins)) { - throw err( - `invalid "plugin.requiredPlugins", must be an array of strings in camel or snake case` - ); - } - - if (optionalPlugins !== undefined && !isArrOfIds(optionalPlugins)) { - throw err( - `invalid "plugin.requiredPlugins", must be an array of strings in camel or snake case` - ); - } - - if (description !== undefined && typeof description !== 'string') { - throw err(`invalid "plugin.description", must be a string`); - } - - if (enabledOnAnonymousPages !== undefined && typeof enabledOnAnonymousPages !== 'boolean') { - throw err(`invalid "plugin.enabledOnAnonymousPages", must be a boolean`); - } - - if (serviceFolders !== undefined && !isArrOfStrings(serviceFolders)) { - throw err(`invalid "plugin.serviceFolders", must be an array of strings`); - } - - return { - id, - configPath, - requiredPlugins, - optionalPlugins, - description, - enabledOnAnonymousPages, - serviceFolders, - }; -} - -/** - * Validate the contents of a parsed kibana.jsonc file. - */ -export function validateKibanaManifest(parsed: unknown): KibanaPackageManifest { - if (!isObj(parsed)) { - throw err('expected root value to be an object'); - } - - const { type, id, owner, typeDeps, runtimeDeps, plugin, sharedBrowserBundle, ...extra } = parsed; - - const extraKeys = Object.keys(extra); - if (extraKeys.length) { - throw err(`unexpected keys in package manifest [${extraKeys.join(', ')}]`); - } - - if (!isValidPkgType(type)) { - throw err(`invalid package "type", options are [${PACKAGE_TYPES.join(', ')}]`); - } - - if (typeof id !== 'string' || !id.startsWith('@kbn/')) { - throw err(`invalid package "id", must be a string that starts with @kbn/`); - } - - if (typeof owner !== 'string' || !owner.startsWith('@')) { - throw err(`invalid package "owner", must be a valid Github team handle starting with @`); - } - - if (!isArrOfStrings(typeDeps)) { - throw err(`invalid "typeDeps", must be an array of strings`); - } - - if (!isArrOfStrings(runtimeDeps)) { - throw err(`invalid "runtimeDeps", must be an array of strings`); - } - - const base = { - id, - owner, - typeDeps, - runtimeDeps, - }; - - // return if this is one of the more basic types of package types - if (type === 'shared-server' || type === 'functional-tests' || type === 'test-helper') { - return { - type, - ...base, - }; - } - - // handle the plugin field for plugin-* types - if (type === 'plugin-browser' || type === 'plugin-server') { - return { - type, - ...base, - plugin: validateKibanaManifestPlugin(plugin), - }; - } - - // parse the sharedBrowserBundle for shared-browser and shared-common types - if (sharedBrowserBundle !== undefined && typeof sharedBrowserBundle !== 'boolean') { - throw err(`invalid "sharedBrowserBundle" field, expected undefined or a boolean`); - } - return { - type, - ...base, - sharedBrowserBundle, - }; -} - -/** - * Parse a kibana.jsonc file from the filesystem - */ -export function readKibanaManifest(path: string) { - let content; - try { - content = Fs.readFileSync(path, 'utf8'); - } catch (error) { - if (error.code === 'ENOENT') { - throw err(`Missing kibana.json file at ${path}`); - } - - throw error; - } - - return parseKibanaManifest(content); -} - -/** - * Parse a kibana.jsonc file from a string - */ -export function parseKibanaManifest(content: string) { - let parsed; - try { - parsed = parse(content); - } catch (error) { - throw err(`Invalid JSONc: ${error.message}`); - } - - return validateKibanaManifest(parsed); -} diff --git a/packages/kbn-kibana-manifest-parser/src/util.ts b/packages/kbn-kibana-manifest-parser/src/util.ts deleted file mode 100644 index 07b6a6c40a91e..0000000000000 --- a/packages/kbn-kibana-manifest-parser/src/util.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { KibanaPackageType } from './kibana_manifest'; - -export const ID_PATTERN = /^[a-z][a-zA-Z_]*$/; - -export function isObj(v: unknown): v is Record { - return typeof v === 'object' && v !== null; -} - -export const isArrOfStrings = (v: unknown): v is string[] => - Array.isArray(v) && v.every((i) => typeof i === 'string'); - -export const isValidId = (id: string) => ID_PATTERN.test(id); - -export const isArrOfIds = (v: unknown): v is string[] => isArrOfStrings(v) && v.every(isValidId); - -/** - * This weird map allows us to ensure that every value in the - * `KibanaPackageType` union is represented because the mapped - * type requires that the `PACKAGE_TYPE_MAP` map has a property - * matching every value in the union. - */ -const PACKAGE_TYPE_MAP: { [k in KibanaPackageType]: true } = { - 'functional-tests': true, - 'plugin-browser': true, - 'plugin-server': true, - 'shared-browser': true, - 'shared-common': true, - 'shared-server': true, - 'test-helper': true, - 'shared-scss': true, -}; - -export const PACKAGE_TYPES = Object.keys(PACKAGE_TYPE_MAP) as KibanaPackageType[]; - -export const isValidPkgType = (type: unknown): type is keyof typeof PACKAGE_TYPE_MAP => - typeof type === 'string' && Object.hasOwn(PACKAGE_TYPE_MAP, type); diff --git a/packages/kbn-kibana-manifest-schema/README.mdx b/packages/kbn-kibana-manifest-schema/README.mdx index 96def9e65ee73..176c960d49d82 100644 --- a/packages/kbn-kibana-manifest-schema/README.mdx +++ b/packages/kbn-kibana-manifest-schema/README.mdx @@ -1,7 +1,7 @@ --- id: kibDevDocsOpsKibanaManifestSchema slug: /kibana-dev-docs/ops/kibana-manifest-schema -title: "@kbn/jsonc" +title: "@kbn/kibana-manifest-schema" description: The JSON schema for Kibana manifest files date: 2022-05-24 tags: ['kibana', 'dev', 'contributor', 'operations', 'json', 'schema', 'manifest'] diff --git a/packages/kbn-kibana-manifest-schema/kibana.jsonc b/packages/kbn-kibana-manifest-schema/kibana.jsonc new file mode 100644 index 0000000000000..5ddba1f9529a7 --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/kibana-manifest-schema", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts index ca02e48f00e2d..b665808837121 100644 --- a/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts +++ b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts @@ -13,11 +13,18 @@ export const PLUGIN_ID_PATTERN = /^[a-z][a-zA-Z_]*$/; export const MANIFEST_V2: JSONSchema = { type: 'object', - required: ['id', 'type', 'owner', 'typeDependencies', 'runtimeDependencies'], + required: ['id', 'type', 'owner', 'typeDeps', 'runtimeDeps'], + // @ts-expect-error VSCode specific JSONSchema extension + allowTrailingCommas: true, properties: { id: { type: 'string', pattern: '^@kbn/', + description: desc` + Module ID for this package. This must be globbally unique amoungst all + packages and should include the most important information about how this + package should be used. Avoid generic names to aid in disambiguation. + `, }, owner: { type: 'string', @@ -25,12 +32,11 @@ export const MANIFEST_V2: JSONSchema = { Github handle for the person or team who is responsible for this package. This owner will be used in the codeowners files for this package. - For additional codeowners, you add additional entries at the end of the - codeowners file. + For additional codeowners, you add manually add entries to the codeowners file. `, pattern: '^@', }, - typeDependencies: { + typeDeps: { type: 'array', description: desc` Packages which are required for the source code in the package to be @@ -40,7 +46,7 @@ export const MANIFEST_V2: JSONSchema = { type: 'string', }, }, - runtimeDependencies: { + runtimeDeps: { type: 'array', description: desc` Packages which are required for the source code in the package to run. This list @@ -50,6 +56,14 @@ export const MANIFEST_V2: JSONSchema = { type: 'string', }, }, + devOnly: { + type: 'boolean', + description: desc` + A devOnly package can be used by other devOnly packages and only by other devOnly + packages and will never be included in the distributable. + `, + default: false, + }, }, oneOf: [ { diff --git a/packages/kbn-logging-mocks/kibana.jsonc b/packages/kbn-logging-mocks/kibana.jsonc new file mode 100644 index 0000000000000..6b95f3a750f28 --- /dev/null +++ b/packages/kbn-logging-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/logging-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-logging/kibana.jsonc b/packages/kbn-logging/kibana.jsonc new file mode 100644 index 0000000000000..ab4df8442093b --- /dev/null +++ b/packages/kbn-logging/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/logging", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-managed-vscode-config-cli/kibana.jsonc b/packages/kbn-managed-vscode-config-cli/kibana.jsonc new file mode 100644 index 0000000000000..1cbb5cb7ce7cb --- /dev/null +++ b/packages/kbn-managed-vscode-config-cli/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/managed-vscode-config-cli", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-managed-vscode-config-cli/package.json b/packages/kbn-managed-vscode-config-cli/package.json index c1da1e53dbc04..ba4086c773eee 100644 --- a/packages/kbn-managed-vscode-config-cli/package.json +++ b/packages/kbn-managed-vscode-config-cli/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-managed-vscode-config/kibana.jsonc b/packages/kbn-managed-vscode-config/kibana.jsonc new file mode 100644 index 0000000000000..c973127eb5485 --- /dev/null +++ b/packages/kbn-managed-vscode-config/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/managed-vscode-config", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-managed-vscode-config/package.json b/packages/kbn-managed-vscode-config/package.json index 84de677b6f378..cc337813a7300 100644 --- a/packages/kbn-managed-vscode-config/package.json +++ b/packages/kbn-managed-vscode-config/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-mapbox-gl/kibana.jsonc b/packages/kbn-mapbox-gl/kibana.jsonc new file mode 100644 index 0000000000000..35ffb25b0b115 --- /dev/null +++ b/packages/kbn-mapbox-gl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/mapbox-gl", + "owner": "@elastic/kibana-gis", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-monaco/kibana.jsonc b/packages/kbn-monaco/kibana.jsonc new file mode 100644 index 0000000000000..42524f21bd542 --- /dev/null +++ b/packages/kbn-monaco/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/monaco", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-optimizer-webpack-helpers/kibana.jsonc b/packages/kbn-optimizer-webpack-helpers/kibana.jsonc new file mode 100644 index 0000000000000..102818ed032c2 --- /dev/null +++ b/packages/kbn-optimizer-webpack-helpers/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/optimizer-webpack-helpers", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-optimizer-webpack-helpers/package.json b/packages/kbn-optimizer-webpack-helpers/package.json index ca65edcf9d017..a37b5ba48ee48 100644 --- a/packages/kbn-optimizer-webpack-helpers/package.json +++ b/packages/kbn-optimizer-webpack-helpers/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-optimizer/kibana.jsonc b/packages/kbn-optimizer/kibana.jsonc new file mode 100644 index 0000000000000..945fdc1e0366c --- /dev/null +++ b/packages/kbn-optimizer/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/optimizer", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 71abf16bb8fcd..511d230ec968d 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -1,23 +1,56 @@ pageLoadAssetSize: - advancedSettings: 27596 actions: 20000 + advancedSettings: 27596 aiops: 10000 alerting: 106936 apm: 64385 + banners: 17946 + bfetch: 22837 canvas: 1066647 + cases: 144442 charts: 55000 cloud: 21076 + cloudSecurityPosture: 19109 console: 46091 + controls: 40000 core: 435325 crossClusterReplication: 65408 + customIntegrations: 28810 dashboard: 82025 dashboardEnhanced: 65646 + data: 454087 + dataViewEditor: 12000 + dataViewFieldEditor: 27000 + dataViewManagement: 5000 + dataViews: 44532 + dataVisualizer: 27530 devTools: 38637 discover: 99999 discoverEnhanced: 42730 + embeddable: 87309 + embeddableEnhanced: 22107 enterpriseSearch: 35741 esUiShared: 326654 + eventAnnotation: 19334 + expressionError: 22127 + expressionGauge: 25000 + expressionHeatmap: 27505 + expressionImage: 19288 + expressionLegacyMetricVis: 23121 + expressionMetric: 22238 + expressionMetricVis: 23121 + expressionPartitionVis: 26338 + expressionRepeatImage: 22341 + expressionRevealImage: 25675 + expressions: 140958 + expressionShape: 34008 + expressionTagcloud: 27505 + expressionXY: 38000 features: 21723 + fieldFormats: 65209 + files: 22673 + fileUpload: 25664 + fleet: 126917 globalSearch: 29696 globalSearchBar: 50403 globalSearchProviders: 25554 @@ -27,11 +60,15 @@ pageLoadAssetSize: indexLifecycleManagement: 107090 indexManagement: 140608 infra: 184320 - fleet: 126917 ingestPipelines: 58003 inputControlVis: 172675 inspector: 148711 + interactiveSetup: 80000 kibanaOverview: 56279 + kibanaReact: 74422 + kibanaUsageCollection: 16463 + kibanaUtils: 79713 + kubernetesSecurity: 77234 lens: 36000 licenseManagement: 41817 licensing: 29004 @@ -39,31 +76,57 @@ pageLoadAssetSize: logstash: 53548 management: 46112 maps: 90000 + mapsEms: 26072 ml: 82187 monitoring: 80000 navigation: 37269 newsfeed: 42228 observability: 95000 + osquery: 107090 painlessLab: 179748 + presentationUtil: 58834 remoteClusters: 51327 + reporting: 57003 rollup: 97204 + runtimeFields: 41752 savedObjects: 108518 + savedObjectsFinder: 21691 savedObjectsManagement: 101836 savedObjectsTagging: 59482 savedObjectsTaggingOss: 20590 + savedSearch: 16225 + screenshotMode: 17856 + screenshotting: 22870 searchprofiler: 67080 security: 65433 + securitySolution: 273763 + sessionView: 77750 + share: 71239 snapshotRestore: 79032 spaces: 57868 + stackAlerts: 29684 + synthetics: 40958 telemetry: 51957 telemetryManagementSection: 38586 + threatIntelligence: 29195 + timelines: 327300 transform: 41007 triggersActionsUi: 119000 + uiActions: 35121 + uiActionsEnhanced: 38494 + unifiedFieldList: 65500 + unifiedSearch: 71059 upgradeAssistant: 81241 + urlDrilldown: 30063 urlForwarding: 32579 usageCollection: 39762 + ux: 20784 visDefaultEditor: 50178 + visTypeGauge: 24113 + visTypeHeatmap: 25340 visTypeMarkdown: 30896 + visTypeMetric: 23332 + visTypePie: 35583 visTypeTable: 94934 visTypeTagcloud: 37575 visTypeTimelion: 68883 @@ -73,66 +136,3 @@ pageLoadAssetSize: visTypeXy: 30000 visualizations: 90000 watcher: 43598 - runtimeFields: 41752 - stackAlerts: 29684 - presentationUtil: 58834 - osquery: 107090 - fileUpload: 25664 - dataVisualizer: 27530 - banners: 17946 - mapsEms: 26072 - timelines: 327300 - screenshotMode: 17856 - visTypePie: 35583 - expressionRevealImage: 25675 - cases: 144442 - expressionError: 22127 - expressionRepeatImage: 22341 - expressionImage: 19288 - expressionMetric: 22238 - expressionShape: 34008 - interactiveSetup: 80000 - expressionTagcloud: 27505 - securitySolution: 273763 - customIntegrations: 28810 - expressionMetricVis: 23121 - expressionLegacyMetricVis: 23121 - expressionHeatmap: 27505 - visTypeMetric: 23332 - bfetch: 22837 - kibanaUtils: 79713 - dataViews: 44532 - expressions: 140958 - fieldFormats: 65209 - kibanaReact: 74422 - share: 71239 - uiActions: 35121 - embeddable: 87309 - embeddableEnhanced: 22107 - uiActionsEnhanced: 38494 - urlDrilldown: 30063 - dataViewEditor: 12000 - dataViewFieldEditor: 27000 - dataViewManagement: 5000 - reporting: 57003 - visTypeHeatmap: 25340 - expressionGauge: 25000 - controls: 40000 - expressionPartitionVis: 26338 - savedSearch: 16225 - ux: 20784 - sessionView: 77750 - cloudSecurityPosture: 19109 - visTypeGauge: 24113 - unifiedSearch: 71059 - unifiedFieldList: 65500 - data: 454087 - eventAnnotation: 19334 - screenshotting: 22870 - synthetics: 40958 - expressionXY: 36000 - kibanaUsageCollection: 16463 - kubernetesSecurity: 77234 - threatIntelligence: 29195 - files: 22673 - savedObjectsFinder: 21691 diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 4d3d17c5242c6..a7d8a50927634 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "./target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index 449c58fdee218..974bd4dbbcbc0 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -168,7 +168,7 @@ export function runKbnOptimizerCli(options: { defaultLimitsPath: string }) { updateBundleLimits({ log, config, - dropMissing: !(focus || filter), + dropMissing: !(focus.length || filter.length), limitsPath, }); } diff --git a/packages/kbn-optimizer/src/limits.ts b/packages/kbn-optimizer/src/limits.ts index bd7ed501d3a5f..e4d3d24feeff9 100644 --- a/packages/kbn-optimizer/src/limits.ts +++ b/packages/kbn-optimizer/src/limits.ts @@ -71,6 +71,28 @@ export function validateLimitsForAllBundles( ); } + const sorted = limitBundleIds + .slice() + .sort((a, b) => a.localeCompare(b)) + .every((key, i) => limitBundleIds[i] === key); + if (!sorted) { + throw createFailError( + dedent` + The limits defined in packages/kbn-optimizer/limits.yml are not sorted correctly. To make + sure the file is automatically updatedable without dozens of extra changes, the keys in this + file must be sorted. + + Please sort the keys alphabetically or, to automatically update the limits file locally run: + + node scripts/build_kibana_platform_plugins.js --update-limits + + To validate your changes locally run: + + node scripts/build_kibana_platform_plugins.js --validate-limits + ` + '\n' + ); + } + log.success('limits.yml file valid'); } diff --git a/packages/kbn-performance-testing-dataset-extractor/kibana.jsonc b/packages/kbn-performance-testing-dataset-extractor/kibana.jsonc new file mode 100644 index 0000000000000..f09d991b49ec1 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/performance-testing-dataset-extractor", + "devOnly": true, + "owner": "@elastic/kibana-performance-testing", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-performance-testing-dataset-extractor/package.json b/packages/kbn-performance-testing-dataset-extractor/package.json index 4d637728b28de..12073ed76f3ea 100644 --- a/packages/kbn-performance-testing-dataset-extractor/package.json +++ b/packages/kbn-performance-testing-dataset-extractor/package.json @@ -4,8 +4,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-plugin-discovery/kibana.jsonc b/packages/kbn-plugin-discovery/kibana.jsonc new file mode 100644 index 0000000000000..8e6ad8a0c35de --- /dev/null +++ b/packages/kbn-plugin-discovery/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/plugin-discovery", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-plugin-generator/kibana.jsonc b/packages/kbn-plugin-generator/kibana.jsonc new file mode 100644 index 0000000000000..1045f43539324 --- /dev/null +++ b/packages/kbn-plugin-generator/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/plugin-generator", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json index 0b6c11709b90e..28b7e849ab3c1 100644 --- a/packages/kbn-plugin-generator/package.json +++ b/packages/kbn-plugin-generator/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-plugin-helpers/kibana.jsonc b/packages/kbn-plugin-helpers/kibana.jsonc new file mode 100644 index 0000000000000..84a87720dab0c --- /dev/null +++ b/packages/kbn-plugin-helpers/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/plugin-helpers", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index cac041762f739..206b3e77b39af 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -4,9 +4,6 @@ "private": true, "description": "Just some helpers for kibana plugin devs.", "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - }, "main": "target_node/index.js", "bin": { "plugin-helpers": "bin/plugin-helpers.js" diff --git a/packages/kbn-react-field/kibana.jsonc b/packages/kbn-react-field/kibana.jsonc new file mode 100644 index 0000000000000..aade3b0242974 --- /dev/null +++ b/packages/kbn-react-field/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/react-field", + "owner": "@elastic/kibana-app-services", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-repo-source-classifier-cli/kibana.jsonc b/packages/kbn-repo-source-classifier-cli/kibana.jsonc new file mode 100644 index 0000000000000..a93259974364a --- /dev/null +++ b/packages/kbn-repo-source-classifier-cli/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/repo-source-classifier-cli", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-repo-source-classifier-cli/package.json b/packages/kbn-repo-source-classifier-cli/package.json index 47ca9f6598f93..a8e0cea71bef6 100644 --- a/packages/kbn-repo-source-classifier-cli/package.json +++ b/packages/kbn-repo-source-classifier-cli/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-repo-source-classifier/kibana.jsonc b/packages/kbn-repo-source-classifier/kibana.jsonc new file mode 100644 index 0000000000000..edeb2d3c64a39 --- /dev/null +++ b/packages/kbn-repo-source-classifier/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/repo-source-classifier", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-repo-source-classifier/package.json b/packages/kbn-repo-source-classifier/package.json index fce2be1b056ed..a6f81d992b285 100644 --- a/packages/kbn-repo-source-classifier/package.json +++ b/packages/kbn-repo-source-classifier/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-rule-data-utils/kibana.jsonc b/packages/kbn-rule-data-utils/kibana.jsonc new file mode 100644 index 0000000000000..0fe42f6cb6e3d --- /dev/null +++ b/packages/kbn-rule-data-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/rule-data-utils", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-safer-lodash-set/kibana.jsonc b/packages/kbn-safer-lodash-set/kibana.jsonc new file mode 100644 index 0000000000000..8d7c5dfbb6bb3 --- /dev/null +++ b/packages/kbn-safer-lodash-set/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/safer-lodash-set", + "owner": "@elastic/kibana-security", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-autocomplete/kibana.jsonc b/packages/kbn-securitysolution-autocomplete/kibana.jsonc new file mode 100644 index 0000000000000..fbf73ddf07fb4 --- /dev/null +++ b/packages/kbn-securitysolution-autocomplete/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-autocomplete", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-es-utils/kibana.jsonc b/packages/kbn-securitysolution-es-utils/kibana.jsonc new file mode 100644 index 0000000000000..a798aefeae37d --- /dev/null +++ b/packages/kbn-securitysolution-es-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-es-utils", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-hook-utils/kibana.jsonc b/packages/kbn-securitysolution-hook-utils/kibana.jsonc new file mode 100644 index 0000000000000..cd7d23f07792d --- /dev/null +++ b/packages/kbn-securitysolution-hook-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-hook-utils", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc b/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc new file mode 100644 index 0000000000000..d1e730f414d18 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-io-ts-alerting-types", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc b/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc new file mode 100644 index 0000000000000..d50df0b0d6512 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-io-ts-list-types", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts index 730c1f2cd491d..f7e533aa7d4cd 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts @@ -47,6 +47,7 @@ export * from './os_type'; export * from './page'; export * from './per_page'; export * from './pit'; +export * from './search'; export * from './search_after'; export * from './serializer'; export * from './sort_field'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts new file mode 100644 index 0000000000000..99d73fd5e059a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; +import { searchOrUndefined } from '.'; + +import * as t from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('search', () => { + test('it will validate a correct search', () => { + const payload = 'name:foo'; + const decoded = searchOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will validate with the value of "undefined"', () => { + const obj = t.exact( + t.type({ + search: searchOrUndefined, + }) + ); + const payload: t.TypeOf = { + search: undefined, + }; + const decoded = obj.decode({ + pit_id: undefined, + }); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect search', () => { + const payload = ['foo']; + const decoded = searchOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["foo"]" supplied to "(string | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts new file mode 100644 index 0000000000000..319c225edf007 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const search = t.string; +export type Search = t.TypeOf; + +export const searchOrUndefined = t.union([search, t.undefined]); +export type SearchOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts index 8f64dccf6d577..4026d878ca278 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts @@ -16,6 +16,7 @@ export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchem namespace_type: NAMESPACE_TYPE, page: '1', per_page: '25', + search: undefined, sort_field: undefined, sort_order: undefined, }); @@ -26,6 +27,7 @@ export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListI namespace_type: 'single,single,agnostic', page: '1', per_page: '25', + search: undefined, sort_field: undefined, sort_order: undefined, }); @@ -37,6 +39,7 @@ export const getFindExceptionListItemSchemaDecodedMock = namespace_type: [NAMESPACE_TYPE], page: 1, per_page: 25, + search: undefined, sort_field: undefined, sort_order: undefined, }); @@ -48,6 +51,7 @@ export const getFindExceptionListItemSchemaDecodedMultipleMock = namespace_type: ['single', 'single', 'agnostic'], page: 1, per_page: 25, + search: undefined, sort_field: undefined, sort_order: undefined, }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts index 04afee30c1ab3..fab9111aa0eb0 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts @@ -55,6 +55,7 @@ describe('find_list_item_schema', () => { namespace_type: ['single'], page: undefined, per_page: undefined, + search: undefined, sort_field: undefined, sort_order: undefined, }; @@ -73,7 +74,7 @@ describe('find_list_item_schema', () => { expect(message.schema).toEqual(expected); }); - test('it should validate with pre_page missing', () => { + test('it should validate with per_page missing', () => { const payload = getFindExceptionListItemSchemaMock(); delete payload.per_page; const decoded = findExceptionListItemSchema.decode(payload); @@ -123,6 +124,18 @@ describe('find_list_item_schema', () => { expect(message.schema).toEqual(expected); }); + test('it should validate with search missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.search; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.search; + expect(message.schema).toEqual(expected); + }); + test('it should not allow an extra key to be sent in', () => { const payload: FindExceptionListItemSchema & { extraKey: string; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts index 88756ac0eb301..0ca8140d048dc 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts @@ -21,6 +21,7 @@ import { import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; import { sort_field } from '../../common/sort_field'; import { sort_order } from '../../common/sort_order'; +import { search } from '../../common/search'; export const findExceptionListItemSchema = t.intersection([ t.exact( @@ -34,6 +35,7 @@ export const findExceptionListItemSchema = t.intersection([ namespace_type: DefaultNamespaceArray, // defaults to ['single'] if not set during decode page: StringToPositiveNumber, // defaults to undefined if not set during decode per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + search, sort_field, // defaults to undefined if not set during decode sort_order, // defaults to undefined if not set during decode }) diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts index a5eb4f976debd..b8f03629135a2 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts @@ -99,10 +99,10 @@ export interface ExceptionListIdentifiers { export interface ApiCallFindListsItemsMemoProps { lists: ExceptionListIdentifiers[]; - filterOptions: FilterExceptionsOptions[]; pagination: Partial; showDetectionsListsOnly: boolean; showEndpointListsOnly: boolean; + filter?: string; onError: (arg: string[]) => void; onSuccess: (arg: UseExceptionListItemsSuccess) => void; } @@ -168,8 +168,9 @@ export interface ApiCallByListIdProps { http: HttpStart; listIds: string[]; namespaceTypes: NamespaceType[]; - filterOptions: FilterExceptionsOptions[]; pagination: Partial; + search?: string; + filter?: string; signal: AbortSignal; } diff --git a/packages/kbn-securitysolution-io-ts-types/kibana.jsonc b/packages/kbn-securitysolution-io-ts-types/kibana.jsonc new file mode 100644 index 0000000000000..6ef8a21b00e1e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-io-ts-types", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-io-ts-utils/kibana.jsonc b/packages/kbn-securitysolution-io-ts-utils/kibana.jsonc new file mode 100644 index 0000000000000..2c86eea21c0c1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-io-ts-utils", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-list-api/kibana.jsonc b/packages/kbn-securitysolution-list-api/kibana.jsonc new file mode 100644 index 0000000000000..b162805a8c8b4 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-list-api", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-list-api/src/api/index.ts b/packages/kbn-securitysolution-list-api/src/api/index.ts index a0361d044977c..871987ff7d367 100644 --- a/packages/kbn-securitysolution-list-api/src/api/index.ts +++ b/packages/kbn-securitysolution-list-api/src/api/index.ts @@ -34,8 +34,6 @@ import { import { ENDPOINT_LIST_URL, EXCEPTION_LIST_ITEM_URL, - EXCEPTION_LIST_NAMESPACE, - EXCEPTION_LIST_NAMESPACE_AGNOSTIC, EXCEPTION_LIST_URL, } from '@kbn/securitysolution-list-constants'; import { toError, toPromise } from '../fp_utils'; @@ -324,7 +322,8 @@ export { fetchExceptionListByIdWithValidation as fetchExceptionListById }; * @param http Kibana http service * @param listIds ExceptionList list_ids (not ID) * @param namespaceTypes ExceptionList namespace_types - * @param filterOptions optional - filter by field or tags + * @param search optional - simple search string + * @param filter optional * @param pagination optional * @param signal to cancel request * @@ -334,36 +333,20 @@ const fetchExceptionListsItemsByListIds = async ({ http, listIds, namespaceTypes, - filterOptions, + filter, pagination, + search, signal, }: ApiCallByListIdProps): Promise => { - const filters: string = filterOptions - .map((filter, index) => { - const namespace = namespaceTypes[index]; - const filterNamespace = - namespace === 'agnostic' ? EXCEPTION_LIST_NAMESPACE_AGNOSTIC : EXCEPTION_LIST_NAMESPACE; - const formattedFilters = [ - ...(filter.filter.length - ? [`${filterNamespace}.attributes.entries.field:${filter.filter}*`] - : []), - ...(filter.tags.length - ? filter.tags.map((t) => `${filterNamespace}.attributes.tags:${t}`) - : []), - ]; - - return formattedFilters.join(' AND '); - }) - .join(','); - const query = { list_id: listIds.join(','), namespace_type: namespaceTypes.join(','), page: pagination.page ? `${pagination.page}` : '1', per_page: pagination.perPage ? `${pagination.perPage}` : '20', + search, sort_field: 'exception-list.created_at', sort_order: 'desc', - ...(filters.trim() !== '' ? { filter: filters } : {}), + filter, }; return http.fetch(`${EXCEPTION_LIST_ITEM_URL}/_find`, { @@ -374,11 +357,12 @@ const fetchExceptionListsItemsByListIds = async ({ }; const fetchExceptionListsItemsByListIdsWithValidation = async ({ - filterOptions, + filter, http, listIds, namespaceTypes, pagination, + search, signal, }: ApiCallByListIdProps): Promise => flow( @@ -386,11 +370,12 @@ const fetchExceptionListsItemsByListIdsWithValidation = async ({ tryCatch( () => fetchExceptionListsItemsByListIds({ - filterOptions, + filter, http, listIds, namespaceTypes, pagination, + search, signal, }), toError diff --git a/packages/kbn-securitysolution-list-constants/kibana.jsonc b/packages/kbn-securitysolution-list-constants/kibana.jsonc new file mode 100644 index 0000000000000..ffe606ca6ade8 --- /dev/null +++ b/packages/kbn-securitysolution-list-constants/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-list-constants", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-list-hooks/index.ts b/packages/kbn-securitysolution-list-hooks/index.ts index 4c523fe577211..c2469bc4c4948 100644 --- a/packages/kbn-securitysolution-list-hooks/index.ts +++ b/packages/kbn-securitysolution-list-hooks/index.ts @@ -10,7 +10,6 @@ export * from './src/use_api'; export * from './src/use_create_list_index'; export * from './src/use_cursor'; export * from './src/use_delete_list'; -export * from './src/use_exception_list_items'; export * from './src/use_exception_lists'; export * from './src/use_export_list'; export * from './src/use_find_lists'; diff --git a/packages/kbn-securitysolution-list-hooks/kibana.jsonc b/packages/kbn-securitysolution-list-hooks/kibana.jsonc new file mode 100644 index 0000000000000..12d670f46ae32 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-list-hooks", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-list-hooks/src/index.ts b/packages/kbn-securitysolution-list-hooks/src/index.ts new file mode 100644 index 0000000000000..e458abd0448ad --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/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 and the Server Side Public License, v 1; you may 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 './transforms'; +export * from './use_api'; +export * from './use_create_list_index'; +export * from './use_cursor'; +export * from './use_delete_list'; +export * from './use_exception_lists'; +export * from './use_export_list'; +export * from './use_find_lists'; +export * from './use_import_list'; +export * from './use_persist_exception_item'; +export * from './use_persist_exception_list'; +export * from './use_read_list_index'; +export * from './use_read_list_privileges'; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts index 3b980f84d82a8..ec76d169390cc 100644 --- a/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts @@ -170,7 +170,7 @@ export const useApi = (http: HttpStart): ExceptionsApi => { }, async getExceptionListsItems({ lists, - filterOptions, + filter, pagination, showDetectionsListsOnly, showEndpointListsOnly, @@ -192,7 +192,7 @@ export const useApi = (http: HttpStart): ExceptionsApi => { per_page: perPage, total, } = await Api.fetchExceptionListsItemsByListIds({ - filterOptions, + filter, http, listIds: ids, namespaceTypes: namespaces, diff --git a/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts deleted file mode 100644 index 4ca0a66e4f602..0000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -describe('useExceptionListItems', () => { - test('Tests should be ported', () => { - // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts here once mocks are figured out and kbn package mocks are figured out - expect(true).toBe(true); - }); -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts deleted file mode 100644 index 623e1e76a7f53..0000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { useEffect, useRef, useState } from 'react'; -import type { - ExceptionListItemSchema, - Pagination, - UseExceptionListProps, - FilterExceptionsOptions, -} from '@kbn/securitysolution-io-ts-list-types'; -import { fetchExceptionListsItemsByListIds } from '@kbn/securitysolution-list-api'; - -import { getIdsAndNamespaces } from '@kbn/securitysolution-list-utils'; -import { transformInput } from '../transforms'; - -type Func = () => void; -export type ReturnExceptionListAndItems = [ - boolean, - ExceptionListItemSchema[], - Pagination, - Func | null -]; - -/** - * Hook for using to get an ExceptionList and its ExceptionListItems - * - * @param http Kibana http service - * @param lists array of ExceptionListIdentifiers for all lists to fetch - * @param onError error callback - * @param onSuccess callback when all lists fetched successfully - * @param filterOptions optional - filter by fields or tags - * @param showDetectionsListsOnly boolean, if true, only detection lists are searched - * @param showEndpointListsOnly boolean, if true, only endpoint lists are searched - * @param matchFilters boolean, if true, applies first filter in filterOptions to - * all lists - * @param pagination optional - * - */ -export const useExceptionListItems = ({ - http, - lists, - pagination = { - page: 1, - perPage: 20, - total: 0, - }, - filterOptions, - showDetectionsListsOnly, - showEndpointListsOnly, - matchFilters, - onError, - onSuccess, -}: UseExceptionListProps): ReturnExceptionListAndItems => { - const [exceptionItems, setExceptionListItems] = useState([]); - const [paginationInfo, setPagination] = useState(pagination); - const fetchExceptionListsItems = useRef(null); - const [loading, setLoading] = useState(true); - const { ids, namespaces } = getIdsAndNamespaces({ - lists, - showDetection: showDetectionsListsOnly, - showEndpoint: showEndpointListsOnly, - }); - const filters: FilterExceptionsOptions[] = - matchFilters && filterOptions.length > 0 ? ids.map(() => filterOptions[0]) : filterOptions; - const idsAsString: string = ids.join(','); - const namespacesAsString: string = namespaces.join(','); - const filterAsString: string = filterOptions.map(({ filter }) => filter).join(','); - const filterTagsAsString: string = filterOptions.map(({ tags }) => tags.join(',')).join(','); - - useEffect( - () => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - const fetchData = async (): Promise => { - try { - setLoading(true); - - if (ids.length === 0 && isSubscribed) { - setPagination({ - page: 0, - perPage: pagination.perPage, - total: 0, - }); - setExceptionListItems([]); - - if (onSuccess != null) { - onSuccess({ - exceptions: [], - pagination: { - page: 0, - perPage: pagination.perPage, - total: 0, - }, - }); - } - setLoading(false); - } else { - const { - page, - per_page: perPage, - total, - data, - } = await fetchExceptionListsItemsByListIds({ - filterOptions: filters, - http, - listIds: ids, - namespaceTypes: namespaces, - pagination: { - page: pagination.page, - perPage: pagination.perPage, - }, - signal: abortCtrl.signal, - }); - - // Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes - // for context around the temporary `id` - const transformedData = data.map((item) => transformInput(item)); - - if (isSubscribed) { - setPagination({ - page, - perPage, - total, - }); - setExceptionListItems(transformedData); - - if (onSuccess != null) { - onSuccess({ - exceptions: transformedData, - pagination: { - page, - perPage, - total, - }, - }); - } - } - } - } catch (error) { - if (isSubscribed) { - setExceptionListItems([]); - setPagination({ - page: 1, - perPage: 20, - total: 0, - }); - if (onError != null) { - onError(error); - } - } - } - - if (isSubscribed) { - setLoading(false); - } - }; - - fetchData(); - - fetchExceptionListsItems.current = fetchData; - return (): void => { - isSubscribed = false; - abortCtrl.abort(); - }; - }, // eslint-disable-next-line react-hooks/exhaustive-deps - [ - http, - idsAsString, - namespacesAsString, - setExceptionListItems, - pagination.page, - pagination.perPage, - filterAsString, - filterTagsAsString, - ] - ); - - return [loading, exceptionItems, paginationInfo, fetchExceptionListsItems.current]; -}; diff --git a/packages/kbn-securitysolution-list-utils/kibana.jsonc b/packages/kbn-securitysolution-list-utils/kibana.jsonc new file mode 100644 index 0000000000000..db7d0a5ec334f --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-list-utils", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-rules/kibana.jsonc b/packages/kbn-securitysolution-rules/kibana.jsonc new file mode 100644 index 0000000000000..b7e64cfd39e6b --- /dev/null +++ b/packages/kbn-securitysolution-rules/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-rules", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-t-grid/kibana.jsonc b/packages/kbn-securitysolution-t-grid/kibana.jsonc new file mode 100644 index 0000000000000..bc0f533b72120 --- /dev/null +++ b/packages/kbn-securitysolution-t-grid/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-t-grid", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-securitysolution-utils/kibana.jsonc b/packages/kbn-securitysolution-utils/kibana.jsonc new file mode 100644 index 0000000000000..24e63965c20c8 --- /dev/null +++ b/packages/kbn-securitysolution-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-utils", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-server-http-tools/kibana.jsonc b/packages/kbn-server-http-tools/kibana.jsonc new file mode 100644 index 0000000000000..b96916745c984 --- /dev/null +++ b/packages/kbn-server-http-tools/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/server-http-tools", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-server-route-repository/kibana.jsonc b/packages/kbn-server-route-repository/kibana.jsonc new file mode 100644 index 0000000000000..e1e69049b1791 --- /dev/null +++ b/packages/kbn-server-route-repository/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/server-route-repository", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-shared-svg/kibana.jsonc b/packages/kbn-shared-svg/kibana.jsonc new file mode 100644 index 0000000000000..e816819c9c24a --- /dev/null +++ b/packages/kbn-shared-svg/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-svg", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-shared-ux-utility/kibana.jsonc b/packages/kbn-shared-ux-utility/kibana.jsonc new file mode 100644 index 0000000000000..55c3996ae4dbc --- /dev/null +++ b/packages/kbn-shared-ux-utility/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-utility", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-some-dev-log/kibana.jsonc b/packages/kbn-some-dev-log/kibana.jsonc new file mode 100644 index 0000000000000..e39904defc552 --- /dev/null +++ b/packages/kbn-some-dev-log/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/some-dev-log", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-some-dev-log/package.json b/packages/kbn-some-dev-log/package.json index f075d74b65359..9b01a43a03c00 100644 --- a/packages/kbn-some-dev-log/package.json +++ b/packages/kbn-some-dev-log/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-sort-package-json/kibana.jsonc b/packages/kbn-sort-package-json/kibana.jsonc new file mode 100644 index 0000000000000..72345c2ccd31c --- /dev/null +++ b/packages/kbn-sort-package-json/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/sort-package-json", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-sort-package-json/package.json b/packages/kbn-sort-package-json/package.json index 9a875835d7450..922124b1bdd73 100644 --- a/packages/kbn-sort-package-json/package.json +++ b/packages/kbn-sort-package-json/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-spec-to-console/kibana.jsonc b/packages/kbn-spec-to-console/kibana.jsonc new file mode 100644 index 0000000000000..cf71c222f6f16 --- /dev/null +++ b/packages/kbn-spec-to-console/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/spec-to-console", + "devOnly": true, + "owner": "@elastic/platform-deployment-management", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-spec-to-console/package.json b/packages/kbn-spec-to-console/package.json index b4e488db7f4d9..b60df2a95ebb9 100644 --- a/packages/kbn-spec-to-console/package.json +++ b/packages/kbn-spec-to-console/package.json @@ -12,9 +12,6 @@ }, "author": "", "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - }, "bugs": { "url": "https://github.com/jbudz/spec-to-console/issues" }, diff --git a/packages/kbn-std/kibana.jsonc b/packages/kbn-std/kibana.jsonc new file mode 100644 index 0000000000000..246c11ee7c3f1 --- /dev/null +++ b/packages/kbn-std/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/std", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-stdio-dev-helpers/kibana.jsonc b/packages/kbn-stdio-dev-helpers/kibana.jsonc new file mode 100644 index 0000000000000..0001ba53d7b7e --- /dev/null +++ b/packages/kbn-stdio-dev-helpers/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/stdio-dev-helpers", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-stdio-dev-helpers/package.json b/packages/kbn-stdio-dev-helpers/package.json index 23b0a1cf65a76..ac14acd56e729 100644 --- a/packages/kbn-stdio-dev-helpers/package.json +++ b/packages/kbn-stdio-dev-helpers/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-storybook/kibana.jsonc b/packages/kbn-storybook/kibana.jsonc new file mode 100644 index 0000000000000..b5499440f46ad --- /dev/null +++ b/packages/kbn-storybook/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/storybook", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index f24d926aa42a3..162152c7f1922 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -4,8 +4,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js", - "kibana": { - "devOnly": true - } + "main": "./target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-synthetic-package-map/kibana.jsonc b/packages/kbn-synthetic-package-map/kibana.jsonc new file mode 100644 index 0000000000000..153b6548ce841 --- /dev/null +++ b/packages/kbn-synthetic-package-map/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/synthetic-package-map", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-synthetic-package-map/package.json b/packages/kbn-synthetic-package-map/package.json index ae209fdf98db0..ec6ac454bf31d 100644 --- a/packages/kbn-synthetic-package-map/package.json +++ b/packages/kbn-synthetic-package-map/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-telemetry-tools/kibana.jsonc b/packages/kbn-telemetry-tools/kibana.jsonc new file mode 100644 index 0000000000000..c182ddd3e6960 --- /dev/null +++ b/packages/kbn-telemetry-tools/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/telemetry-tools", + "devOnly": true, + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-telemetry-tools/package.json b/packages/kbn-telemetry-tools/package.json index a0096f090e830..649ff72a56956 100644 --- a/packages/kbn-telemetry-tools/package.json +++ b/packages/kbn-telemetry-tools/package.json @@ -4,8 +4,5 @@ "author": "Kibana Core", "license": "SSPL-1.0 OR Elastic License 2.0", "main": "./target_node/index.js", - "private": true, - "kibana": { - "devOnly": true - } + "private": true } \ No newline at end of file diff --git a/packages/kbn-test-jest-helpers/kibana.jsonc b/packages/kbn-test-jest-helpers/kibana.jsonc new file mode 100644 index 0000000000000..70750f8f4f28d --- /dev/null +++ b/packages/kbn-test-jest-helpers/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/test-jest-helpers", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-test-jest-helpers/package.json b/packages/kbn-test-jest-helpers/package.json index afab6001d605d..fa5851895c6d0 100644 --- a/packages/kbn-test-jest-helpers/package.json +++ b/packages/kbn-test-jest-helpers/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node", - "kibana": { - "devOnly": true - } + "main": "./target_node" } diff --git a/packages/kbn-test-subj-selector/kibana.jsonc b/packages/kbn-test-subj-selector/kibana.jsonc new file mode 100644 index 0000000000000..697ce5593ea71 --- /dev/null +++ b/packages/kbn-test-subj-selector/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/test-subj-selector", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-test-subj-selector/package.json b/packages/kbn-test-subj-selector/package.json index 875397d1ec705..8ced36990a6e3 100755 --- a/packages/kbn-test-subj-selector/package.json +++ b/packages/kbn-test-subj-selector/package.json @@ -6,8 +6,5 @@ "keywords": [], "author": "Spencer Alger ", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": "true", - "kibana": { - "devOnly": true - } + "private": "true" } diff --git a/packages/kbn-test/kibana.jsonc b/packages/kbn-test/kibana.jsonc new file mode 100644 index 0000000000000..c921f7ac39626 --- /dev/null +++ b/packages/kbn-test/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/test", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 5f5e475e392c4..de6ba54c26800 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -4,8 +4,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node", - "kibana": { - "devOnly": true - } + "main": "./target_node" } diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index 8c650ec9b6051..70fa5f2e8d375 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -95,6 +95,7 @@ export interface CreateTestEsClusterOptions { */ license?: 'basic' | 'gold' | 'trial'; // | 'oss' log: ToolingLog; + writeLogsToPath?: string; /** * Node-specific configuration if you wish to run a multi-node * cluster. One node will be added for each item in the array. @@ -168,6 +169,7 @@ export function createTestEsCluster< password = 'changeme', license = 'basic', log, + writeLogsToPath, basePath = Path.resolve(REPO_ROOT, '.es'), esFrom = esTestConfig.getBuildFrom(), dataArchive, @@ -272,6 +274,7 @@ export function createTestEsCluster< skipNativeRealmSetup: this.nodes.length > 1 && i < this.nodes.length - 1, skipReadyCheck: this.nodes.length > 1 && i < this.nodes.length - 1, onEarlyExit, + writeLogsToPath, }); }); } diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap index cff0b46afcad1..ff8961e263f17 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap @@ -23,6 +23,7 @@ Options: --include-tag Tags that suites must include to be run, can be included multiple times. --exclude-tag Tags that suites must NOT include to be run, can be included multiple times. --assert-none-excluded Exit with 1/0 based on if any test is excluded with the current set of tags. + --logToFile Write the log output from Kibana/Elasticsearch to files instead of to stdout --verbose Log everything. --debug Run in debug mode. --quiet Only log errors. @@ -40,6 +41,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -62,6 +64,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -85,6 +88,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -107,6 +111,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -133,6 +138,7 @@ Object { "extraKbnOpts": Object { "server.foo": "bar", }, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -154,6 +160,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "quiet": true, "suiteFiles": Object { "exclude": Array [], @@ -176,6 +183,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "silent": true, "suiteFiles": Object { "exclude": Array [], @@ -198,6 +206,7 @@ Object { "esFrom": "source", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -219,6 +228,7 @@ Object { "esFrom": "source", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -241,6 +251,7 @@ Object { "esVersion": "999.999.999", "extraKbnOpts": undefined, "installDir": "foo", + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -263,6 +274,7 @@ Object { "esVersion": "999.999.999", "extraKbnOpts": undefined, "grep": "management", + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -284,6 +296,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], @@ -306,6 +319,7 @@ Object { "esFrom": "snapshot", "esVersion": "999.999.999", "extraKbnOpts": undefined, + "logsDir": undefined, "suiteFiles": Object { "exclude": Array [], "include": Array [], diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap deleted file mode 100644 index 6b81c2e499cf4..0000000000000 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap +++ /dev/null @@ -1,74 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`run tests CLI options accepts help option even if invalid options passed 1`] = ` -"Run Functional Tests - -Usage: - node scripts/functional_tests --help - node scripts/functional_tests [--config [--config ...]] - node scripts/functional_tests [options] [-- --] - -Options: - --help Display this menu and exit. - --config Pass in a config. Can pass in multiple configs. - --esFrom Build Elasticsearch from source or run from snapshot. Default: $TEST_ES_FROM or snapshot - --kibana-install-dir Run Kibana from existing install directory instead of from source. - --bail Stop the test run at the first failure. - --grep Pattern to select which tests to run. - --updateBaselines Replace baseline screenshots with whatever is generated from the test. - --updateSnapshots Replace inline and file snapshots with whatever is generated from the test. - --u Replace both baseline screenshots and snapshots - --include Files that must included to be run, can be included multiple times. - --exclude Files that must NOT be included to be run, can be included multiple times. - --include-tag Tags that suites must include to be run, can be included multiple times. - --exclude-tag Tags that suites must NOT include to be run, can be included multiple times. - --assert-none-excluded Exit with 1/0 based on if any test is excluded with the current set of tags. - --verbose Log everything. - --debug Run in debug mode. - --quiet Only log errors. - --silent Log nothing. - --dry-run Report tests without executing them. -" -`; - -exports[`run tests CLI options rejects boolean config value 1`] = ` -" -functional_tests: invalid argument [true] to option [config] - ...stack trace... -" -`; - -exports[`run tests CLI options rejects boolean value for kibana-install-dir 1`] = ` -" -functional_tests: invalid argument [true] to option [kibana-install-dir] - ...stack trace... -" -`; - -exports[`run tests CLI options rejects empty config value if no default passed 1`] = ` -" -functional_tests: config is required - ...stack trace... -" -`; - -exports[`run tests CLI options rejects invalid options even if valid options exist 1`] = ` -" -functional_tests: invalid option [aintnothang] - ...stack trace... -" -`; - -exports[`run tests CLI options rejects non-boolean value for bail 1`] = ` -" -functional_tests: invalid argument [peanut] to option [bail] - ...stack trace... -" -`; - -exports[`run tests CLI options rejects non-enum value for esFrom 1`] = ` -" -functional_tests: invalid argument [butter] to option [esFrom] - ...stack trace... -" -`; diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js index d94adcfe615a5..8b1bf471f4e98 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js @@ -6,9 +6,11 @@ * Side Public License, v 1. */ -import { resolve } from 'path'; +import Path from 'path'; +import { v4 as uuid } from 'uuid'; import dedent from 'dedent'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, pickLevelFromFlags } from '@kbn/tooling-log'; import { EsVersion } from '../../../functional_test_runner'; @@ -61,6 +63,9 @@ const options = { 'assert-none-excluded': { desc: 'Exit with 1/0 based on if any test is excluded with the current set of tags.', }, + logToFile: { + desc: 'Write the log output from Kibana/Elasticsearch to files instead of to stdout', + }, verbose: { desc: 'Log everything.' }, debug: { desc: 'Run in debug mode.' }, quiet: { desc: 'Only log errors.' }, @@ -142,19 +147,24 @@ export function processOptions(userOptions, defaultConfigPaths) { delete userOptions['dry-run']; } + const log = new ToolingLog({ + level: pickLevelFromFlags(userOptions), + writeTo: process.stdout, + }); function createLogger() { - return new ToolingLog({ - level: pickLevelFromFlags(userOptions), - writeTo: process.stdout, - }); + return log; } + const logToFile = !!userOptions.logToFile; + const logsDir = logToFile ? Path.resolve(REPO_ROOT, 'data/ftr_servers_logs', uuid()) : undefined; + return { ...userOptions, - configs: configs.map((c) => resolve(c)), + configs: configs.map((c) => Path.resolve(c)), createLogger, extraKbnOpts: userOptions._, esVersion: EsVersion.getDefault(), + logsDir, }; } diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.js b/packages/kbn-test/src/functional_tests/cli/run_tests/cli.js index e920e43f375b4..3958c1503cd30 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/cli.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { runTests } from '../../tasks'; +import { runTests, initLogsDir } from '../../tasks'; import { runCli } from '../../lib'; import { processOptions, displayHelp } from './args'; @@ -21,6 +21,7 @@ import { processOptions, displayHelp } from './args'; export async function runTestsCli(defaultConfigPaths) { await runCli(displayHelp, async (userOptions) => { const options = processOptions(userOptions, defaultConfigPaths); + initLogsDir(options); await runTests(options); }); } diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js b/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js deleted file mode 100644 index 1b679f285d133..0000000000000 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Writable } from 'stream'; - -import { runTestsCli } from './cli'; -import { checkMockConsoleLogSnapshot } from '../../test_helpers'; - -// Note: Stub the runTests function to keep testing only around the cli -// method and arguments. -jest.mock('../../tasks', () => ({ - runTests: jest.fn(), -})); - -describe('run tests CLI', () => { - describe('options', () => { - const originalObjects = { process, console }; - const exitMock = jest.fn(); - const logMock = jest.fn(); // mock logging so we don't send output to the test results - const argvMock = ['foo', 'foo']; - - const processMock = { - exit: exitMock, - argv: argvMock, - stdout: new Writable(), - cwd: jest.fn(), - env: { - ...originalObjects.process.env, - TEST_ES_FROM: 'snapshot', - }, - }; - - beforeAll(() => { - global.process = processMock; - global.console = { log: logMock }; - }); - - afterAll(() => { - global.process = originalObjects.process; - global.console = originalObjects.console; - }); - - beforeEach(() => { - global.process.argv = [...argvMock]; - global.process.env = { - ...originalObjects.process.env, - TEST_ES_FROM: 'snapshot', - }; - jest.resetAllMocks(); - }); - - it('rejects boolean config value', async () => { - global.process.argv.push('--config'); - - await runTestsCli(); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('rejects empty config value if no default passed', async () => { - global.process.argv.push('--config', ''); - - await runTestsCli(); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts empty config value if default passed', async () => { - global.process.argv.push('--config', ''); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('rejects non-boolean value for bail', async () => { - global.process.argv.push('--bail', 'peanut'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts string value for kibana-install-dir', async () => { - global.process.argv.push('--kibana-install-dir', 'foo'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('rejects boolean value for kibana-install-dir', async () => { - global.process.argv.push('--kibana-install-dir'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts boolean value for updateBaselines', async () => { - global.process.argv.push('--updateBaselines'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalledWith(); - }); - - it('accepts boolean value for updateSnapshots', async () => { - global.process.argv.push('--updateSnapshots'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalledWith(); - }); - - it('accepts boolean value for -u', async () => { - global.process.argv.push('-u'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalledWith(); - }); - - it('accepts source value for esFrom', async () => { - global.process.argv.push('--esFrom', 'source'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('rejects non-enum value for esFrom', async () => { - global.process.argv.push('--esFrom', 'butter'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts value for grep', async () => { - global.process.argv.push('--grep', 'management'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts debug option', async () => { - global.process.argv.push('--debug'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts silent option', async () => { - global.process.argv.push('--silent'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts quiet option', async () => { - global.process.argv.push('--quiet'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts verbose option', async () => { - global.process.argv.push('--verbose'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts network throttle option', async () => { - global.process.argv.push('--throttle'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - }); - - it('accepts headless option', async () => { - global.process.argv.push('--headless'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - }); - - it('accepts extra server options', async () => { - global.process.argv.push('--', '--server.foo=bar'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts help option even if invalid options passed', async () => { - global.process.argv.push('--debug', '--aintnothang', '--help'); - - await runTestsCli(['foo']); - - expect(exitMock).not.toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('rejects invalid options even if valid options exist', async () => { - global.process.argv.push('--debug', '--aintnothang', '--bail'); - - await runTestsCli(['foo']); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - }); -}); diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap index cd3174d13c3e6..1f572578119f7 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap @@ -13,6 +13,7 @@ Options: --config Pass in a config --esFrom Build Elasticsearch from source, snapshot or path to existing install dir. Default: $TEST_ES_FROM or snapshot --kibana-install-dir Run Kibana from existing install directory instead of from source. + --logToFile Write the log output from Kibana/Elasticsearch to files instead of to stdout --verbose Log everything. --debug Run in debug mode. --quiet Only log errors. @@ -26,6 +27,7 @@ Object { "debug": true, "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -36,6 +38,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -51,6 +54,7 @@ Object { "extraKbnOpts": Object { "server.foo": "bar", }, + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -61,6 +65,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "quiet": true, "useDefaultConfig": true, } @@ -72,6 +77,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "silent": true, "useDefaultConfig": true, } @@ -83,6 +89,7 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -93,6 +100,7 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -104,6 +112,7 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "installDir": "foo", + "logsDir": undefined, "useDefaultConfig": true, } `; @@ -114,6 +123,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, "verbose": true, } @@ -125,6 +135,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "logsDir": undefined, "useDefaultConfig": true, } `; diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap deleted file mode 100644 index ba085b0868216..0000000000000 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`start servers CLI options accepts boolean value for updateBaselines 1`] = ` -" -functional_tests_server: invalid option [updateBaselines] - ...stack trace... -" -`; - -exports[`start servers CLI options accepts boolean value for updateSnapshots 1`] = ` -" -functional_tests_server: invalid option [updateSnapshots] - ...stack trace... -" -`; - -exports[`start servers CLI options rejects bail 1`] = ` -" -functional_tests_server: invalid option [bail] - ...stack trace... -" -`; - -exports[`start servers CLI options rejects boolean config value 1`] = ` -" -functional_tests_server: invalid argument [true] to option [config] - ...stack trace... -" -`; - -exports[`start servers CLI options rejects boolean value for kibana-install-dir 1`] = ` -" -functional_tests_server: invalid argument [true] to option [kibana-install-dir] - ...stack trace... -" -`; - -exports[`start servers CLI options rejects empty config value if no default passed 1`] = ` -" -functional_tests_server: config is required - ...stack trace... -" -`; - -exports[`start servers CLI options rejects invalid options even if valid options exist 1`] = ` -" -functional_tests_server: invalid option [grep] - ...stack trace... -" -`; diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js index 527e3ce64613d..e025bdc339331 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js @@ -6,9 +6,11 @@ * Side Public License, v 1. */ -import { resolve } from 'path'; +import Path from 'path'; +import { v4 as uuid } from 'uuid'; import dedent from 'dedent'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, pickLevelFromFlags } from '@kbn/tooling-log'; const options = { @@ -26,6 +28,9 @@ const options = { arg: '', desc: 'Run Kibana from existing install directory instead of from source.', }, + logToFile: { + desc: 'Write the log output from Kibana/Elasticsearch to files instead of to stdout', + }, verbose: { desc: 'Log everything.' }, debug: { desc: 'Run in debug mode.' }, quiet: { desc: 'Only log errors.' }, @@ -80,16 +85,22 @@ export function processOptions(userOptions, defaultConfigPath) { delete userOptions['kibana-install-dir']; } + const log = new ToolingLog({ + level: pickLevelFromFlags(userOptions), + writeTo: process.stdout, + }); + function createLogger() { - return new ToolingLog({ - level: pickLevelFromFlags(userOptions), - writeTo: process.stdout, - }); + return log; } + const logToFile = !!userOptions.logToFile; + const logsDir = logToFile ? Path.resolve(REPO_ROOT, 'data/ftr_servers_logs', uuid()) : undefined; + return { ...userOptions, - config: resolve(config), + logsDir, + config: Path.resolve(config), useDefaultConfig, createLogger, extraKbnOpts: userOptions._, diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.js b/packages/kbn-test/src/functional_tests/cli/start_servers/cli.js index df7f8750b2ae3..d57d5c4761f6e 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.js +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/cli.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { startServers } from '../../tasks'; +import { startServers, initLogsDir } from '../../tasks'; import { runCli } from '../../lib'; import { processOptions, displayHelp } from './args'; @@ -18,6 +18,7 @@ import { processOptions, displayHelp } from './args'; export async function startServersCli(defaultConfigPath) { await runCli(displayHelp, async (userOptions) => { const options = processOptions(userOptions, defaultConfigPath); + initLogsDir(options); await startServers({ ...options, }); diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js b/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js deleted file mode 100644 index a88e4dbd01169..0000000000000 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js +++ /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 { Writable } from 'stream'; - -import { startServersCli } from './cli'; -import { checkMockConsoleLogSnapshot } from '../../test_helpers'; - -// Note: Stub the startServers function to keep testing only around the cli -// method and arguments. -jest.mock('../../tasks', () => ({ - startServers: jest.fn(), -})); - -describe('start servers CLI', () => { - describe('options', () => { - const originalObjects = { process, console }; - const exitMock = jest.fn(); - const logMock = jest.fn(); // mock logging so we don't send output to the test results - const argvMock = ['foo', 'foo']; - - const processMock = { - exit: exitMock, - argv: argvMock, - stdout: new Writable(), - cwd: jest.fn(), - env: { - ...originalObjects.process.env, - TEST_ES_FROM: 'snapshot', - }, - }; - - beforeAll(() => { - global.process = processMock; - global.console = { log: logMock }; - }); - - afterAll(() => { - global.process = originalObjects.process; - global.console = originalObjects.console; - }); - - beforeEach(() => { - global.process.argv = [...argvMock]; - global.process.env = { - ...originalObjects.process.env, - TEST_ES_FROM: 'snapshot', - }; - jest.resetAllMocks(); - }); - - it('rejects boolean config value', async () => { - global.process.argv.push('--config'); - - await startServersCli(); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('rejects empty config value if no default passed', async () => { - global.process.argv.push('--config', ''); - - await startServersCli(); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts empty config value if default passed', async () => { - global.process.argv.push('--config', ''); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('rejects bail', async () => { - global.process.argv.push('--bail', true); - - await startServersCli('foo'); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts string value for kibana-install-dir', async () => { - global.process.argv.push('--kibana-install-dir', 'foo'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('rejects boolean value for kibana-install-dir', async () => { - global.process.argv.push('--kibana-install-dir'); - - await startServersCli('foo'); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts boolean value for updateBaselines', async () => { - global.process.argv.push('--updateBaselines'); - - await startServersCli('foo'); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts boolean value for updateSnapshots', async () => { - global.process.argv.push('--updateSnapshots'); - - await startServersCli('foo'); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - - it('accepts source value for esFrom', async () => { - global.process.argv.push('--esFrom', 'source'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts debug option', async () => { - global.process.argv.push('--debug'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts silent option', async () => { - global.process.argv.push('--silent'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts quiet option', async () => { - global.process.argv.push('--quiet'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts verbose option', async () => { - global.process.argv.push('--verbose'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts extra server options', async () => { - global.process.argv.push('--', '--server.foo=bar'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalled(); - }); - - it('accepts help option even if invalid options passed', async () => { - global.process.argv.push('--debug', '--grep', '--help'); - - await startServersCli('foo'); - - expect(exitMock).not.toHaveBeenCalledWith(1); - }); - - it('rejects invalid options even if valid options exist', async () => { - global.process.argv.push('--debug', '--grep', '--bail'); - - await startServersCli('foo'); - - expect(exitMock).toHaveBeenCalledWith(1); - checkMockConsoleLogSnapshot(logMock); - }); - }); -}); 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 5dcee56e765e0..b367af4daf492 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts @@ -18,6 +18,7 @@ interface RunElasticsearchOptions { esFrom?: string; config: Config; onEarlyExit?: (msg: string) => void; + logsDir?: string; } interface CcsConfig { @@ -62,26 +63,41 @@ function getEsConfig({ export async function runElasticsearch( options: RunElasticsearchOptions ): Promise<() => Promise> { - const { log } = options; + const { log, logsDir } = options; const config = getEsConfig(options); if (!config.ccsConfig) { - const node = await startEsNode(log, 'ftr', config); + const node = await startEsNode({ + log, + name: 'ftr', + logsDir, + config, + }); return async () => { await node.cleanup(); }; } const remotePort = await getPort(); - const remoteNode = await startEsNode(log, 'ftr-remote', { - ...config, - port: parseInt(new URL(config.ccsConfig.remoteClusterUrl).port, 10), - transportPort: remotePort, + const remoteNode = await startEsNode({ + log, + name: 'ftr-remote', + logsDir, + config: { + ...config, + port: parseInt(new URL(config.ccsConfig.remoteClusterUrl).port, 10), + transportPort: remotePort, + }, }); - const localNode = await startEsNode(log, 'ftr-local', { - ...config, - esArgs: [...config.esArgs, `cluster.remote.ftr-remote.seeds=localhost:${remotePort}`], + const localNode = await startEsNode({ + log, + name: 'ftr-local', + logsDir, + config: { + ...config, + esArgs: [...config.esArgs, `cluster.remote.ftr-remote.seeds=localhost:${remotePort}`], + }, }); return async () => { @@ -90,12 +106,19 @@ export async function runElasticsearch( }; } -async function startEsNode( - log: ToolingLog, - name: string, - config: EsConfig & { transportPort?: number }, - onEarlyExit?: (msg: string) => void -) { +async function startEsNode({ + log, + name, + config, + onEarlyExit, + logsDir, +}: { + log: ToolingLog; + name: string; + config: EsConfig & { transportPort?: number }; + onEarlyExit?: (msg: string) => void; + logsDir?: string; +}) { const cluster = createTestEsCluster({ clusterName: `cluster-${name}`, esArgs: config.esArgs, @@ -106,6 +129,7 @@ async function startEsNode( port: config.port, ssl: config.ssl, log, + writeLogsToPath: logsDir ? resolve(logsDir, `es-cluster-${name}.log`) : undefined, basePath: resolve(REPO_ROOT, '.es'), nodes: [ { 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 58b77151a9fde..2ae15ca5f83f8 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 @@ -42,7 +42,11 @@ export async function runKibanaServer({ }: { procs: ProcRunner; config: Config; - options: { installDir?: string; extraKbnOpts?: string[] }; + options: { + installDir?: string; + extraKbnOpts?: string[]; + logsDir?: string; + }; onEarlyExit?: (msg: string) => void; }) { const runOptions = config.get('kbnTestServer.runOptions'); @@ -84,10 +88,14 @@ export async function runKibanaServer({ ...(options.extraKbnOpts ?? []), ]); + const mainName = useTaskRunner ? 'kbn-ui' : 'kibana'; const promises = [ // main process - procs.run(useTaskRunner ? 'kbn-ui' : 'kibana', { + procs.run(mainName, { ...procRunnerOpts, + writeLogsToPath: options.logsDir + ? Path.resolve(options.logsDir, `${mainName}.log`) + : undefined, args: [ ...prefixArgs, ...parseRawFlags([ @@ -110,6 +118,9 @@ export async function runKibanaServer({ promises.push( procs.run('kbn-tasks', { ...procRunnerOpts, + writeLogsToPath: options.logsDir + ? Path.resolve(options.logsDir, 'kbn-tasks.log') + : undefined, args: [ ...prefixArgs, ...parseRawFlags([ diff --git a/packages/kbn-test/src/functional_tests/tasks.ts b/packages/kbn-test/src/functional_tests/tasks.ts index 9b5fb5424f3fe..26504b07544b0 100644 --- a/packages/kbn-test/src/functional_tests/tasks.ts +++ b/packages/kbn-test/src/functional_tests/tasks.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import Fs from 'fs'; import Path from 'path'; import { setTimeout } from 'timers/promises'; @@ -51,6 +52,16 @@ const makeSuccessMessage = (options: StartServerOptions) => { ); }; +export async function initLogsDir(options: { logsDir?: string; createLogger(): ToolingLog }) { + if (options.logsDir) { + options + .createLogger() + .info(`Kibana/ES logs will be written to ${Path.relative(process.cwd(), options.logsDir)}/`); + + Fs.mkdirSync(options.logsDir, { recursive: true }); + } +} + /** * Run servers and tests for each config */ diff --git a/packages/kbn-timelion-grammar/kibana.jsonc b/packages/kbn-timelion-grammar/kibana.jsonc new file mode 100644 index 0000000000000..211d8483089c2 --- /dev/null +++ b/packages/kbn-timelion-grammar/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/timelion-grammar", + "owner": "@elastic/kibana-vis-editors", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-tinymath/kibana.jsonc b/packages/kbn-tinymath/kibana.jsonc new file mode 100644 index 0000000000000..6ebf2ac0c00ea --- /dev/null +++ b/packages/kbn-tinymath/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/tinymath", + "owner": "@elastic/kibana-vis-editors", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-tooling-log/kibana.jsonc b/packages/kbn-tooling-log/kibana.jsonc new file mode 100644 index 0000000000000..88eecfa75bf38 --- /dev/null +++ b/packages/kbn-tooling-log/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/tooling-log", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-tooling-log/package.json b/packages/kbn-tooling-log/package.json index 53137ddf6366e..5af0ae2aca79a 100644 --- a/packages/kbn-tooling-log/package.json +++ b/packages/kbn-tooling-log/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-type-summarizer-cli/kibana.jsonc b/packages/kbn-type-summarizer-cli/kibana.jsonc new file mode 100644 index 0000000000000..88434bc04812d --- /dev/null +++ b/packages/kbn-type-summarizer-cli/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/type-summarizer-cli", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-type-summarizer-cli/package.json b/packages/kbn-type-summarizer-cli/package.json index fafc57c3c7d08..8b71981054f11 100644 --- a/packages/kbn-type-summarizer-cli/package.json +++ b/packages/kbn-type-summarizer-cli/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-type-summarizer-core/kibana.jsonc b/packages/kbn-type-summarizer-core/kibana.jsonc new file mode 100644 index 0000000000000..322a6c152b2f7 --- /dev/null +++ b/packages/kbn-type-summarizer-core/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/type-summarizer-core", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-type-summarizer-core/package.json b/packages/kbn-type-summarizer-core/package.json index a1dfb4fea8751..1ad7560b3571c 100644 --- a/packages/kbn-type-summarizer-core/package.json +++ b/packages/kbn-type-summarizer-core/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-type-summarizer/kibana.jsonc b/packages/kbn-type-summarizer/kibana.jsonc new file mode 100644 index 0000000000000..e4eb9dc7c6034 --- /dev/null +++ b/packages/kbn-type-summarizer/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/type-summarizer", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-type-summarizer/package.json b/packages/kbn-type-summarizer/package.json index b9118e2b43a3d..9ea19f6497219 100644 --- a/packages/kbn-type-summarizer/package.json +++ b/packages/kbn-type-summarizer/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-typed-react-router-config/kibana.jsonc b/packages/kbn-typed-react-router-config/kibana.jsonc new file mode 100644 index 0000000000000..51882f6266c34 --- /dev/null +++ b/packages/kbn-typed-react-router-config/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/typed-react-router-config", + "owner": "@elastic/apm-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ui-framework/kibana.jsonc b/packages/kbn-ui-framework/kibana.jsonc new file mode 100644 index 0000000000000..eaef655970e37 --- /dev/null +++ b/packages/kbn-ui-framework/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ui-framework", + "owner": "@elastic/kibana-design", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ui-shared-deps-npm/kibana.jsonc b/packages/kbn-ui-shared-deps-npm/kibana.jsonc new file mode 100644 index 0000000000000..b7d47a5ccff4b --- /dev/null +++ b/packages/kbn-ui-shared-deps-npm/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ui-shared-deps-npm", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ui-shared-deps-npm/src/public_path_loader.js b/packages/kbn-ui-shared-deps-npm/src/public_path_loader.js index 6b2c0a8e9512f..4f52032b316ac 100644 --- a/packages/kbn-ui-shared-deps-npm/src/public_path_loader.js +++ b/packages/kbn-ui-shared-deps-npm/src/public_path_loader.js @@ -7,6 +7,7 @@ */ const Qs = require('querystring'); +// eslint-disable-next-line import/no-extraneous-dependencies const { stringifyRequest } = require('loader-utils'); const VAL_LOADER = require.resolve('val-loader'); diff --git a/packages/kbn-ui-shared-deps-src/kibana.jsonc b/packages/kbn-ui-shared-deps-src/kibana.jsonc new file mode 100644 index 0000000000000..49da1e45e9d27 --- /dev/null +++ b/packages/kbn-ui-shared-deps-src/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ui-shared-deps-src", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-ui-theme/kibana.jsonc b/packages/kbn-ui-theme/kibana.jsonc new file mode 100644 index 0000000000000..3f90299e80360 --- /dev/null +++ b/packages/kbn-ui-theme/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ui-theme", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-user-profile-components/kibana.jsonc b/packages/kbn-user-profile-components/kibana.jsonc new file mode 100644 index 0000000000000..d4c7f266fe60b --- /dev/null +++ b/packages/kbn-user-profile-components/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/user-profile-components", + "owner": "@elastic/kibana-security", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-utility-types-jest/kibana.jsonc b/packages/kbn-utility-types-jest/kibana.jsonc new file mode 100644 index 0000000000000..83eaa2ce42aa2 --- /dev/null +++ b/packages/kbn-utility-types-jest/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/utility-types-jest", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-utility-types-jest/package.json b/packages/kbn-utility-types-jest/package.json index 808dd51dec793..b409e49384fc7 100644 --- a/packages/kbn-utility-types-jest/package.json +++ b/packages/kbn-utility-types-jest/package.json @@ -3,8 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "target_node/index.js", - "kibana": { - "devOnly": false - } + "main": "target_node/index.js" } \ No newline at end of file diff --git a/packages/kbn-utility-types/kibana.jsonc b/packages/kbn-utility-types/kibana.jsonc new file mode 100644 index 0000000000000..c041668a7689b --- /dev/null +++ b/packages/kbn-utility-types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/utility-types", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-utility-types/package.json b/packages/kbn-utility-types/package.json index f79164388f18b..fa899c332dee9 100644 --- a/packages/kbn-utility-types/package.json +++ b/packages/kbn-utility-types/package.json @@ -4,9 +4,6 @@ "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", "main": "target_node/index.js", - "kibana": { - "devOnly": false - }, "scripts": { "test": "../../node_modules/.bin/tsd src/tsd_tests" } diff --git a/packages/kbn-utils/kibana.jsonc b/packages/kbn-utils/kibana.jsonc new file mode 100644 index 0000000000000..1e6935937f6e5 --- /dev/null +++ b/packages/kbn-utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/utils", + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-yarn-lock-validator/kibana.jsonc b/packages/kbn-yarn-lock-validator/kibana.jsonc new file mode 100644 index 0000000000000..9ff3e33975597 --- /dev/null +++ b/packages/kbn-yarn-lock-validator/kibana.jsonc @@ -0,0 +1,8 @@ +{ + "type": "shared-common", + "id": "@kbn/yarn-lock-validator", + "devOnly": true, + "owner": "@elastic/kibana-operations", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-yarn-lock-validator/package.json b/packages/kbn-yarn-lock-validator/package.json index e7d0c00269ac7..4d024fb6aded5 100644 --- a/packages/kbn-yarn-lock-validator/package.json +++ b/packages/kbn-yarn-lock-validator/package.json @@ -3,8 +3,5 @@ "private": true, "version": "1.0.0", "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } + "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-yarn-lock-validator/src/validate_yarn_lock.ts b/packages/kbn-yarn-lock-validator/src/validate_yarn_lock.ts index 923a0f11fcbf6..668f2911556c9 100644 --- a/packages/kbn-yarn-lock-validator/src/validate_yarn_lock.ts +++ b/packages/kbn-yarn-lock-validator/src/validate_yarn_lock.ts @@ -93,14 +93,15 @@ export async function validateDependencies(log: SomeDevLog, yarnLock: YarnLock) // look for packages that have the the `kibana.devOnly` flag in their package.json // and make sure they aren't included in the production dependencies of Kibana const bazelPackages = await discoverBazelPackages(REPO_ROOT); - const devOnlyPackagesInProduction = bazelPackages - .filter((p) => p.isDevOnly() && Object.hasOwn(kibanaPackageJson.dependencies, p.pkg.name)) - .map((p) => p.pkg.name); - + const devOnlyPackagesInProduction = bazelPackages.flatMap((p) => + p.isDevOnly() && Object.hasOwn(kibanaPackageJson.dependencies, p.manifest.id) + ? p.manifest.id + : [] + ); if (devOnlyPackagesInProduction.length) { log.error(dedent` Some of the packages in the production dependency chain for Kibana and X-Pack are - flagged with "kibana.devOnly" in their package.json. Please check changes made to + flagged with "devOnly" in their package.json. Please check changes made to packages and their dependencies to ensure they don't end up in production. The devOnly dependencies that are being dependend on in production are: diff --git a/packages/shared-ux/avatar/solution/kibana.jsonc b/packages/shared-ux/avatar/solution/kibana.jsonc new file mode 100644 index 0000000000000..6a2ea7f756381 --- /dev/null +++ b/packages/shared-ux/avatar/solution/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-avatar-solution", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/button/exit_full_screen/impl/kibana.jsonc b/packages/shared-ux/button/exit_full_screen/impl/kibana.jsonc new file mode 100644 index 0000000000000..328b3ebbdc7bb --- /dev/null +++ b/packages/shared-ux/button/exit_full_screen/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-button-exit-full-screen", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/button/exit_full_screen/mocks/kibana.jsonc b/packages/shared-ux/button/exit_full_screen/mocks/kibana.jsonc new file mode 100644 index 0000000000000..4cfb6b20a297b --- /dev/null +++ b/packages/shared-ux/button/exit_full_screen/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-button-exit-full-screen-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/button/exit_full_screen/types/kibana.jsonc b/packages/shared-ux/button/exit_full_screen/types/kibana.jsonc new file mode 100644 index 0000000000000..00597791c1c83 --- /dev/null +++ b/packages/shared-ux/button/exit_full_screen/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-button-exit-full-screen-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/button_toolbar/kibana.jsonc b/packages/shared-ux/button_toolbar/kibana.jsonc new file mode 100644 index 0000000000000..45d7bb2c935a9 --- /dev/null +++ b/packages/shared-ux/button_toolbar/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-button-toolbar", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/card/no_data/impl/kibana.jsonc b/packages/shared-ux/card/no_data/impl/kibana.jsonc new file mode 100644 index 0000000000000..38ce883168de3 --- /dev/null +++ b/packages/shared-ux/card/no_data/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-card-no-data", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/card/no_data/mocks/kibana.jsonc b/packages/shared-ux/card/no_data/mocks/kibana.jsonc new file mode 100644 index 0000000000000..79230acd9e35c --- /dev/null +++ b/packages/shared-ux/card/no_data/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-card-no-data-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/card/no_data/types/kibana.jsonc b/packages/shared-ux/card/no_data/types/kibana.jsonc new file mode 100644 index 0000000000000..d612fb80bfcd5 --- /dev/null +++ b/packages/shared-ux/card/no_data/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-card-no-data-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/link/redirect_app/impl/kibana.jsonc b/packages/shared-ux/link/redirect_app/impl/kibana.jsonc new file mode 100644 index 0000000000000..38f34416473b9 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-link-redirect-app", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/link/redirect_app/mocks/kibana.jsonc b/packages/shared-ux/link/redirect_app/mocks/kibana.jsonc new file mode 100644 index 0000000000000..f9991820df022 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-link-redirect-app-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/link/redirect_app/types/kibana.jsonc b/packages/shared-ux/link/redirect_app/types/kibana.jsonc new file mode 100644 index 0000000000000..4de2b13ec358f --- /dev/null +++ b/packages/shared-ux/link/redirect_app/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-link-redirect-app-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/analytics_no_data/impl/kibana.jsonc b/packages/shared-ux/page/analytics_no_data/impl/kibana.jsonc new file mode 100644 index 0000000000000..6b44af223ee87 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-analytics-no-data", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/analytics_no_data/mocks/kibana.jsonc b/packages/shared-ux/page/analytics_no_data/mocks/kibana.jsonc new file mode 100644 index 0000000000000..30284a98f4b10 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-analytics-no-data-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/analytics_no_data/types/kibana.jsonc b/packages/shared-ux/page/analytics_no_data/types/kibana.jsonc new file mode 100644 index 0000000000000..c5f393e68b31d --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-analytics-no-data-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_no_data/impl/kibana.jsonc b/packages/shared-ux/page/kibana_no_data/impl/kibana.jsonc new file mode 100644 index 0000000000000..afbc38e5c31ea --- /dev/null +++ b/packages/shared-ux/page/kibana_no_data/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-no-data", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_no_data/mocks/kibana.jsonc b/packages/shared-ux/page/kibana_no_data/mocks/kibana.jsonc new file mode 100644 index 0000000000000..40b9b7ae6949f --- /dev/null +++ b/packages/shared-ux/page/kibana_no_data/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-no-data-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_no_data/types/kibana.jsonc b/packages/shared-ux/page/kibana_no_data/types/kibana.jsonc new file mode 100644 index 0000000000000..b29d44238995f --- /dev/null +++ b/packages/shared-ux/page/kibana_no_data/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-no-data-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_template/impl/kibana.jsonc b/packages/shared-ux/page/kibana_template/impl/kibana.jsonc new file mode 100644 index 0000000000000..6d1d8ff74de77 --- /dev/null +++ b/packages/shared-ux/page/kibana_template/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-template", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_template/mocks/kibana.jsonc b/packages/shared-ux/page/kibana_template/mocks/kibana.jsonc new file mode 100644 index 0000000000000..43a17e3257739 --- /dev/null +++ b/packages/shared-ux/page/kibana_template/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-template-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/kibana_template/types/kibana.jsonc b/packages/shared-ux/page/kibana_template/types/kibana.jsonc new file mode 100644 index 0000000000000..b4b18d25baf0b --- /dev/null +++ b/packages/shared-ux/page/kibana_template/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-kibana-template-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data/impl/kibana.jsonc b/packages/shared-ux/page/no_data/impl/kibana.jsonc new file mode 100644 index 0000000000000..2a0576ad190bd --- /dev/null +++ b/packages/shared-ux/page/no_data/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data/mocks/kibana.jsonc b/packages/shared-ux/page/no_data/mocks/kibana.jsonc new file mode 100644 index 0000000000000..6735d457c937d --- /dev/null +++ b/packages/shared-ux/page/no_data/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data/types/kibana.jsonc b/packages/shared-ux/page/no_data/types/kibana.jsonc new file mode 100644 index 0000000000000..02a6dbc2f8fa1 --- /dev/null +++ b/packages/shared-ux/page/no_data/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data_config/impl/kibana.jsonc b/packages/shared-ux/page/no_data_config/impl/kibana.jsonc new file mode 100644 index 0000000000000..991cb4ef781b9 --- /dev/null +++ b/packages/shared-ux/page/no_data_config/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data-config", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data_config/mocks/kibana.jsonc b/packages/shared-ux/page/no_data_config/mocks/kibana.jsonc new file mode 100644 index 0000000000000..d87174032f553 --- /dev/null +++ b/packages/shared-ux/page/no_data_config/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data-config-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/no_data_config/types/kibana.jsonc b/packages/shared-ux/page/no_data_config/types/kibana.jsonc new file mode 100644 index 0000000000000..2491f5442db8c --- /dev/null +++ b/packages/shared-ux/page/no_data_config/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-no-data-config-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/page/solution_nav/kibana.jsonc b/packages/shared-ux/page/solution_nav/kibana.jsonc new file mode 100644 index 0000000000000..8c62fb2f3e750 --- /dev/null +++ b/packages/shared-ux/page/solution_nav/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-page-solution-nav", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/prompt/no_data_views/impl/kibana.jsonc b/packages/shared-ux/prompt/no_data_views/impl/kibana.jsonc new file mode 100644 index 0000000000000..4861e3d3b6edd --- /dev/null +++ b/packages/shared-ux/prompt/no_data_views/impl/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-prompt-no-data-views", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/prompt/no_data_views/mocks/kibana.jsonc b/packages/shared-ux/prompt/no_data_views/mocks/kibana.jsonc new file mode 100644 index 0000000000000..65532173dd0ed --- /dev/null +++ b/packages/shared-ux/prompt/no_data_views/mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-prompt-no-data-views-mocks", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/prompt/no_data_views/types/kibana.jsonc b/packages/shared-ux/prompt/no_data_views/types/kibana.jsonc new file mode 100644 index 0000000000000..1385b91ec370e --- /dev/null +++ b/packages/shared-ux/prompt/no_data_views/types/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-prompt-no-data-views-types", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/kbn-jsonc/BUILD.bazel b/packages/shared-ux/router/impl/BUILD.bazel similarity index 75% rename from packages/kbn-jsonc/BUILD.bazel rename to packages/shared-ux/router/impl/BUILD.bazel index e38170649a9a8..bc9b0aaac6d38 100644 --- a/packages/kbn-jsonc/BUILD.bazel +++ b/packages/shared-ux/router/impl/BUILD.bazel @@ -2,25 +2,17 @@ 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-jsonc" -PKG_REQUIRE_NAME = "@kbn/jsonc" +PKG_DIRNAME = "shared-ux-router" +PKG_REQUIRE_NAME = "@kbn/shared-ux-router" SOURCE_FILES = glob( [ - "**/*.js", + "**/*.ts", + "**/*.tsx", + "**/*.mdx" ], exclude = [ - "**/*.config.js", - "**/*.mock.*", "**/*.test.*", - "**/*.stories.*", - "**/__snapshots__/**", - "**/integration_tests/**", - "**/mocks/**", - "**/scripts/**", - "**/storybook/**", - "**/test_fixtures/**", - "**/test_helpers/**", ], ) @@ -46,6 +38,12 @@ NPM_MODULE_EXTRA_FILES = [ # "@npm//name-of-package" # eg. "@npm//lodash" RUNTIME_DEPS = [ + "@npm//react", + "@npm//react-router-dom", + "@npm//react-use", + "@npm//rxjs", + "//packages/kbn-shared-ux-utility", + "//packages/kbn-test-jest-helpers", ] # In this array place dependencies necessary to build the types, which will include the @@ -60,6 +58,13 @@ RUNTIME_DEPS = [ TYPES_DEPS = [ "@npm//@types/node", "@npm//@types/jest", + "@npm//@types/react", + "@npm//@types/react-router-dom", + "@npm//react-use", + "@npm//rxjs", + "//packages/kbn-shared-ux-utility:npm_module_types", + "//packages/shared-ux/router/types:npm_module_types", + "//packages/kbn-ambient-ui-types", ] jsts_transpiler( @@ -68,6 +73,17 @@ jsts_transpiler( 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", @@ -84,7 +100,6 @@ ts_project( deps = TYPES_DEPS, declaration = True, declaration_map = True, - allow_js = True, emit_declaration_only = True, out_dir = "target_types", tsconfig = ":tsconfig", @@ -93,7 +108,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/shared-ux/router/impl/README.mdx b/packages/shared-ux/router/impl/README.mdx new file mode 100644 index 0000000000000..b8b0235e9a1e4 --- /dev/null +++ b/packages/shared-ux/router/impl/README.mdx @@ -0,0 +1,53 @@ +--- +id: sharedUX/Router +slug: /shared-ux/router +title: Router +description: A router component +tags: ['shared-ux', 'component', 'router', 'route'] +date: 2022-08-12 +--- + +## Summary +This is a wrapper around the `react-router-dom` Route component that inserts MatchPropagator in every application route. It helps track all route changes and send them to the execution context, later used to enrich APM 'route-change' transactions. +The component does not require any props and accepts props from the RouteProps interface such as a `path`, or a component like `AppContainer`. + + +```jsx + +``` + +### Explanation of RouteProps + +```jsx +export interface RouteProps { + location?: H.Location; + component?: React.ComponentType> | React.ComponentType; + render?: (props: RouteComponentProps) => React.ReactNode; + children?: ((props: RouteChildrenProps) => React.ReactNode) | React.ReactNode; + path?: string | string[]; + exact?: boolean; + sensitive?: boolean; + strict?: boolean; +} +``` + +All props are optional + +| Prop Name | Prop Type | Description | +|---|---|---| +| `location` | `H.Location` | the location of one instance of history | +| `component` | `React.ComponentType>` or `React.ComponentType;` | a react component | +| `render` | `(props: RouteComponentProps) => React.ReactNode;` | render props to a react node| +| `children` | `((props: RouteChildrenProps) => React.ReactNode)` or `React.ReactNode;` | pass children to a react node | +| `path` | `string` or `string[];` | a url path or array of paths | +| `exact` | `boolean` | exact match for a route (see: https://stackoverflow.com/questions/52275146/usage-of-exact-and-strict-props) | +| `sensitive` | `boolean` | case senstive route | +| `strict` | `boolean` | strict entry of the requested path in the path name | + + + +This component removes the need for manual calls to `useExecutionContext` and they should be removed. + +## EUI Promotion Status + +This component is not currently considered for promotion to EUI. \ No newline at end of file diff --git a/packages/shared-ux/router/impl/__snapshots__/router.test.tsx.snap b/packages/shared-ux/router/impl/__snapshots__/router.test.tsx.snap new file mode 100644 index 0000000000000..418aa60b7c1f4 --- /dev/null +++ b/packages/shared-ux/router/impl/__snapshots__/router.test.tsx.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Route component prop renders 1`] = ` + +`; + +exports[`Route location renders as expected 1`] = ` + + + +`; + +exports[`Route render prop renders 1`] = ` + +`; + +exports[`Route renders 1`] = ` + + + +`; diff --git a/packages/shared-ux/router/impl/index.ts b/packages/shared-ux/router/impl/index.ts new file mode 100644 index 0000000000000..8659ff73ced36 --- /dev/null +++ b/packages/shared-ux/router/impl/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 { Route } from './router'; diff --git a/packages/shared-ux/router/impl/jest.config.js b/packages/shared-ux/router/impl/jest.config.js new file mode 100644 index 0000000000000..fe0025102d655 --- /dev/null +++ b/packages/shared-ux/router/impl/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/router/impl'], +}; diff --git a/packages/shared-ux/router/impl/package.json b/packages/shared-ux/router/impl/package.json new file mode 100644 index 0000000000000..3faa6ac609ebc --- /dev/null +++ b/packages/shared-ux/router/impl/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-router", + "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/router/impl/router.test.tsx b/packages/shared-ux/router/impl/router.test.tsx new file mode 100644 index 0000000000000..8c068d5a162d0 --- /dev/null +++ b/packages/shared-ux/router/impl/router.test.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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, { Component, FC } from 'react'; +import { shallow } from 'enzyme'; +import { Route } from './router'; +import { createMemoryHistory } from 'history'; + +describe('Route', () => { + test('renders', () => { + const example = shallow(); + expect(example).toMatchSnapshot(); + }); + + test('location renders as expected', () => { + // create a history + const historyLocation = createMemoryHistory(); + // add the path to the history + historyLocation.push('/app/wow'); + // prevent the location key from remaking itself each jest test + historyLocation.location.key = 's5brde'; + // the Route component takes the history location + const example = shallow(); + expect(example).toMatchSnapshot(); + }); + + test('component prop renders', () => { + const sampleComponent: FC<{}> = () => { + return Test; + }; + const example = shallow(); + expect(example).toMatchSnapshot(); + }); + + test('render prop renders', () => { + const sampleReactNode = React.createElement('li', { id: 'li1' }, 'one'); + const example = shallow( sampleReactNode} />); + expect(example).toMatchSnapshot(); + }); +}); diff --git a/packages/shared-ux/router/impl/router.tsx b/packages/shared-ux/router/impl/router.tsx new file mode 100644 index 0000000000000..da1dc2def3fc8 --- /dev/null +++ b/packages/shared-ux/router/impl/router.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo } from 'react'; +import { + Route as ReactRouterRoute, + RouteComponentProps, + RouteProps, + useRouteMatch, +} from 'react-router-dom'; +import { useKibanaSharedUX } from './services'; +import { useSharedUXExecutionContext } from './use_execution_context'; + +/** + * This is a wrapper around the react-router-dom Route component that inserts + * MatchPropagator in every application route. It helps track all route changes + * and send them to the execution context, later used to enrich APM + * 'route-change' transactions. + */ +export const Route = ({ children, component: Component, render, ...rest }: RouteProps) => { + const component = useMemo(() => { + if (!Component) { + return undefined; + } + return (props: RouteComponentProps) => ( + <> + + + + ); + }, [Component]); + + if (component) { + return ; + } + if (render || typeof children === 'function') { + const renderFunction = typeof children === 'function' ? children : render; + return ( + ( + <> + + {/* @ts-ignore else condition exists if renderFunction is undefined*/} + {renderFunction(props)} + + )} + /> + ); + } + return ( + + + {children} + + ); +}; + +/** + * The match propogator that is part of the Route + */ +const MatchPropagator = () => { + const { executionContext } = useKibanaSharedUX().services; + const match = useRouteMatch(); + + useSharedUXExecutionContext(executionContext, { + type: 'application', + page: match.path, + id: Object.keys(match.params).length > 0 ? JSON.stringify(match.params) : undefined, + }); + + return null; +}; diff --git a/packages/shared-ux/router/impl/services.ts b/packages/shared-ux/router/impl/services.ts new file mode 100644 index 0000000000000..78150b576905b --- /dev/null +++ b/packages/shared-ux/router/impl/services.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Observable } from 'rxjs'; +import { createContext, useContext } from 'react'; +import { SharedUXExecutionContext } from './types'; + +/** + * @public Execution context start and setup types are the same + */ +export declare type SharedUXExecutionContextStart = SharedUXExecutionContextSetup; + +/** + * Reduced the interface from ExecutionContextSetup from '@kbn/core-execution-context-browser' to only include properties needed for the Route + */ +export interface SharedUXExecutionContextSetup { + /** + * The current context observable + **/ + context$: Observable; + /** + * Set the current top level context + **/ + set(c$: SharedUXExecutionContext): void; + /** + * Get the current top level context + **/ + get(): SharedUXExecutionContext; + /** + * clears the context + **/ + clear(): void; +} + +/** + * Taken from Core services exposed to the `Plugin` start lifecycle + * + * @public + * + * @internalRemarks We document the properties with + * \@link tags to improve + * navigation in the generated docs until there's a fix for + * https://github.com/Microsoft/web-build-tools/issues/1237 + */ +export interface SharedUXExecutionContextSetup { + /** {@link SharedUXExecutionContextSetup} */ + executionContext: SharedUXExecutionContextStart; +} + +export type KibanaServices = Partial; + +export interface SharedUXRouterContextValue { + readonly services: Services; +} + +const defaultContextValue = { + services: {}, +}; + +export const sharedUXContext = + createContext>(defaultContextValue); + +export const useKibanaSharedUX = (): SharedUXRouterContextValue< + KibanaServices & Extra +> => + useContext( + sharedUXContext as unknown as React.Context> + ); diff --git a/packages/shared-ux/router/impl/tsconfig.json b/packages/shared-ux/router/impl/tsconfig.json new file mode 100644 index 0000000000000..764f1f42f52f9 --- /dev/null +++ b/packages/shared-ux/router/impl/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types", + ] + }, + "include": [ + "**/*", + ] +} diff --git a/packages/shared-ux/router/impl/types.ts b/packages/shared-ux/router/impl/types.ts new file mode 100644 index 0000000000000..a76e8a87c4fe3 --- /dev/null +++ b/packages/shared-ux/router/impl/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. + */ + +/** + * @public + * Represents a meta-information about a Kibana entity initiating a search request. + */ +export declare interface SharedUXExecutionContext { + /** + * Kibana application initiated an operation. + * */ + readonly type?: string; + /** public name of an application or a user-facing feature */ + readonly name?: string; + /** a stand alone, logical unit such as an application page or tab */ + readonly page?: string; + /** unique value to identify the source */ + readonly id?: string; + /** human readable description. For example, a vis title, action name */ + readonly description?: string; + /** in browser - url to navigate to a current page, on server - endpoint path, for task: task SO url */ + readonly url?: string; + /** Metadata attached to the field. An optional parameter that allows to describe the execution context in more detail. **/ + readonly meta?: { + [key: string]: string | number | boolean | undefined; + }; + /** an inner context spawned from the current context. */ + child?: SharedUXExecutionContext; +} diff --git a/packages/shared-ux/router/impl/use_execution_context.ts b/packages/shared-ux/router/impl/use_execution_context.ts new file mode 100644 index 0000000000000..e2bb6168d1268 --- /dev/null +++ b/packages/shared-ux/router/impl/use_execution_context.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 useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect'; +import { SharedUXExecutionContextSetup } from './services'; +import { SharedUXExecutionContext } from './types'; + +/** + * Set and clean up application level execution context + * @param executionContext + * @param context + */ +export function useSharedUXExecutionContext( + executionContext: SharedUXExecutionContextSetup | undefined, + context: SharedUXExecutionContext +) { + useDeepCompareEffect(() => { + executionContext?.set(context); + + return () => { + executionContext?.clear(); + }; + }, [context]); +} diff --git a/packages/kbn-kibana-manifest-parser/BUILD.bazel b/packages/shared-ux/router/mocks/BUILD.bazel similarity index 83% rename from packages/kbn-kibana-manifest-parser/BUILD.bazel rename to packages/shared-ux/router/mocks/BUILD.bazel index aff27a65537ad..248dd93ce803b 100644 --- a/packages/kbn-kibana-manifest-parser/BUILD.bazel +++ b/packages/shared-ux/router/mocks/BUILD.bazel @@ -2,26 +2,26 @@ 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-kibana-manifest-parser" -PKG_REQUIRE_NAME = "@kbn/kibana-manifest-parser" +PKG_DIRNAME = "mocks" +PKG_REQUIRE_NAME = "@kbn/shared-ux-router-mocks" SOURCE_FILES = glob( [ - "**/*.js", "**/*.ts", + "**/*.tsx", ], exclude = [ "**/*.config.js", "**/*.mock.*", "**/*.test.*", "**/*.stories.*", - "**/__snapshots__/**", - "**/integration_tests/**", - "**/mocks/**", - "**/scripts/**", - "**/storybook/**", - "**/test_fixtures/**", - "**/test_helpers/**", + "**/__snapshots__", + "**/integration_tests", + "**/mocks", + "**/scripts", + "**/storybook", + "**/test_fixtures", + "**/test_helpers", ], ) @@ -47,6 +47,7 @@ NPM_MODULE_EXTRA_FILES = [ # "@npm//name-of-package" # eg. "@npm//lodash" RUNTIME_DEPS = [ + "@npm//react" ] # In this array place dependencies necessary to build the types, which will include the @@ -61,7 +62,7 @@ RUNTIME_DEPS = [ TYPES_DEPS = [ "@npm//@types/node", "@npm//@types/jest", - "//packages/kbn-jsonc:npm_module_types", + "@npm//@types/react" ] jsts_transpiler( @@ -70,6 +71,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,16 +94,16 @@ ts_project( deps = TYPES_DEPS, declaration = True, declaration_map = True, - allow_js = True, emit_declaration_only = True, out_dir = "target_types", + root_dir = ".", tsconfig = ":tsconfig", ) 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/shared-ux/router/mocks/README.md b/packages/shared-ux/router/mocks/README.md new file mode 100644 index 0000000000000..4aa41535f4bb2 --- /dev/null +++ b/packages/shared-ux/router/mocks/README.md @@ -0,0 +1,3 @@ +# @kbn/shared-ux-router-mocks + +Empty package generated by @kbn/generate diff --git a/packages/shared-ux/router/mocks/index.ts b/packages/shared-ux/router/mocks/index.ts new file mode 100644 index 0000000000000..b6e7485e36ab2 --- /dev/null +++ b/packages/shared-ux/router/mocks/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 function foo() { + return 'hello world'; +} diff --git a/packages/shared-ux/router/mocks/jest.config.js b/packages/shared-ux/router/mocks/jest.config.js new file mode 100644 index 0000000000000..9fbc3e5c70246 --- /dev/null +++ b/packages/shared-ux/router/mocks/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/router/mocks'], +}; diff --git a/packages/shared-ux/router/mocks/package.json b/packages/shared-ux/router/mocks/package.json new file mode 100644 index 0000000000000..d089a5d01f106 --- /dev/null +++ b/packages/shared-ux/router/mocks/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-router-mocks", + "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/router/mocks/src/index.ts b/packages/shared-ux/router/mocks/src/index.ts new file mode 100644 index 0000000000000..4687a8e2cb53f --- /dev/null +++ b/packages/shared-ux/router/mocks/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 { RouterMock } from './storybook'; +export type { RouterParams } from './storybook'; diff --git a/packages/shared-ux/router/mocks/src/storybook.ts b/packages/shared-ux/router/mocks/src/storybook.ts new file mode 100644 index 0000000000000..96c15d715cdee --- /dev/null +++ b/packages/shared-ux/router/mocks/src/storybook.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const RouterMock = undefined; +export type RouterParams = undefined; diff --git a/packages/shared-ux/router/mocks/tsconfig.json b/packages/shared-ux/router/mocks/tsconfig.json new file mode 100644 index 0000000000000..a4f1ce7985a55 --- /dev/null +++ b/packages/shared-ux/router/mocks/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": ".", + "stripInternal": false, + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ] +} diff --git a/packages/shared-ux/router/types/BUILD.bazel b/packages/shared-ux/router/types/BUILD.bazel new file mode 100644 index 0000000000000..b33071f126efe --- /dev/null +++ b/packages/shared-ux/router/types/BUILD.bazel @@ -0,0 +1,60 @@ +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 = "types" +PKG_REQUIRE_NAME = "@kbn/shared-ux-router-types" + +SRCS = glob( + [ + "*.d.ts", + ] +) + +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 = [ +] + +js_library( + name = PKG_DIRNAME, + srcs = SRCS + NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS, + 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"], +) + +alias( + name = "npm_module_types", + actual = ":" + PKG_DIRNAME, + visibility = ["//visibility:public"], +) + diff --git a/packages/shared-ux/router/types/README.md b/packages/shared-ux/router/types/README.md new file mode 100644 index 0000000000000..ad806d7d070bd --- /dev/null +++ b/packages/shared-ux/router/types/README.md @@ -0,0 +1,3 @@ +# @kbn/shared-ux-router-types + +TODO: rshen91 diff --git a/packages/shared-ux/router/types/index.d.ts b/packages/shared-ux/router/types/index.d.ts new file mode 100644 index 0000000000000..5c2d5b68ae2e0 --- /dev/null +++ b/packages/shared-ux/router/types/index.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ diff --git a/packages/kbn-jsonc/package.json b/packages/shared-ux/router/types/package.json similarity index 75% rename from packages/kbn-jsonc/package.json rename to packages/shared-ux/router/types/package.json index 73fc5acfe447c..323e9848a50a7 100644 --- a/packages/kbn-jsonc/package.json +++ b/packages/shared-ux/router/types/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/jsonc", + "name": "@kbn/shared-ux-router-types", "private": true, "version": "1.0.0", "main": "./target_node/index.js", diff --git a/packages/shared-ux/router/types/tsconfig.json b/packages/shared-ux/router/types/tsconfig.json new file mode 100644 index 0000000000000..1a57218f76493 --- /dev/null +++ b/packages/shared-ux/router/types/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [] + }, + "include": [ + "*.d.ts" + ] +} diff --git a/packages/shared-ux/storybook/config/kibana.jsonc b/packages/shared-ux/storybook/config/kibana.jsonc new file mode 100644 index 0000000000000..9a3b26cb20f83 --- /dev/null +++ b/packages/shared-ux/storybook/config/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-storybook-config", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/packages/shared-ux/storybook/mock/kibana.jsonc b/packages/shared-ux/storybook/mock/kibana.jsonc new file mode 100644 index 0000000000000..305626d6f3cdc --- /dev/null +++ b/packages/shared-ux/storybook/mock/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-storybook-mock", + "owner": "@elastic/shared-ux", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/src/core/public/core_app/status/components/status_table.test.tsx b/src/core/public/core_app/status/components/status_table.test.tsx index 1aa39325d808e..d4bfccd7d0b2a 100644 --- a/src/core/public/core_app/status/components/status_table.test.tsx +++ b/src/core/public/core_app/status/components/status_table.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { ServiceStatus } from '../../../../types/status'; +import type { StatusInfoServiceStatus as ServiceStatus } from '@kbn/core-status-common-internal'; import { StatusTable } from './status_table'; const state = { diff --git a/src/core/public/core_app/status/components/version_header.test.tsx b/src/core/public/core_app/status/components/version_header.test.tsx index 172a720a51751..bfe2d6a2c40de 100644 --- a/src/core/public/core_app/status/components/version_header.test.tsx +++ b/src/core/public/core_app/status/components/version_header.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers'; -import type { ServerVersion } from '../../../../types/status'; +import type { ServerVersion } from '@kbn/core-status-common-internal'; import { VersionHeader } from './version_header'; const buildServerVersion = (parts: Partial = {}): ServerVersion => ({ diff --git a/src/core/public/core_app/status/components/version_header.tsx b/src/core/public/core_app/status/components/version_header.tsx index c9b4109cf1f78..81b76f771cda1 100644 --- a/src/core/public/core_app/status/components/version_header.tsx +++ b/src/core/public/core_app/status/components/version_header.tsx @@ -14,7 +14,7 @@ import { EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ServerVersion } from '../../../../types/status'; +import type { ServerVersion } from '@kbn/core-status-common-internal'; interface VersionHeaderProps { version: ServerVersion; diff --git a/src/core/public/core_app/status/lib/load_status.test.ts b/src/core/public/core_app/status/lib/load_status.test.ts index f14e250fec4b5..ac40eedfccb7d 100644 --- a/src/core/public/core_app/status/lib/load_status.test.ts +++ b/src/core/public/core_app/status/lib/load_status.test.ts @@ -7,7 +7,7 @@ */ import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import type { StatusResponse } from '../../../../types/status'; +import type { StatusResponse } from '@kbn/core-status-common-internal'; import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; import { mocked } from '@kbn/core-metrics-collectors-server-mocks'; import { loadStatus } from './load_status'; diff --git a/src/core/public/core_app/status/lib/load_status.ts b/src/core/public/core_app/status/lib/load_status.ts index 6a5dfa7ad3e45..5a4b2b5907ea2 100644 --- a/src/core/public/core_app/status/lib/load_status.ts +++ b/src/core/public/core_app/status/lib/load_status.ts @@ -9,7 +9,11 @@ import { i18n } from '@kbn/i18n'; import type { HttpSetup } from '@kbn/core-http-browser'; import type { NotificationsSetup } from '@kbn/core-notifications-browser'; -import type { StatusResponse, ServiceStatus, ServiceStatusLevel } from '../../../../types/status'; +import type { ServiceStatusLevelId } from '@kbn/core-status-common'; +import type { + StatusResponse, + StatusInfoServiceStatus as ServiceStatus, +} from '@kbn/core-status-common-internal'; import type { DataType } from '.'; interface MetricMeta { @@ -18,6 +22,7 @@ interface MetricMeta { value?: number[]; type?: DataType; } + export interface Metric { name: string; value: number | number[]; @@ -32,7 +37,7 @@ export interface FormattedStatus { } export interface StatusState { - id: ServiceStatusLevel; + id: ServiceStatusLevelId; title: string; message: string; uiColor: string; @@ -144,7 +149,7 @@ function formatStatus(id: string, status: ServiceStatus): FormattedStatus { }; } -export const STATUS_LEVEL_UI_ATTRS: Record = { +export const STATUS_LEVEL_UI_ATTRS: Record = { critical: { title: i18n.translate('core.status.redTitle', { defaultMessage: 'Red', diff --git a/src/core/public/core_app/status/lib/status_level.test.ts b/src/core/public/core_app/status/lib/status_level.test.ts index 44322748992a0..d58144a91d939 100644 --- a/src/core/public/core_app/status/lib/status_level.test.ts +++ b/src/core/public/core_app/status/lib/status_level.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { ServiceStatus } from '../../../../types/status'; +import type { StatusInfoServiceStatus as ServiceStatus } from '@kbn/core-status-common-internal'; import { getLevelSortValue, groupByLevel, getHighestStatus } from './status_level'; import { FormattedStatus, StatusState } from './load_status'; diff --git a/src/core/public/core_app/status/lib/status_level.ts b/src/core/public/core_app/status/lib/status_level.ts index f0c3be949057f..d6b3033cfcc91 100644 --- a/src/core/public/core_app/status/lib/status_level.ts +++ b/src/core/public/core_app/status/lib/status_level.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import type { ServiceStatusLevel } from '../../../../types/status'; +import type { ServiceStatusLevelId } from '@kbn/core-status-common'; import { FormattedStatus, StatusState, STATUS_LEVEL_UI_ATTRS } from './load_status'; -export const orderedLevels: ServiceStatusLevel[] = [ +export const orderedLevels: ServiceStatusLevelId[] = [ 'critical', 'unavailable', 'degraded', @@ -21,7 +21,7 @@ export const groupByLevel = (statuses: FormattedStatus[]) => { const existing = map.get(status.state.id) ?? []; map.set(status.state.id, [...existing, status]); return map; - }, new Map()); + }, new Map()); }; export const getHighestStatus = (statuses: FormattedStatus[]): Omit => { diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 6c539dd1f20f6..3aa3cbd52c4e4 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -66,12 +66,12 @@ import type { } from '@kbn/core-saved-objects-server'; import type { DeprecationsServiceSetup } from '@kbn/core-deprecations-server'; import type { CoreUsageDataStart, CoreUsageDataSetup } from '@kbn/core-usage-data-server'; +import type { I18nServiceSetup } from '@kbn/core-i18n-server'; +import type { StatusServiceSetup } from '@kbn/core-status-server'; -import { I18nServiceSetup } from '@kbn/core-i18n-server'; import { HttpResources } from './http_resources'; import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins'; import { UiSettingsServiceSetup, UiSettingsServiceStart } from './ui_settings'; -import { StatusServiceSetup } from './status'; import type { CoreRequestHandlerContext } from './core_route_handler_context'; import type { PrebootCoreRequestHandlerContext } from './preboot_core_route_handler_context'; @@ -433,8 +433,9 @@ export type { DeprecationsDetails } from '@kbn/core-deprecations-common'; export type { AppCategory } from '@kbn/core-application-common'; export { DEFAULT_APP_CATEGORIES, APP_WRAPPER_CLASS } from '@kbn/core-application-common'; -export { ServiceStatusLevels } from './status'; -export type { CoreStatus, ServiceStatus, ServiceStatusLevel, StatusServiceSetup } from './status'; +export { ServiceStatusLevels } from '@kbn/core-status-common'; +export type { CoreStatus, ServiceStatus, ServiceStatusLevel } from '@kbn/core-status-common'; +export type { StatusServiceSetup } from '@kbn/core-status-server'; export type { DocLinksServiceStart, DocLinksServiceSetup } from '@kbn/core-doc-links-server'; diff --git a/src/core/server/integration_tests/status/routes/status.test.ts b/src/core/server/integration_tests/status/routes/status.test.ts index 980bae6cfd2bb..27e18979e3df0 100644 --- a/src/core/server/integration_tests/status/routes/status.test.ts +++ b/src/core/server/integration_tests/status/routes/status.test.ts @@ -15,13 +15,13 @@ import { createCoreContext, createHttpServer } from '@kbn/core-http-server-mocks import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; import { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; import type { MetricsServiceSetup } from '@kbn/core-metrics-server'; - -import { registerStatusRoute } from '../../../status/routes/status'; -import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from '../../../status/types'; -import { statusServiceMock } from '../../../status/status_service.mock'; +import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from '@kbn/core-status-common'; +import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks'; import { contextServiceMock } from '@kbn/core-http-context-server-mocks'; +import { registerStatusRoute } from '@kbn/core-status-server-internal'; + const coreId = Symbol('core'); const createServiceStatus = ( diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index 4f655a9e0f3c8..4a74d2e0aadcb 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -52,6 +52,7 @@ import { import type { CoreUsageDataStart } from '@kbn/core-usage-data-server'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { I18nServiceSetup } from '@kbn/core-i18n-server'; +import type { InternalStatusServiceSetup } from '@kbn/core-status-server-internal'; import { InternalUiSettingsServicePreboot, InternalUiSettingsServiceSetup, @@ -59,7 +60,6 @@ import { } from './ui_settings'; import { InternalRenderingServiceSetup } from './rendering'; import { InternalHttpResourcesPreboot, InternalHttpResourcesSetup } from './http_resources'; -import { InternalStatusServiceSetup } from './status'; /** @internal */ export interface InternalCorePreboot { diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 5efbbdfbad39b..66ff3aea68b37 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -29,6 +29,7 @@ import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks import { deprecationsServiceMock } from '@kbn/core-deprecations-server-mocks'; import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { i18nServiceMock } from '@kbn/core-i18n-server-mocks'; +import { statusServiceMock } from '@kbn/core-status-server-mocks'; import type { PluginInitializerContext, CoreSetup, @@ -41,7 +42,6 @@ import { httpResourcesMock } from './http_resources/http_resources_service.mock' import { renderingMock } from './rendering/rendering_service.mock'; import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { SharedGlobalConfig } from './plugins'; -import { statusServiceMock } from './status/status_service.mock'; export { configServiceMock, configDeprecationsMock } from '@kbn/config-mocks'; export { loggingSystemMock } from '@kbn/core-logging-server-mocks'; @@ -58,7 +58,7 @@ export { migrationMocks } from '@kbn/core-saved-objects-migration-server-mocks'; export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; export { renderingMock } from './rendering/rendering_service.mock'; -export { statusServiceMock } from './status/status_service.mock'; +export { statusServiceMock } from '@kbn/core-status-server-mocks'; export { contextServiceMock } from '@kbn/core-http-context-server-mocks'; export { capabilitiesServiceMock } from '@kbn/core-capabilities-server-mocks'; export { deprecationsServiceMock } from '@kbn/core-deprecations-server-mocks'; diff --git a/src/core/server/rendering/__mocks__/params.ts b/src/core/server/rendering/__mocks__/params.ts index 71b4996590c64..c75353b87a65d 100644 --- a/src/core/server/rendering/__mocks__/params.ts +++ b/src/core/server/rendering/__mocks__/params.ts @@ -9,8 +9,8 @@ import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { pluginServiceMock } from '../../plugins/plugins_service.mock'; -import { statusServiceMock } from '../../status/status_service.mock'; const context = mockCoreContext.create(); const httpPreboot = httpServiceMock.createInternalPrebootContract(); diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 0c5382627e9b6..60059637beb58 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -15,9 +15,9 @@ import type { InternalHttpServiceSetup, } from '@kbn/core-http-server-internal'; import type { InternalElasticsearchServiceSetup } from '@kbn/core-elasticsearch-server-internal'; +import type { InternalStatusServiceSetup } from '@kbn/core-status-server-internal'; import { UiPlugins } from '../plugins'; import { IUiSettingsClient } from '../ui_settings'; -import type { InternalStatusServiceSetup } from '../status'; /** @internal */ export interface RenderingMetadata { diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index a6cce9aafde23..0b055ea5a8cf5 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -89,10 +89,10 @@ jest.doMock('@kbn/core-metrics-server-internal', () => ({ MetricsService: jest.fn(() => mockMetricsService), })); -import { statusServiceMock } from './status/status_service.mock'; +import { statusServiceMock } from '@kbn/core-status-server-mocks'; export const mockStatusService = statusServiceMock.create(); -jest.doMock('./status/status_service', () => ({ +jest.doMock('@kbn/core-status-server-internal', () => ({ StatusService: jest.fn(() => mockStatusService), })); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index a6619f33e84f0..751da1845224d 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -56,16 +56,14 @@ import { config as deprecationConfig, } from '@kbn/core-deprecations-server-internal'; import { CoreUsageDataService } from '@kbn/core-usage-data-server-internal'; +import { StatusService, statusConfig } from '@kbn/core-status-server-internal'; import { CoreApp } from './core_app'; import { HttpResourcesService } from './http_resources'; import { RenderingService } from './rendering'; import { UiSettingsService } from './ui_settings'; import { PluginsService, config as pluginsConfig } from './plugins'; -// do not try to shorten the import to `./status`, it will break server test mocking -import { StatusService } from './status/status_service'; import { config as uiSettingsConfig } from './ui_settings'; -import { config as statusConfig } from './status'; import { InternalCorePreboot, InternalCoreSetup, InternalCoreStart } from './internal_types'; import { CoreRouteHandlerContext } from './core_route_handler_context'; import { PrebootCoreRouteHandlerContext } from './preboot_core_route_handler_context'; diff --git a/src/core/server/test_utils.ts b/src/core/server/test_utils.ts index c96e168e16291..95a05aca5957b 100644 --- a/src/core/server/test_utils.ts +++ b/src/core/server/test_utils.ts @@ -7,7 +7,6 @@ */ export { createHttpServer } from '@kbn/core-http-server-mocks'; -export { ServiceStatusLevelSnapshotSerializer } from './status/test_utils'; export { setupServer } from './integration_tests/saved_objects/routes/test_utils'; export { getDeprecationsFor, diff --git a/src/dev/build/tasks/build_packages_task.ts b/src/dev/build/tasks/build_packages_task.ts index 521b1299f423d..fdb32731fdd8e 100644 --- a/src/dev/build/tasks/build_packages_task.ts +++ b/src/dev/build/tasks/build_packages_task.ts @@ -23,7 +23,7 @@ export const BuildBazelPackages: Task = { await runBazel(['build', '//packages:build']); for (const pkg of packages) { - log.info(`Copying build of`, pkg.pkg.name, 'into build'); + log.info(`Copying build of`, pkg.manifest.id, 'into build'); const pkgDirInBuild = build.resolvePath(pkg.normalizedRepoRelativeDir); @@ -36,7 +36,23 @@ export const BuildBazelPackages: Task = { permissions: (rec) => (rec.isDirectory ? 0o755 : 0o644), }); - await write(Path.resolve(pkgDirInBuild, 'package.json'), JSON.stringify(pkg.pkg, null, 2)); + await write( + Path.resolve(pkgDirInBuild, 'kibana.jsonc'), + JSON.stringify(pkg.manifest, null, 2) + ); + await write( + Path.resolve(pkgDirInBuild, 'package.json'), + JSON.stringify( + { + ...pkg.pkg, + name: pkg.manifest.id, + version: config.getBuildVersion(), + private: undefined, + }, + null, + 2 + ) + ); } }, }; diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts index 409f6a77b50bf..bc4e5bca05675 100644 --- a/src/dev/build/tasks/clean_tasks.ts +++ b/src/dev/build/tasks/clean_tasks.ts @@ -260,9 +260,15 @@ export const DeleteBazelPackagesFromBuildRoot: Task = { '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(REPO_ROOT)).map((pkg) => - build.resolvePath(pkg.normalizedRepoRelativeDir) - ); + const bazelPackagesOnBuildRoot = (await discoverBazelPackages(REPO_ROOT)).flatMap((pkg) => { + const bldSrc = build.resolvePath(pkg.normalizedRepoRelativeDir); + + if (pkg.manifest.type.startsWith('plugin-')) { + return bldSrc; + } + + return [bldSrc, build.resolvePath('node_modules', pkg.manifest.id, 'kibana.jsonc')]; + }); await deleteAll(bazelPackagesOnBuildRoot, log); }, diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js index 6d5962f7f51e8..14f7cefc78cae 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js @@ -34,11 +34,4 @@ describe(`enumeratePatterns`, () => { 'src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app' ); }); - it(`should resolve x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/translations.ts to kibana-security`, () => { - const short = - 'x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout'; - const actual = enumeratePatterns(REPO_ROOT)(log)(new Map([[short, ['kibana-security']]])); - - expect(actual.flat()).toContain(`${short}/translations.ts kibana-security`); - }); }); diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx index 05a0266e676d0..539b41950a23c 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx @@ -321,12 +321,6 @@ export const MetricVis = ({ ); }, [grid.length, scrollDimensions.height]); - // force chart to re-render to circumvent a charts bug - const magicKey = useRef(0); - useEffect(() => { - magicKey.current++; - }, [data]); - return (
- + { + return { + name: 'event_annotations_result', + aliases: [], + type: 'event_annotations_result', + inputTypes: ['null'], + help: strings.getAnnotationLayerFnHelp(), + args: { + layers: { + types: [EXTENDED_ANNOTATION_LAYER], + multi: true, + help: strings.getAnnotationLayerFnHelp(), + }, + datatable: { + types: ['datatable'], + help: strings.getAnnotationLayerFnHelp(), + }, + }, + fn: (input, args) => { + return { + ...args, + type: 'event_annotations_result', + layers: args.layers || [], + datatable: args.datatable || {}, + }; + }, + }; +} 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 index 7c361000f7823..5098be2aa2705 100644 --- 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 @@ -6,14 +6,14 @@ * Side Public License, v 1. */ -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import type { 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, + null, ExtendedAnnotationLayerArgs, ExtendedAnnotationLayerConfigResult > { @@ -21,7 +21,7 @@ export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< name: EXTENDED_ANNOTATION_LAYER, aliases: [], type: EXTENDED_ANNOTATION_LAYER, - inputTypes: ['datatable'], + inputTypes: ['null'], help: strings.getAnnotationLayerFnHelp(), args: { simpleView: { 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 index 392c9a0d5830a..0ca9a3ac4e5cc 100644 --- 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 @@ -12,7 +12,6 @@ import { EXTENDED_DATA_LAYER, REFERENCE_LINE_LAYER, LAYERED_XY_VIS, - EXTENDED_ANNOTATION_LAYER, REFERENCE_LINE, } from '../constants'; import { commonXYArgs } from './common_xy_args'; @@ -26,12 +25,18 @@ export const layeredXyVisFunction: LayeredXyVisFn = { args: { ...commonXYArgs, layers: { - types: [EXTENDED_DATA_LAYER, REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER, REFERENCE_LINE], + types: [EXTENDED_DATA_LAYER, REFERENCE_LINE_LAYER, REFERENCE_LINE], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { defaultMessage: 'Layers of visual series', }), multi: true, }, + annotations: { + types: ['event_annotations_result'], + help: i18n.translate('expressionXY.layeredXyVis.annotations.help', { + defaultMessage: 'Annotations', + }), + }, splitColumnAccessor: { types: ['vis_dimension', 'string'], help: strings.getSplitColumnAccessorHelp(), 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 0b81682eb4381..7e6afa0dd23a7 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 @@ -18,7 +18,7 @@ describe('xyVis', () => { const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer; const result = await xyVisFunction.fn( data, - { ...rest, ...restLayerArgs, referenceLines: [], annotationLayers: [] }, + { ...rest, ...restLayerArgs, referenceLines: [] }, createMockExecutionContext() ); @@ -53,7 +53,6 @@ describe('xyVis', () => { ...{ ...sampleLayer, markSizeAccessor: 'b' }, markSizeRatio: 0, referenceLines: [], - annotationLayers: [], }, createMockExecutionContext() ) @@ -67,7 +66,6 @@ describe('xyVis', () => { ...{ ...sampleLayer, markSizeAccessor: 'b' }, markSizeRatio: 101, referenceLines: [], - annotationLayers: [], }, createMockExecutionContext() ) @@ -86,7 +84,6 @@ describe('xyVis', () => { ...restLayerArgs, minTimeBarInterval: '1q', referenceLines: [], - annotationLayers: [], }, createMockExecutionContext() ) @@ -105,7 +102,6 @@ describe('xyVis', () => { ...restLayerArgs, minTimeBarInterval: '1h', referenceLines: [], - annotationLayers: [], }, createMockExecutionContext() ) @@ -124,7 +120,6 @@ describe('xyVis', () => { ...restLayerArgs, addTimeMarker: true, referenceLines: [], - annotationLayers: [], }, createMockExecutionContext() ) @@ -144,7 +139,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + splitRowAccessor, }, createMockExecutionContext() @@ -165,7 +160,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + splitColumnAccessor, }, createMockExecutionContext() @@ -185,7 +180,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + markSizeRatio: 5, }, createMockExecutionContext() @@ -207,7 +202,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + seriesType: 'bar', showLines: true, }, @@ -230,7 +225,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + isHistogram: true, xScaleType: 'time', xAxisConfig: { @@ -257,7 +252,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + xAxisConfig: { type: 'xAxisConfig', extent: { @@ -287,7 +282,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + xAxisConfig: { type: 'xAxisConfig', extent: { type: 'axisExtentConfig', mode: 'dataBounds' }, @@ -308,7 +303,7 @@ describe('xyVis', () => { ...rest, ...restLayerArgs, referenceLines: [], - annotationLayers: [], + isHistogram: true, xAxisConfig: { type: 'xAxisConfig', 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 9db238a117b75..0b00525e3c914 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 @@ -7,7 +7,7 @@ */ import { XyVisFn } from '../types'; -import { XY_VIS, REFERENCE_LINE, ANNOTATION_LAYER } from '../constants'; +import { XY_VIS, REFERENCE_LINE } from '../constants'; import { strings } from '../i18n'; import { commonXYArgs } from './common_xy_args'; import { commonDataLayerArgs } from './common_data_layer_args'; @@ -39,11 +39,6 @@ export const xyVisFunction: XyVisFn = { help: strings.getReferenceLinesHelp(), multi: true, }, - annotationLayers: { - types: [ANNOTATION_LAYER], - help: strings.getAnnotationLayerHelp(), - multi: true, - }, splitColumnAccessor: { types: ['vis_dimension', 'string'], help: strings.getSplitColumnAccessorHelp(), 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 index 2808b861c6df8..defda933784de 100644 --- 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 @@ -63,7 +63,6 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const { referenceLines = [], - annotationLayers = [], // data_layer args seriesType, accessors, @@ -101,7 +100,6 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const layers: XYLayerConfig[] = [ ...appendLayerIds(dataLayers, 'dataLayers'), ...appendLayerIds(referenceLines, 'referenceLines'), - ...appendLayerIds(annotationLayers, 'annotationLayers'), ]; logDatatable(data, layers, handlers, args.splitColumnAccessor, args.splitRowAccessor); diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index da4c969f47a0c..b39002c8549cb 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -34,7 +34,7 @@ export type { DataLayerConfig, FittingFunction, AxisExtentConfig, - CollectiveConfig, + MergedAnnotation, LegendConfigResult, AxesSettingsConfig, XAxisConfigResult, 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 0970cec985d30..43d05eda09b23 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 @@ -215,7 +215,6 @@ export interface XYArgs extends DataLayerArgs { emphasizeFitting?: boolean; valueLabels: ValueLabelMode; referenceLines: ReferenceLineConfigResult[]; - annotationLayers: AnnotationLayerConfigResult[]; fittingFunction?: FittingFunction; fillOpacity?: number; hideEndzones?: boolean; @@ -233,12 +232,21 @@ export interface XYArgs extends DataLayerArgs { showTooltip: boolean; } +export interface ExpressionAnnotationsLayers { + layers: AnnotationLayerConfigResult[]; + datatable: Datatable; +} +export type ExpressionAnnotationResult = ExpressionAnnotationsLayers & { + type: 'event_annotations_result'; +}; + export interface LayeredXYArgs { legend: LegendConfigResult; endValue?: EndValue; emphasizeFitting?: boolean; valueLabels: ValueLabelMode; layers?: XYExtendedLayerConfigResult[]; + annotations?: ExpressionAnnotationResult; fittingFunction?: FittingFunction; fillOpacity?: number; hideEndzones?: boolean; @@ -279,6 +287,7 @@ export interface XYProps { orderBucketsBySum?: boolean; showTooltip: boolean; singleTable?: boolean; + annotations?: ExpressionAnnotationResult; } export interface AnnotationLayerArgs { @@ -326,7 +335,6 @@ export type XYExtendedLayerConfig = export type XYExtendedLayerConfigResult = | ExtendedDataLayerConfigResult | ReferenceLineLayerConfigResult - | ExtendedAnnotationLayerConfigResult | ReferenceLineConfigResult; export interface ExtendedReferenceLineDecorationConfig extends ReferenceLineArgs { 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 8ee27ad7eab58..431c2721fa1c1 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 @@ -24,9 +24,10 @@ export interface XYRender { value: XYChartProps; } -export interface CollectiveConfig extends Omit { +export interface MergedAnnotation extends Omit { timebucket: number; position: 'bottom'; icon?: AvailableAnnotationIcon | string; - customTooltipDetails?: AnnotationTooltipFormatter | undefined; + customTooltipDetails: AnnotationTooltipFormatter; + isGrouped: boolean; } 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 43dbace37905c..f178c870cffd8 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 @@ -2,6 +2,7 @@ exports[`XYChart component annotations should render basic line annotation 1`] = ` } markerPosition="top" + placement="bottom" style={ Object { "line": Object { @@ -101,9 +105,9 @@ exports[`XYChart component annotations should render grouped line annotations pr dataValues={ Array [ Object { - "dataValue": 1647591900025, + "dataValue": 1647591917125, "details": "Event 1", - "header": 1647591900000, + "header": 1647591917100, }, ] } @@ -118,19 +122,19 @@ exports[`XYChart component annotations should render grouped line annotations pr "customTooltipDetails": [Function], "icon": "3", "id": "event1", + "isGrouped": true, "label": "Event 1", "lineStyle": "dashed", "lineWidth": 3, "position": "bottom", "textVisibility": undefined, "time": "2022-03-18T08:25:00.000Z", - "timebucket": 1647591900000, + "timebucket": 1647591917100, "type": "point", } } hasReducedPadding={true} isHorizontal={true} - label="Event 1" /> } markerBody={ @@ -139,6 +143,7 @@ exports[`XYChart component annotations should render grouped line annotations pr /> } markerPosition="top" + placement="bottom" style={ Object { "line": Object { @@ -161,9 +166,9 @@ exports[`XYChart component annotations should render grouped line annotations wi dataValues={ Array [ Object { - "dataValue": 1647591900025, + "dataValue": 1647591917125, "details": "Event 1", - "header": 1647591900000, + "header": 1647591917100, }, ] } @@ -178,19 +183,19 @@ exports[`XYChart component annotations should render grouped line annotations wi "customTooltipDetails": [Function], "icon": "2", "id": "event1", + "isGrouped": true, "label": "Event 1", "lineStyle": "solid", "lineWidth": 1, "position": "bottom", "textVisibility": undefined, "time": "2022-03-18T08:25:00.000Z", - "timebucket": 1647591900000, + "timebucket": 1647591917100, "type": "point", } } hasReducedPadding={true} isHorizontal={true} - label="Event 1" /> } markerBody={ @@ -199,6 +204,7 @@ exports[`XYChart component annotations should render grouped line annotations wi /> } markerPosition="top" + placement="bottom" style={ Object { "line": Object { @@ -214,6 +220,7 @@ exports[`XYChart component annotations should render grouped line annotations wi exports[`XYChart component annotations should render simplified annotations when simpleView is true 1`] = ` ( +
+ + {hasIcon(icon) && ( + + + + )} + {label} + +
+); + +const TooltipAnnotationDetails = ({ + row, + extraFields, + isGrouped, +}: { + row: PointEventAnnotationRow; + extraFields: Array<{ + key: string; + name: string; + formatter: FieldFormat | undefined; + }>; + isGrouped?: boolean; +}) => { + return ( +
+ + {isGrouped &&
{moment(row.time).format('YYYY-MM-DD, hh:mm:ss')}
} + +
+ {extraFields.map((field) => ( +
+ {field.name}:{' '} + {field.formatter ? field.formatter.convert(row[field.key]) : row[field.key]} +
+ ))} +
+
+
+ ); +}; + +const getExtraFields = ( + row: PointEventAnnotationRow, + formatFactory: FormatFactory, + columns: DatatableColumn[] | undefined +) => { + return Object.keys(row) + .filter((key) => key.startsWith('field:')) + .map((key) => { + const columnFormatter = columns?.find((c) => c.id === key)?.meta?.params; + return { + key, + name: key.replace('field:', ''), + formatter: columnFormatter && formatFactory(columnFormatter), + }; + }); +}; + const createCustomTooltipDetails = ( - config: ManualPointEventAnnotationArgs[], - formatter?: FieldFormat - ): AnnotationTooltipFormatter | undefined => + rows: PointEventAnnotationRow[], + formatFactory: FormatFactory, + columns: DatatableColumn[] | undefined + ): AnnotationTooltipFormatter => () => { + const groupedConfigs = groupBy(rows, 'id'); + const lastElement = rows[rows.length - 1]; return ( -
- {config.map(({ icon, label, time, color }) => ( -
- - {hasIcon(icon) && ( - - - - )} - {label} - - {formatter?.convert(time) || String(time)} +
+ {Object.values(groupedConfigs).map((group) => { + const firstElement = group[0]; + const extraFields = getExtraFields(firstElement, formatFactory, columns); + + return ( +
+ + + {group.map((row, index) => ( + <> + {index > 0 && ( + <> + + + + + )} + 1} + row={row} + extraFields={extraFields} + /> + + ))} + +
+ ); + })} + {lastElement.skippedCount && ( +
+
- ))} + )}
); }; @@ -109,10 +217,12 @@ export const OUTSIDE_RECT_ANNOTATION_WIDTH = 8; export const OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION = 2; export const getAnnotationsGroupedByInterval = ( - annotations: ManualPointEventAnnotationRow[], - formatter?: FieldFormat + annotations: PointEventAnnotationRow[], + configs: EventAnnotationOutput[] | undefined, + columns: DatatableColumn[] | undefined, + formatFactory: FormatFactory ) => { - const visibleGroupedConfigs = annotations.reduce>( + const visibleGroupedConfigs = annotations.reduce>( (acc, current) => { const timebucket = moment(current.timebucket).valueOf(); return { @@ -122,24 +232,36 @@ export const getAnnotationsGroupedByInterval = ( }, {} ); - let collectiveConfig: CollectiveConfig; - return Object.entries(visibleGroupedConfigs).map(([timebucket, configArr]) => { - collectiveConfig = { - ...configArr[0], - icon: configArr[0].icon || 'triangle', + return Object.entries(visibleGroupedConfigs).map(([timebucket, rowsPerBucket]) => { + const firstRow = rowsPerBucket[0]; + + const config = configs?.find((c) => c.id === firstRow.id); + const textField = config && 'textField' in config && config?.textField; + const columnFormatter = columns?.find((c) => c.id === `field:${textField}`)?.meta?.params; + const formatter = columnFormatter && formatFactory(columnFormatter); + const label = + textField && formatter && `field:${textField}` in firstRow + ? formatter.convert(firstRow[`field:${textField}`]) + : firstRow.label; + const mergedAnnotation: MergedAnnotation = { + ...firstRow, + label, + icon: firstRow.icon || 'triangle', timebucket: Number(timebucket), position: 'bottom', + customTooltipDetails: createCustomTooltipDetails(rowsPerBucket, formatFactory, columns), + isGrouped: false, }; - if (configArr.length > 1) { - const commonStyles = getCommonStyles(configArr); - collectiveConfig = { - ...collectiveConfig, + if (rowsPerBucket.length > 1) { + const commonStyles = getCommonStyles(rowsPerBucket); + return { + ...mergedAnnotation, ...commonStyles, - icon: String(configArr.length), - customTooltipDetails: createCustomTooltipDetails(configArr, formatter), + isGrouped: true, + icon: String(rowsPerBucket.length), }; } - return collectiveConfig; + return mergedAnnotation; }); }; @@ -161,20 +283,10 @@ export const Annotations = ({ <> {groupedLineAnnotations.map((annotation) => { const markerPositionVertical = Position.Top; - const markerPosition = isHorizontal - ? mapVerticalToHorizontalPlacement(markerPositionVertical) - : markerPositionVertical; const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const id = snakeCase(`${annotation.id}-${annotation.time}`); - const { timebucket, time } = annotation; - const isGrouped = Boolean(annotation.customTooltipDetails); - const header = - formatter?.convert(isGrouped ? timebucket : time) || - moment(isGrouped ? timebucket : time).toISOString(); + const { timebucket, time, isGrouped, id: configId } = annotation; const strokeWidth = simpleView ? 1 : annotation.lineWidth || 1; - const dataValue = isGrouped - ? moment(isBarChart && minInterval ? timebucket + minInterval / 2 : timebucket).valueOf() - : moment(time).valueOf(); + const id = snakeCase(`${configId}-${time}`); return ( @@ -197,21 +309,34 @@ export const Annotations = ({ !simpleView ? ( ) : undefined } - markerPosition={markerPosition} + markerPosition={ + isHorizontal + ? mapVerticalToHorizontalPlacement(markerPositionVertical) + : markerPositionVertical + } dataValues={[ { - dataValue, - header, + dataValue: isGrouped + ? moment( + isBarChart && minInterval ? timebucket + minInterval / 2 : timebucket + ).valueOf() + : moment(time).valueOf(), + header: + formatter?.convert(isGrouped ? timebucket : time) || + moment(isGrouped ? timebucket : time).toISOString(), details: annotation.label, }, ]} customTooltipDetails={annotation.customTooltipDetails} + placement={'bottom'} style={{ line: { strokeWidth, 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 387d1a077a747..279e1e846ef85 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 @@ -54,13 +54,8 @@ import { sampleLayer, } from '../../common/__mocks__'; import { XYChart, XYChartRenderProps } from './xy_chart'; -import { - CommonXYAnnotationLayerConfig, - ExtendedDataLayerConfig, - XYProps, -} from '../../common/types'; +import { ExtendedDataLayerConfig, XYProps, AnnotationLayerConfigResult } from '../../common/types'; import { DataLayers } from './data_layers'; -import { Annotations } from './annotations'; import { SplitChart } from './split_chart'; import { LegendSize } from '@kbn/visualizations-plugin/common'; @@ -3068,12 +3063,18 @@ describe('XYChart component', () => { label: 'Event range', type: 'manual_range_event_annotation' as const, }; + const configToRowHelper = (config: EventAnnotationOutput) => { + return { + ...config, + timebucket: 1647591917100, + type: config.type === 'manual_point_event_annotation' ? 'point' : 'range', + }; + }; const createLayerWithAnnotations = ( annotations: EventAnnotationOutput[] = [defaultLineStaticAnnotation] - ): CommonXYAnnotationLayerConfig => ({ + ): AnnotationLayerConfigResult => ({ type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', annotations, }); function sampleArgsWithAnnotations(annotationLayers = [createLayerWithAnnotations()]) { @@ -3081,7 +3082,16 @@ describe('XYChart component', () => { return { args: { ...args, - layers: [dateHistogramLayer, ...annotationLayers], + layers: [dateHistogramLayer], + annotations: { + type: 'event_annotations_result' as const, + layers: annotationLayers, + datatable: { + type: 'datatable' as const, + columns: [], + rows: annotationLayers.flatMap((l) => l.annotations.map(configToRowHelper)), + }, + }, }, }; } @@ -3102,7 +3112,7 @@ describe('XYChart component', () => { const { args } = sampleArgsWithAnnotations([ createLayerWithAnnotations([defaultLineStaticAnnotation, defaultRangeStaticAnnotation]), ]); - (args.layers[1] as CommonXYAnnotationLayerConfig).simpleView = true; + args.annotations.layers[0].simpleView = true; const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); expect(component.find('RectAnnotation')).toMatchSnapshot(); @@ -3135,7 +3145,7 @@ describe('XYChart component', () => { // checking tooltip const renderLinks = mount(
{groupedAnnotation.prop('customTooltipDetails')!()}
); expect(renderLinks.text()).toEqual( - ' Event 1 2022-03-18T08:25:00.000Z Event 3 2022-03-18T08:25:00.001Z Event 2 2022-03-18T08:25:00.020Z' + ' Event 12022-03-18, 04:25:002022-03-18, 04:25:002022-03-18, 04:25:00' ); }); @@ -3161,28 +3171,6 @@ describe('XYChart component', () => { // styles are default because they are different for both annotations expect(groupedAnnotation).toMatchSnapshot(); }); - test('should not render hidden annotations', () => { - 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 lineAnnotations = component.find(LineAnnotation); - const rectAnnotations = component.find(Annotations).find(RectAnnotation); - - expect(lineAnnotations.length).toEqual(2); - expect(rectAnnotations.length).toEqual(1); - }); }); describe('split chart', () => { 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 62f67549f7df6..9fb415308635e 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 @@ -29,19 +29,13 @@ import { XYChartElementEvent, } from '@elastic/charts'; import { partition } from 'lodash'; -import moment from 'moment'; import { IconType } from '@elastic/eui'; import { PaletteRegistry } from '@kbn/coloring'; -import { Datatable, DatatableRow, RenderMode } from '@kbn/expressions-plugin/common'; +import { Datatable, RenderMode } from '@kbn/expressions-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EmptyPlaceholder, LegendToggle } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import { - ManualPointEventAnnotationRow, - ManualRangeEventAnnotationOutput, - ManualPointEventAnnotationOutput, - ManualRangeEventAnnotationRow, -} from '@kbn/event-annotation-plugin/common'; +import { PointEventAnnotationRow } from '@kbn/event-annotation-plugin/common'; import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; import { @@ -61,11 +55,9 @@ import type { ExtendedReferenceLineDecorationConfig, XYChartProps, AxisExtentConfigResult, - CommonXYAnnotationLayerConfig, } from '../../common/types'; import { isHorizontalChart, - getAnnotationsLayers, getDataLayers, AxisConfiguration, getAxisPosition, @@ -192,49 +184,6 @@ function createSplitPoint( export const XYChartReportable = React.memo(XYChart); -// TODO: remove this function when we start using fetch_event_annotation expression -const convertToAnnotationsTable = ( - layers: CommonXYAnnotationLayerConfig[], - minInterval?: number, - firstTimestamp?: number -) => { - return layers - .flatMap(({ annotations }) => - annotations.filter( - (a): a is ManualPointEventAnnotationOutput | ManualRangeEventAnnotationOutput => - !a.isHidden && 'time' in a - ) - ) - .sort((a, b) => moment(a.time).valueOf() - moment(b.time).valueOf()) - .map((a) => { - const timebucket = getRoundedTimestamp(moment(a.time).valueOf(), firstTimestamp, minInterval); - if (a.type === 'manual_point_event_annotation') { - const pointRow: ManualPointEventAnnotationRow = { - ...a, - type: 'point', - timebucket: moment(timebucket).toISOString(), - }; - return pointRow; - } - const rangeRow: ManualRangeEventAnnotationRow = { - ...a, - type: 'range', - }; - return rangeRow; - }); -}; - -export const sortByTime = (a: DatatableRow, b: DatatableRow) => { - return 'time' in a && 'time' in b ? a.time.localeCompare(b.time) : 0; -}; - -const getRoundedTimestamp = (timestamp: number, firstTimestamp?: number, minInterval?: number) => { - if (!firstTimestamp || !minInterval) { - return timestamp; - } - return timestamp - ((timestamp - firstTimestamp) % minInterval); -}; - export function XYChart({ args, data, @@ -267,6 +216,7 @@ export function XYChart({ splitColumnAccessor, splitRowAccessor, singleTable, + annotations, } = args; const chartRef = useRef(null); const chartTheme = chartsThemeService.useChartsTheme(); @@ -447,25 +397,18 @@ export function XYChart({ }; const referenceLineLayers = getReferenceLayers(layers); - - const annotationsLayers = getAnnotationsLayers(layers); - const firstTable = dataLayers[0]?.table; - - const columnId = dataLayers[0]?.xAccessor - ? getColumnByAccessor(dataLayers[0]?.xAccessor, firstTable.columns)?.id - : null; - - const annotations = convertToAnnotationsTable( - annotationsLayers, - minInterval, - columnId ? firstTable.rows[0]?.[columnId] : undefined + const [rangeAnnotations, lineAnnotations] = partition( + annotations?.datatable.rows, + isRangeAnnotation ); - const [rangeAnnotations, lineAnnotations] = partition(annotations, isRangeAnnotation); + const annotationsConfigs = annotations?.layers.flatMap((l) => l.annotations); const groupedLineAnnotations = getAnnotationsGroupedByInterval( - lineAnnotations as ManualPointEventAnnotationRow[], - xAxisFormatter + lineAnnotations as PointEventAnnotationRow[], + annotationsConfigs, + annotations?.datatable.columns, + formatFactory ); const visualConfigs = [ @@ -483,7 +426,10 @@ export function XYChart({ ...groupedLineAnnotations, ].filter(Boolean); - const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].simpleView : false; + const shouldHideDetails = + annotations?.layers && annotations.layers.length > 0 + ? annotations?.layers[0].simpleView + : false; const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate) : {}; 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 c4aebbfb96902..132c88e4f863d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -12,7 +12,7 @@ import classnames from 'classnames'; import type { IconPosition, ReferenceLineDecorationConfig, - CollectiveConfig, + MergedAnnotation, } from '../../common/types'; import { getBaseIconPlacement } from '../components'; import { hasIcon, iconSet } from './icon'; @@ -27,16 +27,16 @@ type PartialReferenceLineDecorationConfig = Pick< position?: Position; }; -type PartialCollectiveConfig = Pick; +type PartialMergedAnnotation = Pick; const isExtendedDecorationConfig = ( - config: PartialReferenceLineDecorationConfig | PartialCollectiveConfig | undefined + config: PartialReferenceLineDecorationConfig | PartialMergedAnnotation | undefined ): config is PartialReferenceLineDecorationConfig => (config as PartialReferenceLineDecorationConfig)?.iconPosition ? true : false; // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( - visualConfigs: Array, + visualConfigs: Array, axesMap: AxesMap, shouldRotate: boolean ) => { 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 87d27d30badb2..c75c4ee54ffe6 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -57,10 +57,3 @@ const isAnnotationLayerCommon = ( export const isAnnotationsLayer = ( layer: CommonXYLayerConfig ): layer is CommonXYAnnotationLayerConfig => isAnnotationLayerCommon(layer); - -export const getAnnotationsLayers = ( - 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 6de052b392149..2de8dcd3d1e58 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -31,6 +31,7 @@ import { referenceLineDecorationConfigFunction, } from '../common/expression_functions'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; +import { eventAnnotationsResult } from '../common/expression_functions/event_annotations_result'; export interface XYPluginStartDependencies { data: DataPublicPluginStart; @@ -63,6 +64,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(xAxisConfigFunction); expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction); + expressions.registerFunction(eventAnnotationsResult); expressions.registerFunction(referenceLineFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(xyVisFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index f0e0fc141302a..b6175774ac515 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -25,6 +25,7 @@ import { extendedAnnotationLayerFunction, } from '../common/expression_functions'; import { SetupDeps } from './types'; +import { eventAnnotationsResult } from '../common/expression_functions/event_annotations_result'; export class ExpressionXyPlugin implements Plugin @@ -39,6 +40,7 @@ export class ExpressionXyPlugin expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction); + expressions.registerFunction(eventAnnotationsResult); expressions.registerFunction(referenceLineFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(xyVisFunction); diff --git a/src/plugins/charts/public/static/components/index.ts b/src/plugins/charts/public/static/components/index.ts index ea621c68e450a..cc23b32e05ff2 100644 --- a/src/plugins/charts/public/static/components/index.ts +++ b/src/plugins/charts/public/static/components/index.ts @@ -14,6 +14,7 @@ export { EmptyPlaceholder } from './empty_placeholder'; export { useCommonChartStyles } from './common_chart_styles'; export * from './endzones'; +export * from './warnings'; /** * The Lazily-loaded `ColorPicker` component. Consumers should use `React.Suspense` or diff --git a/src/plugins/charts/public/static/components/warnings.tsx b/src/plugins/charts/public/static/components/warnings.tsx new file mode 100644 index 0000000000000..9681cef1d6ace --- /dev/null +++ b/src/plugins/charts/public/static/components/warnings.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 { EuiButtonEmpty, EuiHorizontalRule, EuiPopover, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { euiThemeVars } from '@kbn/ui-theme'; +import React, { useState } from 'react'; + +export function Warnings({ warnings }: { warnings: React.ReactNode[] }) { + const [open, setOpen] = useState(false); + if (warnings.length === 0) return null; + return ( + <> + setOpen(false)} + button={ + setOpen(!open)} size="xs"> + {i18n.translate('charts.warning.warningLabel', { + defaultMessage: + '{numberWarnings, number} {numberWarnings, plural, one {warning} other {warnings}}', + values: { + numberWarnings: warnings.length, + }, + })} + + } + > +
+ {warnings.map((w, i) => ( + +
+ {w} +
+ {i < warnings.length - 1 && } +
+ ))} +
+
+ + ); +} diff --git a/src/plugins/data/common/search/aggs/buckets/index.ts b/src/plugins/data/common/search/aggs/buckets/index.ts index 4019e2d5b2aa0..9d7819157e189 100644 --- a/src/plugins/data/common/search/aggs/buckets/index.ts +++ b/src/plugins/data/common/search/aggs/buckets/index.ts @@ -29,6 +29,7 @@ export * from './lib/cidr_mask'; export * from './lib/date_range'; export * from './lib/ip_range'; export * from './lib/time_buckets/calc_auto_interval'; +export { TimeBuckets } from './lib/time_buckets'; export * from './migrate_include_exclude_format'; export * from './range_fn'; export * from './range'; diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.ts b/src/plugins/data_views/public/data_views/data_views_api_client.ts index 835c22891c68c..a45b9f29e595f 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.ts @@ -27,18 +27,17 @@ export class DataViewsApiClient implements IDataViewsApiClient { this.http = http; } - private _request(url: string, query?: {}): Promise { - return this.http - .fetch(url, { - query, - }) - .catch((resp) => { - if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { - throw new DataViewMissingIndices(resp.body.message); - } + private _request(url: string, query?: {}, body?: string): Promise { + const request = body + ? this.http.post(url, { query, body }) + : this.http.fetch(url, { query }); + return request.catch((resp) => { + if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { + throw new DataViewMissingIndices(resp.body.message); + } - throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); - }); + throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); + }); } private _getUrl(path: string[]) { @@ -51,14 +50,17 @@ export class DataViewsApiClient implements IDataViewsApiClient { */ getFieldsForWildcard(options: GetFieldsOptions) { const { pattern, metaFields, type, rollupIndex, allowNoIndex, filter } = options; - return this._request(this._getUrl(['_fields_for_wildcard']), { - pattern, - meta_fields: metaFields, - type, - rollup_index: rollupIndex, - allow_no_index: allowNoIndex, - filter, - }).then((response) => { + return this._request( + this._getUrl(['_fields_for_wildcard']), + { + pattern, + meta_fields: metaFields, + type, + rollup_index: rollupIndex, + allow_no_index: allowNoIndex, + }, + filter ? JSON.stringify({ index_filter: filter }) : undefined + ).then((response) => { return response || { fields: [], indices: [] }; }); } diff --git a/src/plugins/data_views/public/mocks.ts b/src/plugins/data_views/public/mocks.ts index f4f8c08686077..e9bcd6726611a 100644 --- a/src/plugins/data_views/public/mocks.ts +++ b/src/plugins/data_views/public/mocks.ts @@ -35,6 +35,7 @@ const createStartContract = (): Start => { clearCache: jest.fn(), getCanSaveSync: jest.fn(), getIdsWithTitle: jest.fn(), + getFieldsForIndexPattern: jest.fn(), } as unknown as jest.Mocked; }; diff --git a/src/plugins/data_views/server/routes/fields_for.ts b/src/plugins/data_views/server/routes/fields_for.ts index 9e0e94bd57511..67b58148dcf6b 100644 --- a/src/plugins/data_views/server/routes/fields_for.ts +++ b/src/plugins/data_views/server/routes/fields_for.ts @@ -118,5 +118,6 @@ export const registerFieldForWildcard = ( > ) => { router.put({ path, validate }, handler); + router.post({ path, validate }, handler); router.get({ path, validate }, handler); }; diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx index a83c1e98179ae..bbb00b574076a 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx @@ -23,6 +23,7 @@ import { EuiTablePagination, EuiSelectableMessage, EuiI18n, + useIsWithinBreakpoints, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -108,6 +109,8 @@ export const DocViewerTable = ({ onAddColumn, onRemoveColumn, }: DocViewRenderProps) => { + const showActionsInsideTableCell = useIsWithinBreakpoints(['xl'], true); + const { storage, uiSettings, fieldFormats } = useDiscoverServices(); const showMultiFields = uiSettings.get(SHOW_MULTIFIELDS); const currentDataViewId = dataView.id!; @@ -266,7 +269,11 @@ export const DocViewerTable = ({ const headers = [ !isSingleDocView && ( - + + {panels[0].items.map((item) => ( + + + + + + ))} + + ); + } + return ( { + return { + name: 'fetch_event_annotation', + aliases: [], + type: 'fetch_event_annotation', + inputTypes: ['null'], + help: i18n.translate('eventAnnotation.fetch.description', { + defaultMessage: 'Event annotation fetch', + }), + args: { + group: { + types: ['event_annotation_group'], + help: i18n.translate('eventAnnotation.group.args.annotationGroups', { + defaultMessage: 'Annotation group', + }), + multi: true, + }, + }, + fn: (input, args) => { + return { + type: 'fetch_event_annotation', + group: args.group, + }; + }, + }; +} diff --git a/src/plugins/event_annotation/common/fetch_event_annotations/fetch_event_annotations_fn.ts b/src/plugins/event_annotation/common/fetch_event_annotations/fetch_event_annotations_fn.ts index 66b470aa0afb0..d5ea46a4f3367 100644 --- a/src/plugins/event_annotation/common/fetch_event_annotations/fetch_event_annotations_fn.ts +++ b/src/plugins/event_annotation/common/fetch_event_annotations/fetch_event_annotations_fn.ts @@ -23,7 +23,6 @@ export const getFetchEventAnnotationsMeta: () => Omit< }), args: { groups: { - required: true, types: ['event_annotation_group'], help: i18n.translate('eventAnnotation.fetchEventAnnotations.args.annotationConfigs', { defaultMessage: 'Annotation configs', diff --git a/src/plugins/event_annotation/common/fetch_event_annotations/request_event_annotations.ts b/src/plugins/event_annotation/common/fetch_event_annotations/request_event_annotations.ts index 1f734b0182d6e..e7127bf500ab5 100644 --- a/src/plugins/event_annotation/common/fetch_event_annotations/request_event_annotations.ts +++ b/src/plugins/event_annotation/common/fetch_event_annotations/request_event_annotations.ts @@ -6,17 +6,17 @@ * Side Public License, v 1. */ -import { defer, firstValueFrom } from 'rxjs'; +import { defer, lastValueFrom } from 'rxjs'; import { partition } from 'lodash'; import { AggsStart, - DataViewsContract, + DataView, DataViewSpec, ExpressionValueSearchContext, parseEsInterval, AggConfigs, - IndexPatternExpressionType, } from '@kbn/data-plugin/common'; + import { ExecutionContext } from '@kbn/expressions-plugin/common'; import moment from 'moment'; import { ESCalendarInterval, ESFixedInterval, roundDateToESInterval } from '@elastic/charts'; @@ -26,6 +26,7 @@ import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { handleRequest } from './handle_request'; import { ANNOTATIONS_PER_BUCKET, + getCalculatedInterval, isInRange, isManualAnnotation, isManualPointAnnotation, @@ -45,9 +46,9 @@ interface ManualGroup { interface QueryGroup { type: 'query'; annotations: QueryPointEventAnnotationOutput[]; - allFields?: string[]; - dataView: IndexPatternExpressionType; timeField: string; + dataView: DataView; + allFields?: string[]; } export function getTimeZone(uiSettings: IUiSettingsClient) { @@ -58,6 +59,7 @@ export function getTimeZone(uiSettings: IUiSettingsClient) { return configuredTimeZone; } +const emptyDatatable = { rows: [], columns: [], type: 'datatable' }; export const requestEventAnnotations = ( input: ExpressionValueSearchContext | null, @@ -71,20 +73,39 @@ export const requestEventAnnotations = ( getStartDependencies: () => Promise ) => { return defer(async () => { + if (!input?.timeRange || !args.groups) { + return emptyDatatable; + } const { aggs, dataViews, searchSource, getNow, uiSettings } = await getStartDependencies(); + const interval = getCalculatedInterval(uiSettings, args.interval, input?.timeRange); + if (!interval) { + return emptyDatatable; + } + + const uniqueDataViewsToLoad = args.groups + .map((g) => g.dataView.value) + .reduce((acc, current) => { + if (acc.find((el) => el.id === current.id)) return acc; + return [...acc, current]; + }, []); + + const loadedDataViews = await Promise.all( + uniqueDataViewsToLoad.map((dataView) => dataViews.create(dataView, true)) + ); + const [manualGroups, queryGroups] = partition( - regroupForRequestOptimization(args, input), + regroupForRequestOptimization(args, input, loadedDataViews), isManualSubGroup ); const manualAnnotationDatatableRows = manualGroups.length - ? convertManualToDatatableRows(manualGroups[0], args.interval, getTimeZone(uiSettings)) + ? convertManualToDatatableRows(manualGroups[0], interval, getTimeZone(uiSettings)) : []; if (!queryGroups.length) { return manualAnnotationDatatableRows.length ? wrapRowsInDatatable(manualAnnotationDatatableRows) - : null; + : emptyDatatable; } const createEsaggsSingleRequest = async ({ @@ -92,11 +113,11 @@ export const requestEventAnnotations = ( aggConfigs, timeFields, }: { - dataView: any; + dataView: DataView; aggConfigs: AggConfigs; timeFields: string[]; }) => - firstValueFrom( + lastValueFrom( handleRequest({ aggs: aggConfigs, indexPattern: dataView, @@ -113,12 +134,7 @@ export const requestEventAnnotations = ( }) ); - const esaggsGroups = await prepareEsaggsForQueryGroups( - queryGroups, - args.interval, - dataViews, - aggs - ); + const esaggsGroups = await prepareEsaggsForQueryGroups(queryGroups, interval, aggs); const allQueryAnnotationsConfigs = queryGroups.flatMap((group) => group.annotations); @@ -169,23 +185,9 @@ const convertManualToDatatableRows = ( const prepareEsaggsForQueryGroups = async ( queryGroups: QueryGroup[], interval: string, - dataViews: DataViewsContract, aggs: AggsStart ) => { - const uniqueDataViewsToLoad = queryGroups - .map((g) => g.dataView.value) - .reduce((acc, current) => { - if (acc.find((el) => el.id === current.id)) return acc; - return [...acc, current]; - }, []); - - const loadedDataViews = await Promise.all( - uniqueDataViewsToLoad.map((dataView) => dataViews.create(dataView, true)) - ); - return queryGroups.map((group) => { - const dataView = loadedDataViews.find((dv) => dv.id === group.dataView.value.id)!; - const annotationsFilters = { type: 'agg_type', value: { @@ -260,9 +262,12 @@ const prepareEsaggsForQueryGroups = async ( ...fieldsTopMetric, ]; - const aggConfigs = aggs.createAggConfigs(dataView, aggregations?.map((agg) => agg.value) ?? []); + const aggConfigs = aggs.createAggConfigs( + group.dataView, + aggregations?.map((agg) => agg.value) ?? [] + ); return { - esaggsParams: { dataView, aggConfigs, timeFields: [group.timeField] }, + esaggsParams: { dataView: group.dataView, aggConfigs, timeFields: [group.timeField] }, fieldsColIdMap: group.allFields?.reduce>( (acc, fieldName, i) => ({ @@ -278,15 +283,12 @@ const prepareEsaggsForQueryGroups = async ( function regroupForRequestOptimization( { groups }: FetchEventAnnotationsArgs, - input: ExpressionValueSearchContext | null + input: ExpressionValueSearchContext | null, + loadedDataViews: DataView[] ) { const outputGroups = groups .map((g) => { return g.annotations.reduce>((acc, current) => { - if (current.isHidden) { - return acc; - } - if (isManualAnnotation(current)) { if (!isInRange(current, input?.timeRange)) { return acc; @@ -297,7 +299,14 @@ function regroupForRequestOptimization( (acc.manual as ManualGroup).annotations.push(current); return acc; } else { - const key = `${g.dataView.value.id}-${current.timeField}`; + const dataView = loadedDataViews.find((dv) => dv.id === g.dataView.value.id)!; + + const timeField = + current.timeField ?? + (dataView.timeFieldName || + dataView.fields.find((field) => field.type === 'date' && field.displayName)?.name); + + const key = `${g.dataView.value.id}-${timeField}`; const subGroup = acc[key] as QueryGroup; if (subGroup) { let allFields = [...(subGroup.allFields || []), ...(current.extraFields || [])]; @@ -321,8 +330,8 @@ function regroupForRequestOptimization( ...acc, [key]: { type: 'query', - dataView: g.dataView, - timeField: current.timeField, + dataView, + timeField: timeField!, allFields, annotations: [current], }, diff --git a/src/plugins/event_annotation/common/fetch_event_annotations/types.ts b/src/plugins/event_annotation/common/fetch_event_annotations/types.ts index d323814e98895..79921cd2e1399 100644 --- a/src/plugins/event_annotation/common/fetch_event_annotations/types.ts +++ b/src/plugins/event_annotation/common/fetch_event_annotations/types.ts @@ -17,7 +17,9 @@ import { ExpressionFunctionDefinition, Datatable } from '@kbn/expressions-plugin import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { EventAnnotationGroupOutput } from '../event_annotation_group'; -export type FetchEventAnnotationsOutput = Observable; +export type FetchEventAnnotationsOutput = Observable< + Datatable | { rows: never[]; columns: never[]; type: string } +>; export interface FetchEventAnnotationsArgs { groups: EventAnnotationGroupOutput[]; diff --git a/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts b/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts index 9f53d2ca452ad..caf6055c60793 100644 --- a/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts +++ b/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts @@ -6,10 +6,12 @@ * Side Public License, v 1. */ -import { TimeRange } from '@kbn/data-plugin/common'; +import { TimeBuckets, TimeRange, UI_SETTINGS } from '@kbn/data-plugin/common'; import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; import { omit, pick } from 'lodash'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { ManualEventAnnotationOutput, ManualPointEventAnnotationOutput, @@ -41,15 +43,70 @@ export const isManualAnnotation = ( ): annotation is ManualPointEventAnnotationOutput | ManualRangeEventAnnotationOutput => isRangeAnnotation(annotation) || isManualPointAnnotation(annotation); +function toAbsoluteDate(date: string) { + const parsed = dateMath.parse(date); + return parsed ? parsed.toDate() : undefined; +} + +export function toAbsoluteDates(range: TimeRange) { + const fromDate = dateMath.parse(range.from); + const toDate = dateMath.parse(range.to, { roundUp: true }); + + if (!fromDate || !toDate) { + return; + } + + return { + from: fromDate.toDate(), + to: toDate.toDate(), + }; +} + +export const getCalculatedInterval = ( + uiSettings: IUiSettingsClient, + usedInterval: string, + timeRange?: TimeRange +) => { + const dates = timeRange && toAbsoluteDates(timeRange); + if (!dates) { + return; + } + const buckets = new TimeBuckets({ + 'histogram:maxBars': uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS), + 'histogram:barTarget': uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET), + dateFormat: uiSettings.get('dateFormat'), + 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'), + }); + + buckets.setInterval(usedInterval); + buckets.setBounds({ + min: moment(dates.from), + max: moment(dates.to), + }); + + return buckets.getInterval().expression; +}; + export const isInRange = (annotation: ManualEventAnnotationOutput, timerange?: TimeRange) => { if (!timerange) { return false; } + const { from, to } = toAbsoluteDates(timerange) || {}; + if (!from || !to) { + return false; + } if (isRangeAnnotation(annotation)) { - return !(annotation.time >= timerange.to || annotation.endTime < timerange.from); + const time = toAbsoluteDate(annotation.time); + const endTime = toAbsoluteDate(annotation.endTime); + if (time && endTime) { + return !(time >= to || endTime < from); + } } if (isManualPointAnnotation(annotation)) { - return annotation.time >= timerange.from && annotation.time <= timerange.to; + const time = toAbsoluteDate(annotation.time); + if (time) { + return time >= from && time <= to; + } } return true; }; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index c3badda900a0c..779d0e13e7813 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -12,13 +12,12 @@ export type { ManualRangeEventAnnotationArgs, ManualRangeEventAnnotationOutput, ManualRangeEventAnnotationRow, - ManualPointEventAnnotationRow, + PointEventAnnotationRow, } from './manual_event_annotation/types'; export type { QueryPointEventAnnotationArgs, QueryPointEventAnnotationOutput, } from './query_point_event_annotation/types'; -export type { EventAnnotationArgs, EventAnnotationOutput } from './types'; export { manualPointEventAnnotation, manualRangeEventAnnotation } from './manual_event_annotation'; export { queryPointEventAnnotation } from './query_point_event_annotation'; export { eventAnnotationGroup } from './event_annotation_group'; @@ -27,8 +26,11 @@ export type { EventAnnotationGroupArgs } from './event_annotation_group'; export type { FetchEventAnnotationsArgs } from './fetch_event_annotations/types'; export type { EventAnnotationConfig, + EventAnnotationGroupConfig, + EventAnnotationArgs, RangeEventAnnotationConfig, PointInTimeEventAnnotationConfig, QueryPointEventAnnotationConfig, AvailableAnnotationIcon, + EventAnnotationOutput, } from './types'; 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 235c0b03ff39d..a74ba5723b61e 100644 --- a/src/plugins/event_annotation/common/manual_event_annotation/types.ts +++ b/src/plugins/event_annotation/common/manual_event_annotation/types.ts @@ -17,13 +17,14 @@ export type ManualPointEventAnnotationOutput = ManualPointEventAnnotationArgs & type: 'manual_point_event_annotation'; }; -export type ManualPointEventAnnotationRow = { +export type PointEventAnnotationRow = { id: string; time: string; type: 'point'; timebucket: string; - skippedCount?: string; -} & PointStyleProps; + skippedCount?: number; +} & PointStyleProps & + Record; export type ManualRangeEventAnnotationArgs = { id: string; diff --git a/src/plugins/event_annotation/common/query_point_event_annotation/index.ts b/src/plugins/event_annotation/common/query_point_event_annotation/index.ts index 40f4ade0f9594..cb9ba882a9f89 100644 --- a/src/plugins/event_annotation/common/query_point_event_annotation/index.ts +++ b/src/plugins/event_annotation/common/query_point_event_annotation/index.ts @@ -38,7 +38,6 @@ export const queryPointEventAnnotation: ExpressionFunctionDefinition< help: i18n.translate('eventAnnotation.queryAnnotation.args.filter', { defaultMessage: `Annotation filter`, }), - required: true, }, extraFields: { multi: true, @@ -48,7 +47,6 @@ export const queryPointEventAnnotation: ExpressionFunctionDefinition< }), }, timeField: { - required: true, types: ['string'], help: i18n.translate('eventAnnotation.queryAnnotation.args.timeField', { defaultMessage: `The time field of the annotation`, diff --git a/src/plugins/event_annotation/common/query_point_event_annotation/types.ts b/src/plugins/event_annotation/common/query_point_event_annotation/types.ts index 0aa172017b36b..592c50a0621ce 100644 --- a/src/plugins/event_annotation/common/query_point_event_annotation/types.ts +++ b/src/plugins/event_annotation/common/query_point_event_annotation/types.ts @@ -12,7 +12,7 @@ import { PointStyleProps } from '../types'; export type QueryPointEventAnnotationArgs = { id: string; filter: KibanaQueryOutput; - timeField: string; + timeField?: string; extraFields?: string[]; textField?: string; } & PointStyleProps; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 2e539a910124e..76749bee5bc79 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -22,35 +22,39 @@ import { export type LineStyle = 'solid' | 'dashed' | 'dotted'; export type Fill = 'inside' | 'outside' | 'none'; -export type AnnotationType = 'manual'; +export type ManualAnnotationType = 'manual'; +export type QueryAnnotationType = 'query'; export type KeyType = 'point_in_time' | 'range'; export type AvailableAnnotationIcon = $Values; -export interface PointStyleProps { + +interface StyleSharedProps { label: string; color?: string; + isHidden?: boolean; +} + +export type PointStyleProps = StyleSharedProps & { icon?: AvailableAnnotationIcon; lineWidth?: number; lineStyle?: LineStyle; textVisibility?: boolean; - isHidden?: boolean; -} +}; export type PointInTimeEventAnnotationConfig = { id: string; + type: ManualAnnotationType; key: { type: 'point_in_time'; timestamp: string; }; } & PointStyleProps; -export interface RangeStyleProps { - label: string; - color?: string; +export type RangeStyleProps = StyleSharedProps & { outside?: boolean; - isHidden?: boolean; -} +}; export type RangeEventAnnotationConfig = { + type: ManualAnnotationType; id: string; key: { type: 'range'; @@ -63,9 +67,10 @@ export type StyleProps = PointStyleProps & RangeStyleProps; export type QueryPointEventAnnotationConfig = { id: string; + type: QueryAnnotationType; filter: KibanaQueryOutput; - timeField: string; - textField: string; + timeField?: string; + textField?: string; extraFields?: string[]; key: { type: 'point_in_time'; @@ -77,6 +82,11 @@ export type EventAnnotationConfig = | RangeEventAnnotationConfig | QueryPointEventAnnotationConfig; +export interface EventAnnotationGroupConfig { + annotations: EventAnnotationConfig[]; + indexPatternId: string; +} + export type EventAnnotationArgs = | ManualPointEventAnnotationArgs | ManualRangeEventAnnotationArgs 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 280674c3dbfa2..afe64a5a47eb2 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/helpers.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/helpers.ts @@ -14,6 +14,7 @@ import { QueryPointEventAnnotationConfig, } from '../../common'; export const defaultAnnotationColor = euiLightVars.euiColorAccent; +// Do not compute it live as dependencies will add tens of Kbs to the plugin export const defaultAnnotationRangeColor = `#F04E981A`; // defaultAnnotationColor with opacity 0.1 export const defaultAnnotationLabel = i18n.translate( @@ -38,5 +39,5 @@ export const isManualPointAnnotationConfig = ( export const isQueryAnnotationConfig = ( annotation?: EventAnnotationConfig ): annotation is QueryPointEventAnnotationConfig => { - return Boolean(annotation && 'filter' in annotation); + return Boolean(annotation && annotation.type === 'query'); }; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts new file mode 100644 index 0000000000000..0fea0e7c38ea7 --- /dev/null +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -0,0 +1,352 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getEventAnnotationService } from './service'; +import { EventAnnotationServiceType } from './types'; + +describe('Event Annotation Service', () => { + let eventAnnotationService: EventAnnotationServiceType; + beforeAll(() => { + eventAnnotationService = getEventAnnotationService(); + }); + describe('toExpression', () => { + it('should work for an empty list', () => { + expect(eventAnnotationService.toExpression([])).toEqual([]); + }); + + it('should skip hidden annotations', () => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: '2022', + }, + label: 'Hello', + isHidden: true, + }, + { + id: 'myRangeEvent', + type: 'manual', + key: { + type: 'range', + timestamp: '2021', + endTimestamp: '2022', + }, + label: 'Hello Range', + isHidden: true, + }, + { + id: 'myEvent', + type: 'query', + timeField: '@timestamp', + key: { + type: 'point_in_time', + }, + label: 'Hello Range', + isHidden: true, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + ]) + ).toEqual([]); + }); + it('should process manual point annotations', () => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: '2022', + }, + label: 'Hello', + }, + ]) + ).toEqual([ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_point_event_annotation', + arguments: { + id: ['myEvent'], + time: ['2022'], + label: ['Hello'], + color: ['#f04e98'], + lineWidth: [1], + lineStyle: ['solid'], + icon: ['triangle'], + textVisibility: [false], + }, + }, + ], + }, + ]); + }); + it('should process manual range annotations', () => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'manual', + key: { + type: 'range', + timestamp: '2021', + endTimestamp: '2022', + }, + label: 'Hello', + }, + ]) + ).toEqual([ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_range_event_annotation', + arguments: { + id: ['myEvent'], + time: ['2021'], + endTime: ['2022'], + label: ['Hello'], + color: ['#F04E981A'], + outside: [false], + }, + }, + ], + }, + ]); + }); + it('should process query based annotations', () => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'query', + timeField: '@timestamp', + key: { + type: 'point_in_time', + }, + label: 'Hello', + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + ]) + ).toEqual([ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'query_point_event_annotation', + arguments: { + id: ['myEvent'], + timeField: ['@timestamp'], + label: ['Hello'], + color: ['#f04e98'], + lineWidth: [1], + lineStyle: ['solid'], + icon: ['triangle'], + textVisibility: [false], + textField: [], + filter: [ + { + chain: [ + { + arguments: { + q: [''], + }, + function: 'kql', + type: 'function', + }, + ], + type: 'expression', + }, + ], + extraFields: [], + }, + }, + ], + }, + ]); + }); + it('should process mixed annotations', () => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: '2022', + }, + label: 'Hello', + }, + { + id: 'myRangeEvent', + type: 'manual', + key: { + type: 'range', + timestamp: '2021', + endTimestamp: '2022', + }, + label: 'Hello Range', + }, + { + id: 'myEvent', + type: 'query', + timeField: '@timestamp', + key: { + type: 'point_in_time', + }, + label: 'Hello', + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + ]) + ).toEqual([ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_point_event_annotation', + arguments: { + id: ['myEvent'], + time: ['2022'], + label: ['Hello'], + color: ['#f04e98'], + lineWidth: [1], + lineStyle: ['solid'], + icon: ['triangle'], + textVisibility: [false], + }, + }, + ], + }, + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_range_event_annotation', + arguments: { + id: ['myRangeEvent'], + time: ['2021'], + endTime: ['2022'], + label: ['Hello Range'], + color: ['#F04E981A'], + outside: [false], + }, + }, + ], + }, + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'query_point_event_annotation', + arguments: { + id: ['myEvent'], + timeField: ['@timestamp'], + label: ['Hello'], + color: ['#f04e98'], + lineWidth: [1], + lineStyle: ['solid'], + icon: ['triangle'], + textVisibility: [false], + textField: [], + filter: [ + { + chain: [ + { + arguments: { + q: [''], + }, + function: 'kql', + type: 'function', + }, + ], + type: 'expression', + }, + ], + extraFields: [], + }, + }, + ], + }, + ]); + }); + it.each` + textVisibility | textField | expected + ${'true'} | ${''} | ${''} + ${'false'} | ${''} | ${''} + ${'true'} | ${'myField'} | ${'myField'} + ${'false'} | ${''} | ${''} + `( + "should handle correctly textVisibility when set to '$textVisibility' and textField to '$textField'", + ({ textVisibility, textField, expected }) => { + expect( + eventAnnotationService.toExpression([ + { + id: 'myEvent', + type: 'query', + timeField: '@timestamp', + key: { + type: 'point_in_time', + }, + label: 'Hello', + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + textVisibility, + textField, + }, + ]) + ).toEqual([ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'query_point_event_annotation', + arguments: { + id: ['myEvent'], + timeField: ['@timestamp'], + label: ['Hello'], + color: ['#f04e98'], + lineWidth: [1], + lineStyle: ['solid'], + icon: ['triangle'], + textVisibility: [textVisibility], + textField: expected ? [expected] : [], + filter: [ + { + chain: [ + { + arguments: { + q: [''], + }, + function: 'kql', + type: 'function', + }, + ], + type: 'expression', + }, + ], + extraFields: [], + }, + }, + ], + }, + ]); + } + ); + }); +}); 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 e657faca4afd7..41bf12c288cc1 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -6,38 +6,42 @@ * Side Public License, v 1. */ +import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; +import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; +import { EventAnnotationConfig } from '../../common'; import { EventAnnotationServiceType } from './types'; import { defaultAnnotationColor, defaultAnnotationRangeColor, defaultAnnotationLabel, + isRangeAnnotationConfig, isQueryAnnotationConfig, } 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: (annotation) => { - if (isRangeAnnotation(annotation)) { - const { label, isHidden, color, key, outside, id } = annotation; + const annotationsToExpression = (annotations: EventAnnotationConfig[]) => { + const visibleAnnotations = annotations.filter(({ isHidden }) => !isHidden); + const [queryBasedAnnotations, manualBasedAnnotations] = partition( + visibleAnnotations, + isQueryAnnotationConfig + ); + + const expressions = []; + + for (const annotation of manualBasedAnnotations) { + if (isRangeAnnotationConfig(annotation)) { + const { label, color, key, outside, id } = annotation; const { timestamp: time, endTimestamp: endTime } = key; - return { - type: 'expression', + expressions.push({ + type: 'expression' as const, chain: [ { - type: 'function', + type: 'function' as const, function: 'manual_range_event_annotation', arguments: { id: [id], @@ -46,57 +50,17 @@ export function getEventAnnotationService(): EventAnnotationServiceType { label: [label || defaultAnnotationLabel], color: [color || defaultAnnotationRangeColor], outside: [Boolean(outside)], - isHidden: [Boolean(isHidden)], - }, - }, - ], - }; - } else if (isQueryAnnotationConfig(annotation)) { - const { - id, - extraFields, - label, - isHidden, - color, - lineStyle, - lineWidth, - icon, - filter, - textVisibility, - timeField, - textField, - } = annotation; - return { - type: 'expression', - chain: [ - { - type: 'function', - function: 'query_point_event_annotation', - arguments: { - id: [id], - filter: filter ? [queryToAst(filter)] : [], - timeField: [timeField], - textField: [textField], - label: [label || defaultAnnotationLabel], - color: [color || defaultAnnotationColor], - lineWidth: [lineWidth || 1], - lineStyle: [lineStyle || 'solid'], - icon: hasIcon(icon) ? [icon] : ['triangle'], - textVisibility: [textVisibility || false], - isHidden: [Boolean(isHidden)], - extraFields: extraFields || [], }, }, ], - }; + }); } else { - const { label, isHidden, color, lineStyle, lineWidth, icon, key, textVisibility, id } = - annotation; - return { - type: 'expression', + const { label, color, lineStyle, lineWidth, icon, key, textVisibility, id } = annotation; + expressions.push({ + type: 'expression' as const, chain: [ { - type: 'function', + type: 'function' as const, function: 'manual_point_event_annotation', arguments: { id: [id], @@ -107,12 +71,106 @@ export function getEventAnnotationService(): EventAnnotationServiceType { lineStyle: [lineStyle || 'solid'], icon: hasIcon(icon) ? [icon] : ['triangle'], textVisibility: [textVisibility || false], - isHidden: [Boolean(isHidden)], }, }, ], - }; + }); } + } + + for (const annotation of queryBasedAnnotations) { + const { + id, + label, + color, + lineStyle, + lineWidth, + icon, + timeField, + textVisibility, + textField, + filter, + extraFields, + } = annotation; + expressions.push({ + type: 'expression' as const, + chain: [ + { + type: 'function' as const, + function: 'query_point_event_annotation', + arguments: { + id: [id], + timeField: timeField ? [timeField] : [], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationColor], + lineWidth: [lineWidth || 1], + lineStyle: [lineStyle || 'solid'], + icon: hasIcon(icon) ? [icon] : ['triangle'], + textVisibility: [textVisibility || false], + textField: textVisibility && textField ? [textField] : [], + filter: filter ? [queryToAst(filter)] : [], + extraFields: extraFields || [], + }, + }, + ], + }); + } + return expressions; + }; + return { + toExpression: annotationsToExpression, + toFetchExpression: ({ interval, groups }) => { + if (groups.length === 0) { + return []; + } + + const groupsExpressions = groups + .filter((g) => g.annotations.some((a) => !a.isHidden)) + .map(({ annotations, indexPatternId }): ExpressionAstExpression => { + const indexPatternExpression: ExpressionAstExpression = { + type: 'expression', + chain: [ + { + type: 'function', + function: 'indexPatternLoad', + arguments: { + id: [indexPatternId], + }, + }, + ], + }; + const annotationExpressions = annotationsToExpression(annotations); + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'event_annotation_group', + arguments: { + dataView: [indexPatternExpression], + annotations: [...annotationExpressions], + }, + }, + ], + }; + }); + + const fetchExpression: ExpressionAstExpression = { + type: 'expression', + chain: [ + { type: 'function', function: 'kibana', arguments: {} }, + { + type: 'function', + function: 'fetch_event_annotations', + arguments: { + interval: [interval], + groups: [...groupsExpressions], + }, + }, + ], + }; + + return [fetchExpression]; }, }; } 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 d5fcaa23107c8..cf3d759b7a324 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,12 @@ */ import { ExpressionAstExpression } from '@kbn/expressions-plugin/common/ast'; -import { EventAnnotationConfig } from '../../common'; +import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; export interface EventAnnotationServiceType { - toExpression: (props: EventAnnotationConfig) => ExpressionAstExpression; + toExpression: (props: EventAnnotationConfig[]) => ExpressionAstExpression[]; + toFetchExpression: (props: { + interval: string; + groups: EventAnnotationGroupConfig[]; + }) => ExpressionAstExpression[]; } diff --git a/src/plugins/event_annotation/public/fetch_event_annotations/__snapshots__/fetch_event_annotations.test.ts.snap b/src/plugins/event_annotation/public/fetch_event_annotations/__snapshots__/fetch_event_annotations.test.ts.snap index daefa3f4af7ce..25729bfd5b80e 100644 --- a/src/plugins/event_annotation/public/fetch_event_annotations/__snapshots__/fetch_event_annotations.test.ts.snap +++ b/src/plugins/event_annotation/public/fetch_event_annotations/__snapshots__/fetch_event_annotations.test.ts.snap @@ -37,6 +37,13 @@ Array [ "timebucket": "2022-07-05T04:30:00.000Z", "type": "point", }, + Object { + "id": "mann5", + "isHidden": true, + "time": "2022-07-05T05:55:00Z", + "timebucket": "2022-07-05T05:30:00.000Z", + "type": "point", + }, Object { "id": "mann1", "time": "2022-07-05T11:12:00Z", diff --git a/src/plugins/event_annotation/public/fetch_event_annotations/fetch_event_annotations.test.ts b/src/plugins/event_annotation/public/fetch_event_annotations/fetch_event_annotations.test.ts index aafb59a97d42a..271ab5910afdf 100644 --- a/src/plugins/event_annotation/public/fetch_event_annotations/fetch_event_annotations.test.ts +++ b/src/plugins/event_annotation/public/fetch_event_annotations/fetch_event_annotations.test.ts @@ -285,12 +285,12 @@ describe('getFetchEventAnnotations', () => { (startServices[1].data.dataViews.create as jest.Mock).mockClear(); (handleRequest as jest.Mock).mockClear(); }); - test('Returns null for empty groups', async () => { + test('Returns empty datatable for empty groups', async () => { const result = await runGetFetchEventAnnotations({ interval: '2h', groups: [], }); - expect(result).toEqual(null); + expect(result).toEqual({ columns: [], rows: [], type: 'datatable' }); }); describe('Manual annotations', () => { @@ -322,10 +322,6 @@ describe('getFetchEventAnnotations', () => { ], } as unknown as FetchEventAnnotationsArgs; - test(`Doesn't run dataViews.create for manual annotations groups only`, async () => { - await runGetFetchEventAnnotations(manualOnlyArgs); - expect(startServices[1].data.dataViews.create).not.toHaveBeenCalled(); - }); test('Sorts annotations by time, assigns correct timebuckets, filters out hidden and out of range annotations', async () => { const result = await runGetFetchEventAnnotations(manualOnlyArgs); expect(result!.rows).toMatchSnapshot(); @@ -340,7 +336,7 @@ describe('getFetchEventAnnotations', () => { { type: 'event_annotation_group', annotations: [manualAnnotationSamples.point1], - dataView1, + dataView: dataView1, }, { type: 'event_annotation_group', diff --git a/src/plugins/event_annotation/public/index.ts b/src/plugins/event_annotation/public/index.ts index 84245752e19fd..58f6e2c7c9f22 100644 --- a/src/plugins/event_annotation/public/index.ts +++ b/src/plugins/event_annotation/public/index.ts @@ -19,4 +19,5 @@ export { defaultAnnotationRangeColor, isRangeAnnotationConfig, isManualPointAnnotationConfig, + isQueryAnnotationConfig, } from './event_annotation_service/helpers'; diff --git a/src/plugins/interactive_setup/public/progress_indicator.tsx b/src/plugins/interactive_setup/public/progress_indicator.tsx index 51235f55fe621..9fee7e6da7110 100644 --- a/src/plugins/interactive_setup/public/progress_indicator.tsx +++ b/src/plugins/interactive_setup/public/progress_indicator.tsx @@ -14,7 +14,7 @@ import useAsyncFn from 'react-use/lib/useAsyncFn'; import useTimeoutFn from 'react-use/lib/useTimeoutFn'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { StatusResponse } from '@kbn/core/types/status'; +import type { StatusResponse } from '@kbn/core-status-common-internal'; import { i18n } from '@kbn/i18n'; import { useKibana } from './use_kibana'; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx index 0ffd353c8ddd2..d51c7461d684d 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx @@ -277,7 +277,7 @@ export class Table extends PureComponent { ); }, } as EuiTableFieldDataColumnType>, - ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []), + ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition({ serverPaging: true })] : []), ...columnRegistry.getAll().map((column) => { column.setColumnContext({ capabilities }); column.registerOnFinishCallback(() => { diff --git a/src/plugins/saved_objects_tagging_oss/public/api.ts b/src/plugins/saved_objects_tagging_oss/public/api.ts index 61d6fe29e64d9..6315185978472 100644 --- a/src/plugins/saved_objects_tagging_oss/public/api.ts +++ b/src/plugins/saved_objects_tagging_oss/public/api.ts @@ -102,7 +102,9 @@ export interface SavedObjectsTaggingApiUi { * ) * ``` */ - getTableColumnDefinition(): EuiTableFieldDataColumnType; + getTableColumnDefinition( + options?: GetTableColumnDefinitionOptions + ): EuiTableFieldDataColumnType; /** * Convert given tag name to a {@link SavedObjectsFindOptionsReference | reference } @@ -251,6 +253,28 @@ export interface SavedObjectSaveModalTagSelectorComponentProps { onTagsSelected: (ids: string[]) => void; } +/** + * Options for the {@link SavedObjectsTaggingApiUi.getTableColumnDefinition | getTableColumnDefinition api} + * + * @public + */ +export interface GetTableColumnDefinitionOptions { + /** + * By default, the `tags` column definition will be automatically sortable + * by tag name. + * + * However, when paging is performed on the server, we need to remove the sorting + * capability from the column to avoid unexpected behavior by triggering fetch request + * when sorting by column. + * + * Should be set to `true` when generating the definition for a table that performs + * server-side paging. + * + * Defaults to false. + */ + serverPaging?: boolean; +} + /** * Options for the {@link SavedObjectsTaggingApiUi.getSearchBarFilter | getSearchBarFilter api} * diff --git a/src/plugins/saved_objects_tagging_oss/public/index.ts b/src/plugins/saved_objects_tagging_oss/public/index.ts index cb22cb9ce4864..46e30d51e084a 100644 --- a/src/plugins/saved_objects_tagging_oss/public/index.ts +++ b/src/plugins/saved_objects_tagging_oss/public/index.ts @@ -23,6 +23,7 @@ export type { ParseSearchQueryOptions, SavedObjectSaveModalTagSelectorComponentProps, SavedObjectTagDecoratorTypeGuard, + GetTableColumnDefinitionOptions, } from './api'; export type { TagDecoratedSavedObject } from './decorator'; diff --git a/src/plugins/unified_field_list/README.md b/src/plugins/unified_field_list/README.md index 657523aad9c1b..4f0e841de03b2 100755 --- a/src/plugins/unified_field_list/README.md +++ b/src/plugins/unified_field_list/README.md @@ -12,10 +12,14 @@ This Kibana plugin contains components and services for field list UI (as in fie * `loadStats(...)` - returns the loaded field stats (can also work with Ad-hoc data views) +* `loadFieldExisting(...)` - returns the loaded existing fields (can also work with Ad-hoc data views) + ## Server APIs * `/api/unified_field_list/field_stats` - returns the loaded field stats (except for Ad-hoc data views) +* `/api/unified_field_list/existing_fields/{dataViewId}` - returns the loaded existing fields (except for Ad-hoc data views) + ## Development See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/unified_field_list/common/constants.ts b/src/plugins/unified_field_list/common/constants.ts index ef8a01b2705ff..f8723792759d7 100644 --- a/src/plugins/unified_field_list/common/constants.ts +++ b/src/plugins/unified_field_list/common/constants.ts @@ -8,3 +8,4 @@ export const BASE_API_PATH = '/api/unified_field_list'; export const FIELD_STATS_API_PATH = `${BASE_API_PATH}/field_stats`; +export const FIELD_EXISTING_API_PATH = `${BASE_API_PATH}/existing_fields/{dataViewId}`; diff --git a/src/plugins/unified_field_list/common/index.ts b/src/plugins/unified_field_list/common/index.ts index 747db7a56bbae..72274d44a7263 100755 --- a/src/plugins/unified_field_list/common/index.ts +++ b/src/plugins/unified_field_list/common/index.ts @@ -7,3 +7,4 @@ */ export const PLUGIN_ID = 'unifiedFieldList'; +export const FIELD_EXISTENCE_SETTING = 'lens:useFieldExistenceSampling'; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.test.ts b/src/plugins/unified_field_list/common/utils/field_existing_utils.test.ts similarity index 94% rename from x-pack/plugins/lens/server/routes/existing_fields.test.ts rename to src/plugins/unified_field_list/common/utils/field_existing_utils.test.ts index 7c0257c7808ba..79b2b2d10ec35 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.test.ts +++ b/src/plugins/unified_field_list/common/utils/field_existing_utils.test.ts @@ -1,12 +1,18 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { DataView } from '@kbn/data-views-plugin/common'; -import { legacyExistingFields, existingFields, Field, buildFieldList } from './existing_fields'; +import { + legacyExistingFields, + existingFields, + Field, + buildFieldList, +} from './field_existing_utils'; describe('existingFields', () => { it('should remove missing fields by matching names', () => { diff --git a/src/plugins/unified_field_list/common/utils/field_existing_utils.ts b/src/plugins/unified_field_list/common/utils/field_existing_utils.ts new file mode 100644 index 0000000000000..fcc08a141dae1 --- /dev/null +++ b/src/plugins/unified_field_list/common/utils/field_existing_utils.ts @@ -0,0 +1,266 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Boom from '@hapi/boom'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { RuntimeField } from '@kbn/data-views-plugin/common'; +import type { DataViewsContract, DataView, FieldSpec } from '@kbn/data-views-plugin/common'; +import { IKibanaSearchRequest } from '@kbn/data-plugin/common'; + +export type SearchHandler = ( + params: IKibanaSearchRequest['params'] +) => Promise>>>; + +/** + * The number of docs to sample to determine field empty status. + */ +const SAMPLE_SIZE = 500; + +export interface Field { + name: string; + isScript: boolean; + isMeta: boolean; + lang?: estypes.ScriptLanguage; + script?: string; + runtimeField?: RuntimeField; +} + +export async function fetchFieldExistence({ + search, + dataViewsService, + dataView, + dslQuery = { match_all: {} }, + fromDate, + toDate, + timeFieldName, + includeFrozen, + metaFields, + useSampling, +}: { + search: SearchHandler; + dataView: DataView; + dslQuery: object; + fromDate?: string; + toDate?: string; + timeFieldName?: string; + includeFrozen: boolean; + useSampling: boolean; + metaFields: string[]; + dataViewsService: DataViewsContract; +}) { + if (useSampling) { + return legacyFetchFieldExistenceSampling({ + search, + metaFields, + dataView, + dataViewsService, + dslQuery, + fromDate, + toDate, + timeFieldName, + includeFrozen, + }); + } + + const allFields = buildFieldList(dataView, metaFields); + const existingFieldList = await dataViewsService.getFieldsForIndexPattern(dataView, { + // filled in by data views service + pattern: '', + filter: toQuery(timeFieldName, fromDate, toDate, dslQuery), + }); + return { + indexPatternTitle: dataView.title, + existingFieldNames: existingFields(existingFieldList, allFields), + }; +} + +async function legacyFetchFieldExistenceSampling({ + search, + metaFields, + dataView, + dslQuery, + fromDate, + toDate, + timeFieldName, + includeFrozen, +}: { + search: SearchHandler; + metaFields: string[]; + dataView: DataView; + dataViewsService: DataViewsContract; + dslQuery: object; + fromDate?: string; + toDate?: string; + timeFieldName?: string; + includeFrozen: boolean; +}) { + const fields = buildFieldList(dataView, metaFields); + const runtimeMappings = dataView.getRuntimeMappings(); + + const docs = await fetchDataViewStats({ + search, + fromDate, + toDate, + dslQuery, + index: dataView.title, + timeFieldName: timeFieldName || dataView.timeFieldName, + fields, + runtimeMappings, + includeFrozen, + }); + + return { + indexPatternTitle: dataView.title, + existingFieldNames: legacyExistingFields(docs, fields), + }; +} + +/** + * Exported only for unit tests. + */ +export function buildFieldList(indexPattern: DataView, metaFields: string[]): Field[] { + return indexPattern.fields.map((field) => { + return { + name: field.name, + isScript: !!field.scripted, + lang: field.lang, + script: field.script, + // id is a special case - it doesn't show up in the meta field list, + // but as it's not part of source, it has to be handled separately. + isMeta: metaFields.includes(field.name) || field.name === '_id', + runtimeField: !field.isMapped ? field.runtimeField : undefined, + }; + }); +} + +async function fetchDataViewStats({ + search, + index, + dslQuery, + timeFieldName, + fromDate, + toDate, + fields, + runtimeMappings, + includeFrozen, +}: { + search: SearchHandler; + index: string; + dslQuery: object; + timeFieldName?: string; + fromDate?: string; + toDate?: string; + fields: Field[]; + runtimeMappings: estypes.MappingRuntimeFields; + includeFrozen: boolean; +}) { + const query = toQuery(timeFieldName, fromDate, toDate, dslQuery); + + const scriptedFields = fields.filter((f) => f.isScript); + const response = await search({ + index, + ...(includeFrozen ? { ignore_throttled: false } : {}), + body: { + size: SAMPLE_SIZE, + query, + // Sorted queries are usually able to skip entire shards that don't match + sort: timeFieldName && fromDate && toDate ? [{ [timeFieldName]: 'desc' }] : [], + fields: ['*'], + _source: false, + runtime_mappings: runtimeMappings, + script_fields: scriptedFields.reduce((acc, field) => { + acc[field.name] = { + script: { + lang: field.lang!, + source: field.script!, + }, + }; + return acc; + }, {} as Record), + // Small improvement because there is overhead in counting + track_total_hits: false, + // Per-shard timeout, must be lower than overall. Shards return partial results on timeout + timeout: '4500ms', + }, + }); + + return response?.hits.hits; +} + +function toQuery( + timeFieldName: string | undefined, + fromDate: string | undefined, + toDate: string | undefined, + dslQuery: object +) { + const filter = + timeFieldName && fromDate && toDate + ? [ + { + range: { + [timeFieldName]: { + format: 'strict_date_optional_time', + gte: fromDate, + lte: toDate, + }, + }, + }, + dslQuery, + ] + : [dslQuery]; + + const query = { + bool: { + filter, + }, + }; + return query; +} + +/** + * Exported only for unit tests. + */ +export function existingFields(filteredFields: FieldSpec[], allFields: Field[]): string[] { + const filteredFieldsSet = new Set(filteredFields.map((f) => f.name)); + + return allFields + .filter((field) => field.isScript || field.runtimeField || filteredFieldsSet.has(field.name)) + .map((f) => f.name); +} + +/** + * Exported only for unit tests. + */ +export function legacyExistingFields(docs: estypes.SearchHit[], fields: Field[]): string[] { + const missingFields = new Set(fields); + + for (const doc of docs) { + if (missingFields.size === 0) { + break; + } + + missingFields.forEach((field) => { + let fieldStore = doc.fields!; + if (field.isMeta) { + fieldStore = doc; + } + const value = fieldStore[field.name]; + if (Array.isArray(value) && value.length) { + missingFields.delete(field); + } else if (!Array.isArray(value) && value) { + missingFields.delete(field); + } + }); + } + + return fields.filter((field) => !missingFields.has(field)).map((f) => f.name); +} + +export function isBoomError(error: { isBoom?: boolean }): error is Boom.Boom { + return error.isBoom === true; +} diff --git a/src/plugins/unified_field_list/public/index.ts b/src/plugins/unified_field_list/public/index.ts index f2f666dd47481..df27e7131df9c 100755 --- a/src/plugins/unified_field_list/public/index.ts +++ b/src/plugins/unified_field_list/public/index.ts @@ -17,6 +17,7 @@ export type { export type { FieldStatsProps, FieldStatsServices } from './components/field_stats'; export { FieldStats } from './components/field_stats'; export { loadFieldStats } from './services/field_stats'; +export { loadFieldExisting } from './services/field_existing'; // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. diff --git a/src/plugins/unified_field_list/public/services/field_existing/index.ts b/src/plugins/unified_field_list/public/services/field_existing/index.ts new file mode 100644 index 0000000000000..56be726b7c90f --- /dev/null +++ b/src/plugins/unified_field_list/public/services/field_existing/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 { loadFieldExisting } from './load_field_existing'; diff --git a/src/plugins/unified_field_list/public/services/field_existing/load_field_existing.ts b/src/plugins/unified_field_list/public/services/field_existing/load_field_existing.ts new file mode 100644 index 0000000000000..79b2b056c6062 --- /dev/null +++ b/src/plugins/unified_field_list/public/services/field_existing/load_field_existing.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 { IUiSettingsClient } from '@kbn/core/public'; +import { DataPublicPluginStart, UI_SETTINGS } from '@kbn/data-plugin/public'; +import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/common'; +import { lastValueFrom } from 'rxjs'; +import { FIELD_EXISTENCE_SETTING } from '../../../common'; +import { fetchFieldExistence } from '../../../common/utils/field_existing_utils'; + +interface FetchFieldExistenceParams { + data: DataPublicPluginStart; + dataView: DataView; + fromDate: string; + toDate: string; + dslQuery: object; + timeFieldName?: string; + dataViewsService: DataViewsContract; + uiSettingsClient: IUiSettingsClient; +} + +export async function loadFieldExisting({ + data, + dslQuery, + fromDate, + toDate, + timeFieldName, + dataViewsService, + uiSettingsClient, + dataView, +}: FetchFieldExistenceParams) { + const includeFrozen = uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); + const useSampling = uiSettingsClient.get(FIELD_EXISTENCE_SETTING); + const metaFields = uiSettingsClient.get(UI_SETTINGS.META_FIELDS); + + return await fetchFieldExistence({ + dslQuery, + fromDate, + toDate, + timeFieldName, + dataViewsService, + includeFrozen, + useSampling, + metaFields, + dataView, + search: async (params) => { + const response = await lastValueFrom(data.search.search({ params })); + return response.rawResponse; + }, + }); +} diff --git a/src/plugins/unified_field_list/server/plugin.ts b/src/plugins/unified_field_list/server/plugin.ts index 2853afcf3d6fd..1171eb4c69be7 100644 --- a/src/plugins/unified_field_list/server/plugin.ts +++ b/src/plugins/unified_field_list/server/plugin.ts @@ -14,6 +14,7 @@ import { PluginSetup, } from './types'; import { defineRoutes } from './routes'; +import { getUiSettings } from './ui_settings'; export class UnifiedFieldListPlugin implements Plugin @@ -26,8 +27,9 @@ export class UnifiedFieldListPlugin public setup(core: CoreSetup, plugins: PluginSetup) { this.logger.debug('unifiedFieldList: Setup'); + core.uiSettings.register(getUiSettings()); - defineRoutes(core); + defineRoutes(core, this.logger); return {}; } diff --git a/src/plugins/unified_field_list/server/routes/existing_fields.ts b/src/plugins/unified_field_list/server/routes/existing_fields.ts new file mode 100644 index 0000000000000..e17048e217e77 --- /dev/null +++ b/src/plugins/unified_field_list/server/routes/existing_fields.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 { errors } from '@elastic/elasticsearch'; +import { schema } from '@kbn/config-schema'; +import { CoreSetup, Logger } from '@kbn/core/server'; +import { UI_SETTINGS } from '@kbn/data-plugin/server'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { fetchFieldExistence, isBoomError } from '../../common/utils/field_existing_utils'; +import { FIELD_EXISTING_API_PATH } from '../../common/constants'; +import { FIELD_EXISTENCE_SETTING } from '../../common'; +import { PluginStart } from '../types'; + +export async function existingFieldsRoute(setup: CoreSetup, logger: Logger) { + const router = setup.http.createRouter(); + + router.post( + { + path: FIELD_EXISTING_API_PATH, + validate: { + params: schema.object({ + dataViewId: schema.string(), + }), + body: schema.object({ + dslQuery: schema.object({}, { unknowns: 'allow' }), + fromDate: schema.maybe(schema.string()), + toDate: schema.maybe(schema.string()), + timeFieldName: schema.maybe(schema.string()), + }), + }, + }, + async (context, req, res) => { + const [{ savedObjects, elasticsearch, uiSettings }, { dataViews }] = + await setup.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(req); + const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + const [includeFrozen, useSampling, metaFields] = await Promise.all([ + uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), + uiSettingsClient.get(FIELD_EXISTENCE_SETTING), + uiSettingsClient.get(UI_SETTINGS.META_FIELDS), + ]); + const esClient = elasticsearch.client.asScoped(req).asCurrentUser; + try { + const dataViewsService = await dataViews.dataViewsServiceFactory( + savedObjectsClient, + esClient + ); + return res.ok({ + body: await fetchFieldExistence({ + ...req.body, + dataViewsService, + includeFrozen, + useSampling, + metaFields, + dataView: await dataViewsService.get(req.params.dataViewId), + search: async (params) => { + const contextCore = await context.core; + return await contextCore.elasticsearch.client.asCurrentUser.search< + estypes.SearchHit[] + >( + { ...params }, + { + // Global request timeout. Will cancel the request if exceeded. Overrides the elasticsearch.requestTimeout + requestTimeout: '5000ms', + // Fails fast instead of retrying- default is to retry + maxRetries: 0, + } + ); + }, + }), + }); + } catch (e) { + if (e instanceof errors.TimeoutError) { + logger.info(`Field existence check timed out on ${req.params.dataViewId}`); + // 408 is Request Timeout + return res.customError({ statusCode: 408, body: e.message }); + } + logger.info( + `Field existence check failed on ${req.params.dataViewId}: ${ + isBoomError(e) ? e.output.payload.message : e.message + }` + ); + if (e instanceof errors.ResponseError && e.statusCode === 404) { + return res.notFound({ body: e.message }); + } + if (isBoomError(e)) { + if (e.output.statusCode === 404) { + return res.notFound({ body: e.output.payload.message }); + } + throw new Error(e.output.payload.message); + } else { + throw e; + } + } + } + ); +} diff --git a/src/plugins/unified_field_list/server/routes/index.ts b/src/plugins/unified_field_list/server/routes/index.ts index 3558e93e4a780..4fdc5500a0c5b 100644 --- a/src/plugins/unified_field_list/server/routes/index.ts +++ b/src/plugins/unified_field_list/server/routes/index.ts @@ -6,10 +6,12 @@ * Side Public License, v 1. */ -import { CoreSetup } from '@kbn/core/server'; +import { CoreSetup, Logger } from '@kbn/core/server'; import { PluginStart } from '../types'; +import { existingFieldsRoute } from './existing_fields'; import { initFieldStatsRoute } from './field_stats'; -export function defineRoutes(setup: CoreSetup) { +export function defineRoutes(setup: CoreSetup, logger: Logger) { initFieldStatsRoute(setup); + existingFieldsRoute(setup, logger); } diff --git a/x-pack/plugins/lens/server/ui_settings.ts b/src/plugins/unified_field_list/server/ui_settings.ts similarity index 57% rename from x-pack/plugins/lens/server/ui_settings.ts rename to src/plugins/unified_field_list/server/ui_settings.ts index 8220eb591b903..fe46b7925bfa0 100644 --- a/x-pack/plugins/lens/server/ui_settings.ts +++ b/src/plugins/unified_field_list/server/ui_settings.ts @@ -1,34 +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. + * 2.0 and the Server Side Public License, v 1; you may not 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 { schema } from '@kbn/config-schema'; import { UiSettingsParams } from '@kbn/core/server'; - -export const FIELD_EXISTENCE_SETTING = 'lens:useFieldExistenceSampling'; +import { FIELD_EXISTENCE_SETTING } from '../common'; export const getUiSettings: () => Record = () => ({ [FIELD_EXISTENCE_SETTING]: { - name: i18n.translate('xpack.lens.advancedSettings.useFieldExistenceSampling.title', { + name: i18n.translate('unifiedFieldList.advancedSettings.useFieldExistenceSampling.title', { defaultMessage: 'Use field existence sampling', }), value: false, description: i18n.translate( - 'xpack.lens.advancedSettings.useFieldExistenceSampling.description', + 'unifiedFieldList.advancedSettings.useFieldExistenceSampling.description', { defaultMessage: 'If enabled, document sampling is used to determine field existence (available or empty) for the Lens field list instead of relying on index mappings.', } ), deprecation: { - message: i18n.translate('xpack.lens.advancedSettings.useFieldExistenceSampling.deprecation', { - defaultMessage: 'This setting is deprecated and will not be supported as of 8.6.', - }), + message: i18n.translate( + 'unifiedFieldList.advancedSettings.useFieldExistenceSampling.deprecation', + { + defaultMessage: 'This setting is deprecated and will not be supported as of 8.6.', + } + ), docLinksKey: 'visualizationSettings', }, category: ['visualization'], 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 d37c4bb72d40e..c37e050b0823a 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 @@ -378,7 +378,9 @@ export default class QueryStringInputUI extends PureComponent { } break; case KEY_CODES.ESC: - event.preventDefault(); + if (isSuggestionsVisible) { + event.preventDefault(); + } this.setState({ isSuggestionsVisible: false, index: null }); break; case KEY_CODES.TAB: diff --git a/src/plugins/vis_types/table/kibana.json b/src/plugins/vis_types/table/kibana.json index 76ad6a7fec58f..0d5d6066643ad 100644 --- a/src/plugins/vis_types/table/kibana.json +++ b/src/plugins/vis_types/table/kibana.json @@ -6,7 +6,8 @@ "requiredPlugins": [ "expressions", "visualizations", - "fieldFormats" + "fieldFormats", + "usageCollection" ], "requiredBundles": [ "data", diff --git a/src/plugins/vis_types/table/public/plugin.ts b/src/plugins/vis_types/table/public/plugin.ts index ca05434c031e8..9e246448eb044 100644 --- a/src/plugins/vis_types/table/public/plugin.ts +++ b/src/plugins/vis_types/table/public/plugin.ts @@ -9,10 +9,7 @@ import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import type { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public'; import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; -import type { - UsageCollectionSetup, - UsageCollectionStart, -} from '@kbn/usage-collection-plugin/public'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { setFormatService } from './services'; @@ -22,13 +19,12 @@ import { registerTableVis } from './register_vis'; export interface TablePluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; - usageCollection?: UsageCollectionSetup; } /** @internal */ export interface TablePluginStartDependencies { fieldFormats: FieldFormatsStart; - usageCollection?: UsageCollectionStart; + usageCollection: UsageCollectionStart; } /** @internal */ diff --git a/src/plugins/vis_types/table/public/services.ts b/src/plugins/vis_types/table/public/services.ts index 345c63474d172..edc6969e389ed 100644 --- a/src/plugins/vis_types/table/public/services.ts +++ b/src/plugins/vis_types/table/public/services.ts @@ -7,11 +7,7 @@ */ import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; -import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; export const [getFormatService, setFormatService] = createGetterSetter('FieldFormats'); - -export const [getUsageCollectionStart, setUsageCollectionStart] = - createGetterSetter('UsageCollection', false); diff --git a/src/plugins/vis_types/table/public/table_vis_renderer.tsx b/src/plugins/vis_types/table/public/table_vis_renderer.tsx index 0dc37543dc54e..783cf49d06311 100644 --- a/src/plugins/vis_types/table/public/table_vis_renderer.tsx +++ b/src/plugins/vis_types/table/public/table_vis_renderer.tsx @@ -35,7 +35,7 @@ const extractContainerType = (context?: KibanaExecutionContext): string | undefi export const getTableVisRenderer: ( core: CoreStart, - usageCollection?: UsageCollectionStart + usageCollection: UsageCollectionStart ) => ExpressionRenderDefinition = (core, usageCollection) => ({ name: 'table_vis', reuseDomNode: true, @@ -51,7 +51,7 @@ export const getTableVisRenderer: ( const containerType = extractContainerType(handlers.getExecutionContext()); const visualizationType = 'agg_based'; - if (usageCollection && containerType) { + if (containerType) { const counterEvents = [ `render_${visualizationType}_table`, !visData.table ? `render_${visualizationType}_table_split` : undefined, diff --git a/src/plugins/vis_types/timeseries/kibana.json b/src/plugins/vis_types/timeseries/kibana.json index bf6a781de9525..9194743d3af6b 100644 --- a/src/plugins/vis_types/timeseries/kibana.json +++ b/src/plugins/vis_types/timeseries/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["charts", "data", "expressions", "visualizations", "inspector", "dataViews", "fieldFormats"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "inspector", "dataViews", "fieldFormats", "usageCollection"], "optionalPlugins": ["home"], "requiredBundles": ["unifiedSearch", "kibanaUtils", "kibanaReact", "fieldFormats"], "owner": { diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index edd4ac0297b3c..012bdd35115f2 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -24,9 +24,9 @@ import { extractIndexPatternValues, isStringTypeIndexPattern, } from '../common/index_patterns_utils'; -import { TSVB_DEFAULT_COLOR } from '../common/constants'; +import { TSVB_DEFAULT_COLOR, UI_SETTINGS } from '../common/constants'; import { toExpressionAst } from './to_ast'; -import { getDataViewsStart } from './services'; +import { getDataViewsStart, getUISettings } from './services'; import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types'; import type { IndexPatternValue, Panel } from '../common/types'; import { convertTSVBtoLensConfiguration } from './convert_to_lens'; @@ -176,5 +176,6 @@ export const metricsVisDefinition: VisTypeDefinition< requests: new RequestAdapter(), }), requiresSearch: true, + suppressWarnings: () => !getUISettings().get(UI_SETTINGS.ALLOW_CHECKING_FOR_FAILED_SHARDS), getUsedIndexPattern: getUsedIndexPatterns, }; diff --git a/src/plugins/vis_types/timeseries/public/plugin.ts b/src/plugins/vis_types/timeseries/public/plugin.ts index b784db9a92f86..8ca5a916a7ce3 100644 --- a/src/plugins/vis_types/timeseries/public/plugin.ts +++ b/src/plugins/vis_types/timeseries/public/plugin.ts @@ -43,7 +43,7 @@ export interface MetricsPluginStartDependencies { fieldFormats: FieldFormatsStart; dataViews: DataViewsPublicPluginStart; charts: ChartsPluginStart; - usageCollection?: UsageCollectionStart; + usageCollection: UsageCollectionStart; } /** @internal */ @@ -77,8 +77,6 @@ export class MetricsPlugin implements Plugin { setDataStart(data); setDataViewsStart(dataViews); setCoreStart(core); - if (usageCollection) { - setUsageCollectionStart(usageCollection); - } + setUsageCollectionStart(usageCollection); } } diff --git a/src/plugins/vis_types/timeseries/public/request_handler.ts b/src/plugins/vis_types/timeseries/public/request_handler.ts index 2cf0e740178a9..24768dc10a17f 100644 --- a/src/plugins/vis_types/timeseries/public/request_handler.ts +++ b/src/plugins/vis_types/timeseries/public/request_handler.ts @@ -10,7 +10,7 @@ import type { Adapters } from '@kbn/inspector-plugin/common'; import { KibanaContext } from '@kbn/data-plugin/public'; import { getTimezone } from './application/lib/get_timezone'; import { getUISettings, getDataStart, getCoreStart } from './services'; -import { ROUTES, UI_SETTINGS } from '../common/constants'; +import { ROUTES } from '../common/constants'; import type { TimeseriesVisParams } from './types'; import type { TimeseriesVisData } from '../common/types'; @@ -84,14 +84,6 @@ export const metricsRequestHandler = async ({ ?.start(query.label ?? key, { searchSessionId }) .json(query.body) .ok({ time: query.time, json: { rawResponse: query.response } }); - - if ( - query.response && - inspectorAdapters?.requests && - config.get(UI_SETTINGS.ALLOW_CHECKING_FOR_FAILED_SHARDS) - ) { - data.search.showWarnings(inspectorAdapters.requests); - } }); return visData; diff --git a/src/plugins/vis_types/timeseries/public/services.ts b/src/plugins/vis_types/timeseries/public/services.ts index 4b0e4082d0dc5..c68971e24822b 100644 --- a/src/plugins/vis_types/timeseries/public/services.ts +++ b/src/plugins/vis_types/timeseries/public/services.ts @@ -31,4 +31,4 @@ export const [getI18n, setI18n] = createGetterSetter('I18n'); export const [getCharts, setCharts] = createGetterSetter('ChartsPluginStart'); export const [getUsageCollectionStart, setUsageCollectionStart] = - createGetterSetter('UsageCollection', false); + createGetterSetter('UsageCollection'); diff --git a/src/plugins/vis_types/vega/kibana.json b/src/plugins/vis_types/vega/kibana.json index 423f044aa3275..5fbaabec722aa 100644 --- a/src/plugins/vis_types/vega/kibana.json +++ b/src/plugins/vis_types/vega/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "visualizations", "mapsEms", "expressions", "inspector", "dataViews"], + "requiredPlugins": ["data", "visualizations", "mapsEms", "expressions", "inspector", "dataViews", "usageCollection"], "optionalPlugins": ["home"], "requiredBundles": ["kibanaUtils", "kibanaReact", "visDefaultEditor"], "owner": { diff --git a/src/plugins/vis_types/vega/public/plugin.ts b/src/plugins/vis_types/vega/public/plugin.ts index db4d4a458458a..54f319850d817 100644 --- a/src/plugins/vis_types/vega/public/plugin.ts +++ b/src/plugins/vis_types/vega/public/plugin.ts @@ -58,7 +58,7 @@ export interface VegaPluginStartDependencies { data: DataPublicPluginStart; mapsEms: MapsEmsPluginPublicStart; dataViews: DataViewsPublicPluginStart; - usageCollection?: UsageCollectionStart; + usageCollection: UsageCollectionStart; } /** @internal */ @@ -104,9 +104,6 @@ export class VegaPlugin implements Plugin { setDataViews(dataViews); setDocLinks(core.docLinks); setMapsEms(mapsEms); - - if (usageCollection) { - setUsageCollectionStart(usageCollection); - } + setUsageCollectionStart(usageCollection); } } diff --git a/src/plugins/vis_types/vega/public/services.ts b/src/plugins/vis_types/vega/public/services.ts index a2bc3524ce022..4b3e0ca72cdc3 100644 --- a/src/plugins/vis_types/vega/public/services.ts +++ b/src/plugins/vis_types/vega/public/services.ts @@ -34,4 +34,4 @@ export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls; export const [getDocLinks, setDocLinks] = createGetterSetter('docLinks'); export const [getUsageCollectionStart, setUsageCollectionStart] = - createGetterSetter('UsageCollection', false); + createGetterSetter('UsageCollection'); diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index 69b9eb6c2b090..bf7f7f9cdff0e 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -5,6 +5,7 @@ "ui": true, "requiredPlugins": [ "data", + "charts", "expressions", "fieldFormats", "uiActions", @@ -19,7 +20,7 @@ "dataViewEditor" ], "optionalPlugins": ["home", "share", "spaces", "savedObjectsTaggingOss"], - "requiredBundles": ["kibanaUtils", "savedSearch", "kibanaReact"], + "requiredBundles": ["kibanaUtils", "savedSearch", "kibanaReact", "charts"], "extraPublicDirs": ["common/constants", "common/utils", "common/expression_functions"], "owner": { "name": "Vis Editors", diff --git a/src/plugins/visualizations/public/embeddable/embeddables.scss b/src/plugins/visualizations/public/embeddable/embeddables.scss index 2a4d5c14a46a5..3b96abda035a2 100644 --- a/src/plugins/visualizations/public/embeddable/embeddables.scss +++ b/src/plugins/visualizations/public/embeddable/embeddables.scss @@ -19,6 +19,13 @@ } } +.visPanel__warnings { + position: absolute; + z-index: 2; + right: $euiSizeM; + bottom: $euiSizeM; +} + // OPTIONS MENU /** diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx index d616f48083e00..fcdcede24409e 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx @@ -18,6 +18,7 @@ import type { ErrorLike } from '@kbn/expressions-plugin/common'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { TimefilterContract } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { Warnings } from '@kbn/charts-plugin/public'; import { Adapters, AttributeService, @@ -115,6 +116,7 @@ export class VisualizeEmbeddable private expression?: ExpressionAstExpression; private vis: Vis; private domNode: any; + private warningDomNode: any; public readonly type = VISUALIZE_EMBEDDABLE_TYPE; private abortController?: AbortController; private readonly deps: VisualizeEmbeddableFactoryDeps; @@ -332,6 +334,31 @@ export class VisualizeEmbeddable return dirty; } + private handleWarnings() { + const warnings: React.ReactNode[] = []; + if (this.getInspectorAdapters()?.requests) { + this.deps + .start() + .plugins.data.search.showWarnings(this.getInspectorAdapters()!.requests!, (warning) => { + if ( + warning.type === 'shard_failure' && + warning.reason.type === 'unsupported_aggregation_on_downsampled_index' + ) { + warnings.push(warning.reason.reason || warning.message); + return true; + } + if (this.vis.type.suppressWarnings?.()) { + // if the vis type wishes to supress all warnings, return true so the default logic won't pick it up + return true; + } + }); + } + + if (this.warningDomNode) { + render(, this.warningDomNode); + } + } + // this is a hack to make editor still work, will be removed once we clean up editor // @ts-ignore hasInspector = () => Boolean(this.getInspectorAdapters()); @@ -347,6 +374,7 @@ export class VisualizeEmbeddable }; onContainerData = () => { + this.handleWarnings(); this.updateOutput({ ...this.getOutput(), loading: false, @@ -386,6 +414,11 @@ export class VisualizeEmbeddable div.className = `visualize panel-content panel-content--fullWidth`; domNode.appendChild(div); + const warningDiv = document.createElement('div'); + warningDiv.className = 'visPanel__warnings'; + domNode.appendChild(warningDiv); + this.warningDomNode = warningDiv; + this.domNode = div; super.render(this.domNode); @@ -532,6 +565,7 @@ export class VisualizeEmbeddable timeRange: this.timeRange, query: this.input.query, filters: this.input.filters, + disableShardWarnings: true, }, variables: { embeddableTitle: this.getTitle(), 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 18dcacadcaeab..1b0875f636af4 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -40,6 +40,7 @@ export class BaseVisType { public readonly editorConfig; public hidden; public readonly requiresSearch; + public readonly suppressWarnings; public readonly hasPartialRows; public readonly hierarchicalData; public readonly setup; @@ -64,6 +65,7 @@ export class BaseVisType { this.title = opts.title; this.icon = opts.icon; this.image = opts.image; + this.suppressWarnings = opts.suppressWarnings; this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} }); this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} }); this.options = defaultsDeep({}, opts.options, defaultOptions); diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index ebcdcf594fdfd..45be7f08ca711 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -214,6 +214,10 @@ export interface VisTypeDefinition { * It sets the vis type on a deprecated mode when is true */ readonly isDeprecated?: boolean; + /** + * If returns true, no warning toasts will be shown + */ + readonly suppressWarnings?: () => boolean; /** * Describes the experience group that the visualization belongs. * It can be on tools, aggregation based or promoted group. diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 02eda42929b9f..6a533c71facd7 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -14,6 +14,7 @@ ], "references": [ { "path": "../../core/tsconfig.json" }, + { "path": "../charts/tsconfig.json" }, { "path": "../data/tsconfig.json" }, { "path": "../data_views/tsconfig.json" }, { "path": "../expressions/tsconfig.json" }, diff --git a/x-pack/test/api_integration/apis/lens/existing_fields.ts b/test/api_integration/apis/unified_field_list/existing_fields.ts similarity index 82% rename from x-pack/test/api_integration/apis/lens/existing_fields.ts rename to test/api_integration/apis/unified_field_list/existing_fields.ts index 0934c8bddd539..1543825441604 100644 --- a/x-pack/test/api_integration/apis/lens/existing_fields.ts +++ b/test/api_integration/apis/unified_field_list/existing_fields.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import expect from '@kbn/expect'; @@ -29,26 +30,32 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); + const dataViewId = 'existence_index'; + const API_PATH = `/api/unified_field_list/existing_fields/${dataViewId}`; describe('existing_fields apis', () => { before(async () => { - await esArchiver.load('x-pack/test/api_integration/es_archives/lens/constant_keyword'); + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); await kibanaServer.importExport.load( - 'x-pack/test/api_integration/fixtures/kbn_archiver/lens/constant_keyword.json' + 'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json' ); }); after(async () => { - await esArchiver.unload('x-pack/test/api_integration/es_archives/lens/constant_keyword'); + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); await kibanaServer.importExport.unload( - 'x-pack/test/api_integration/fixtures/kbn_archiver/lens/constant_keyword.json' + 'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json' ); }); describe('existence', () => { it('should find which fields exist in the sample documents', async () => { const { body } = await supertest - .post(`/api/lens/existing_fields/existence_index`) + .post(API_PATH) .set(COMMON_HEADERS) .send({ dslQuery: { @@ -75,7 +82,7 @@ export default ({ getService }: FtrProviderContext) => { ]; const { body } = await supertest - .post(`/api/lens/existing_fields/existence_index`) + .post(API_PATH) .set(COMMON_HEADERS) .send({ dslQuery: { @@ -102,7 +109,7 @@ export default ({ getService }: FtrProviderContext) => { ]; const { body } = await supertest - .post(`/api/lens/existing_fields/existence_index`) + .post(API_PATH) .set(COMMON_HEADERS) .send({ dslQuery: { @@ -129,7 +136,7 @@ export default ({ getService }: FtrProviderContext) => { ]; const { body } = await supertest - .post(`/api/lens/existing_fields/existence_index`) + .post(API_PATH) .set(COMMON_HEADERS) .send({ dslQuery: { diff --git a/test/api_integration/apis/unified_field_list/index.ts b/test/api_integration/apis/unified_field_list/index.ts index da0a6098e0a42..bf146349d4b1a 100644 --- a/test/api_integration/apis/unified_field_list/index.ts +++ b/test/api_integration/apis/unified_field_list/index.ts @@ -10,6 +10,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function lensApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('UnifiedFieldList', () => { + loadTestFile(require.resolve('./existing_fields')); + loadTestFile(require.resolve('./legacy_existing_fields')); loadTestFile(require.resolve('./field_stats')); }); } diff --git a/x-pack/test/api_integration/apis/lens/legacy_existing_fields.ts b/test/api_integration/apis/unified_field_list/legacy_existing_fields.ts similarity index 93% rename from x-pack/test/api_integration/apis/lens/legacy_existing_fields.ts rename to test/api_integration/apis/unified_field_list/legacy_existing_fields.ts index 6963df17ddedd..7791f45eee107 100644 --- a/x-pack/test/api_integration/apis/lens/legacy_existing_fields.ts +++ b/test/api_integration/apis/unified_field_list/legacy_existing_fields.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import expect from '@kbn/expect'; @@ -185,7 +186,7 @@ export default ({ getService }: FtrProviderContext) => { describe('existence', () => { it('should find which fields exist in the sample documents', async () => { const { body } = await supertest - .post(`/api/lens/existing_fields/${encodeURIComponent('logstash-*')}`) + .post(`/api/unified_field_list/existing_fields/${encodeURIComponent('logstash-*')}`) .set(COMMON_HEADERS) .send({ dslQuery: { @@ -204,7 +205,7 @@ export default ({ getService }: FtrProviderContext) => { it('should succeed for thousands of fields', async () => { const { body } = await supertest - .post(`/api/lens/existing_fields/${encodeURIComponent('metricbeat-*')}`) + .post(`/api/unified_field_list/existing_fields/${encodeURIComponent('metricbeat-*')}`) .set(COMMON_HEADERS) .send({ dslQuery: { match_all: {} }, @@ -255,7 +256,7 @@ export default ({ getService }: FtrProviderContext) => { ]; const { body } = await supertest - .post(`/api/lens/existing_fields/${encodeURIComponent('logstash-*')}`) + .post(`/api/unified_field_list/existing_fields/${encodeURIComponent('logstash-*')}`) .set(COMMON_HEADERS) .send({ dslQuery: { diff --git a/x-pack/test/api_integration/es_archives/lens/constant_keyword/data.json b/test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword/data.json similarity index 100% rename from x-pack/test/api_integration/es_archives/lens/constant_keyword/data.json rename to test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword/data.json diff --git a/x-pack/test/api_integration/es_archives/lens/constant_keyword/mappings.json b/test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword/mappings.json similarity index 100% rename from x-pack/test/api_integration/es_archives/lens/constant_keyword/mappings.json rename to test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword/mappings.json diff --git a/test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json b/test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json new file mode 100644 index 0000000000000..fb7c105ec462b --- /dev/null +++ b/test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json @@ -0,0 +1,16 @@ +{ + "attributes": { + "timeFieldName": "ts", + "title": "existence_index_*", + "runtimeFieldMap":"{\"data_view_runtime_field\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit('a')\"}}}" + }, + "coreMigrationVersion": "8.0.0", + "id": "existence_index", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z", + "version": "WzEzLDJd" +} diff --git a/test/examples/search/warnings.ts b/test/examples/search/warnings.ts index 78aa042ce6ea6..fc1949549d66e 100644 --- a/test/examples/search/warnings.ts +++ b/test/examples/search/warnings.ts @@ -6,15 +6,19 @@ * Side Public License, v 1. */ +import type { estypes } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; import { asyncForEach } from '@kbn/std'; -import { FtrProviderContext } from '../../functional/ftr_provider_context'; +import assert from 'assert'; +import type { FtrProviderContext } from '../../functional/ftr_provider_context'; +import type { WebElementWrapper } from '../../functional/services/lib/web_element_wrapper'; // eslint-disable-next-line import/no-default-export export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker']); const testSubjects = getService('testSubjects'); const find = getService('find'); + const retry = getService('retry'); const es = getService('es'); const log = getService('log'); const indexPatterns = getService('indexPatterns'); @@ -22,8 +26,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); - // Failing: See https://github.com/elastic/kibana/issues/139879 - describe.skip('handling warnings with search source fetch', function () { + describe('handling warnings with search source fetch', function () { const dataViewTitle = 'sample-01,sample-01-rollup'; const fromTime = 'Jun 17, 2022 @ 00:00:00.000'; const toTime = 'Jun 23, 2022 @ 00:00:00.000'; @@ -32,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testRollupIndex = 'sample-01-rollup'; const testRollupField = 'kubernetes.container.memory.usage.bytes'; const toastsSelector = '[data-test-subj=globalToastList] [data-test-subj=euiToastHeader]'; - const shardFailureType = 'unsupported_aggregation_on_rollup_index'; + const shardFailureType = 'unsupported_aggregation_on_downsampled_index'; const shardFailureReason = `Field [${testRollupField}] of type [aggregate_metric_double] is not supported for aggregation [percentiles]`; const getTestJson = async (tabTestSubj: string, codeTestSubj: string) => { @@ -51,10 +54,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await es.indices.addBlock({ index: testIndex, block: 'write' }); try { log.info(`rolling up ${testIndex} index...`); - await es.rollup.rollup({ - index: testIndex, - rollup_index: testRollupIndex, - config: { fixed_interval: '1h' }, + // es client currently does not have method for downsample + await es.transport.request({ + method: 'POST', + path: '/sample-01/_downsample/sample-01-rollup', + body: { fixed_interval: '1h' }, }); } catch (err) { log.info(`ignoring resource_already_exists_exception...`); @@ -76,6 +80,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: '0ae0bc7a-e4ca-405c-ab67-f2b5913f2a51', 'timepicker:timeDefaults': '{ "from": "now-1y", "to": "now" }', }); + + await PageObjects.common.navigateToApp('searchExamples'); + }); + + beforeEach(async () => { + await comboBox.setCustom('dataViewSelector', dataViewTitle); + await comboBox.set('searchMetricField', testRollupField); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); after(async () => { @@ -84,23 +96,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.replace({}); }); - beforeEach(async () => { - // reload the page to clear toasts from previous test - - await PageObjects.common.navigateToApp('searchExamples'); - - await comboBox.setCustom('dataViewSelector', dataViewTitle); - await comboBox.set('searchMetricField', testRollupField); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + afterEach(async () => { + await PageObjects.common.clearAllToasts(); }); it('shows shard failure warning notifications by default', async () => { await testSubjects.click('searchSourceWithOther'); + // wait for response - toasts appear before the response is rendered + let response: estypes.SearchResponse | undefined; + await retry.try(async () => { + response = await getTestJson('responseTab', 'responseCodeBlock'); + expect(response).not.to.eql({}); + }); + // toasts const toasts = await find.allByCssSelector(toastsSelector); - expect(toasts.length).to.be(3); - const expects = ['2 of 4 shards failed', '2 of 4 shards failed', 'Query result']; // BUG: there are 2 shards failed toast notifications + expect(toasts.length).to.be(2); + const expects = ['2 of 4 shards failed', 'Query result']; await asyncForEach(toasts, async (t, index) => { expect(await t.getVisibleText()).to.eql(expects[index]); }); @@ -119,12 +132,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const responseBlock = await testSubjects.find('shardsFailedModalResponseBlock'); expect(await responseBlock.getVisibleText()).to.contain(shardFailureReason); - // close things await testSubjects.click('closeShardFailureModal'); - await PageObjects.common.clearAllToasts(); // response tab - const response = await getTestJson('responseTab', 'responseCodeBlock'); + assert(response && response._shards.failures); expect(response._shards.total).to.be(4); expect(response._shards.successful).to.be(2); expect(response._shards.skipped).to.be(0); @@ -142,9 +153,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('able to handle shard failure warnings and prevent default notifications', async () => { await testSubjects.click('searchSourceWithoutOther'); - // toasts - const toasts = await find.allByCssSelector(toastsSelector); - expect(toasts.length).to.be(2); + // wait for toasts - toasts appear after the response is rendered + let toasts: WebElementWrapper[] = []; + await retry.try(async () => { + toasts = await find.allByCssSelector(toastsSelector); + expect(toasts.length).to.be(2); + }); const expects = ['2 of 4 shards failed', 'Query result']; await asyncForEach(toasts, async (t, index) => { expect(await t.getVisibleText()).to.eql(expects[index]); @@ -164,9 +178,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const responseBlock = await testSubjects.find('shardsFailedModalResponseBlock'); expect(await responseBlock.getVisibleText()).to.contain(shardFailureReason); - // close things await testSubjects.click('closeShardFailureModal'); - await PageObjects.common.clearAllToasts(); // response tab const response = await getTestJson('responseTab', 'responseCodeBlock'); diff --git a/test/functional/apps/console/_comments.ts b/test/functional/apps/console/_comments.ts index 9132e0f04b0c8..3250035275976 100644 --- a/test/functional/apps/console/_comments.ts +++ b/test/functional/apps/console/_comments.ts @@ -15,8 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'console', 'header']); - // Failing: See https://github.com/elastic/kibana/issues/139295 - describe.skip('console app', function testComments() { + describe('console app', function testComments() { this.tags('includeFirefox'); before(async () => { log.debug('navigateTo console'); @@ -47,8 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); } - // FLAKY: https://github.com/elastic/kibana/issues/138160 - describe.skip('with single line comments', async () => { + describe('with single line comments', async () => { await runTests( [ { @@ -129,6 +127,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { body: '{\n "query": \'\'', // E.g. using single quotes }, ], + async () => { expect(await PageObjects.console.hasInvalidSyntax()).to.be(true); } diff --git a/test/functional/apps/dashboard/group3/dashboard_state.ts b/test/functional/apps/dashboard/group3/dashboard_state.ts index 8bff6355988f2..bc3f2ed2774a0 100644 --- a/test/functional/apps/dashboard/group3/dashboard_state.ts +++ b/test/functional/apps/dashboard/group3/dashboard_state.ts @@ -40,7 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }; - describe('dashboard state', function describeIndexTests() { + // Failing: See https://github.com/elastic/kibana/issues/139762 + describe.skip('dashboard state', function describeIndexTests() { // Used to track flag before and after reset let isNewChartsLibraryEnabled = true; diff --git a/test/functional/apps/visualize/group4/_tsvb_chart.ts b/test/functional/apps/visualize/group4/_tsvb_chart.ts index 013c0473a59b9..b71458c5c5527 100644 --- a/test/functional/apps/visualize/group4/_tsvb_chart.ts +++ b/test/functional/apps/visualize/group4/_tsvb_chart.ts @@ -18,17 +18,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const security = getService('security'); const kibanaServer = getService('kibanaServer'); - const { timePicker, visChart, visualBuilder, visualize, settings } = getPageObjects([ - 'timePicker', + const { visChart, visualBuilder, visualize, settings, common } = getPageObjects([ 'visChart', 'visualBuilder', 'visualize', 'settings', + 'common', ]); + const from = 'Sep 19, 2015 @ 06:31:44.000'; + const to = 'Sep 22, 2015 @ 18:31:44.000'; + describe('visual builder', function describeIndexTests() { before(async () => { await visualize.initTests(); + await common.setTime({ from, to }); }); beforeEach(async () => { @@ -36,6 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ['kibana_admin', 'test_logstash_reader', 'kibana_sample_admin'], { skipBrowserRefresh: true } ); + await visualize.navigateToNewVisualization(); await visualize.clickVisualBuilder(); await visualBuilder.checkVisualBuilderIsPresent(); @@ -398,10 +403,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.setMetricsDataTimerangeMode('Last value'); await visualBuilder.setDropLastBucket(true); await visualBuilder.clickDataTab('metric'); - await timePicker.setAbsoluteRange( - 'Sep 19, 2015 @ 06:31:44.000', - 'Sep 22, 2015 @ 18:31:44.000' - ); }); const switchIndexTest = async (useKibanaIndexes: boolean) => { @@ -435,10 +436,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.clickPanelOptions('metric'); await visualBuilder.setMetricsDataTimerangeMode('Last value'); await visualBuilder.setDropLastBucket(true); - await timePicker.setAbsoluteRange( - 'Sep 19, 2015 @ 06:31:44.000', - 'Sep 22, 2015 @ 18:31:44.000' - ); }); it('should be able to switch to gte interval (>=2d)', async () => { diff --git a/x-pack/packages/ml/agg_utils/kibana.jsonc b/x-pack/packages/ml/agg_utils/kibana.jsonc new file mode 100644 index 0000000000000..4bcfcbd8721a6 --- /dev/null +++ b/x-pack/packages/ml/agg_utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-agg-utils", + "owner": "@elastic/ml-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/x-pack/packages/ml/aiops_components/kibana.jsonc b/x-pack/packages/ml/aiops_components/kibana.jsonc new file mode 100644 index 0000000000000..3fc4f228e2b0f --- /dev/null +++ b/x-pack/packages/ml/aiops_components/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/aiops-components", + "owner": "@elastic/ml-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/x-pack/packages/ml/aiops_utils/kibana.jsonc b/x-pack/packages/ml/aiops_utils/kibana.jsonc new file mode 100644 index 0000000000000..46d16f2cdc334 --- /dev/null +++ b/x-pack/packages/ml/aiops_utils/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/aiops-utils", + "owner": "@elastic/ml-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/x-pack/packages/ml/aiops_utils/src/stream_factory.test.ts b/x-pack/packages/ml/aiops_utils/src/stream_factory.test.ts index a0c5212244ad6..1e6d7b40b22d0 100644 --- a/x-pack/packages/ml/aiops_utils/src/stream_factory.test.ts +++ b/x-pack/packages/ml/aiops_utils/src/stream_factory.test.ts @@ -44,7 +44,12 @@ describe('streamFactory', () => { streamResult += chunk.toString('utf8'); } - expect(responseWithHeaders.headers).toBe(undefined); + expect(responseWithHeaders.headers).toStrictEqual({ + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Transfer-Encoding': 'chunked', + 'X-Accel-Buffering': 'no', + }); expect(streamResult).toBe('push1push2'); }); @@ -65,7 +70,12 @@ describe('streamFactory', () => { const parsedItems = streamItems.map((d) => JSON.parse(d)); - expect(responseWithHeaders.headers).toBe(undefined); + expect(responseWithHeaders.headers).toStrictEqual({ + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Transfer-Encoding': 'chunked', + 'X-Accel-Buffering': 'no', + }); expect(parsedItems).toHaveLength(2); expect(parsedItems[0]).toStrictEqual(mockItem1); expect(parsedItems[1]).toStrictEqual(mockItem2); @@ -105,7 +115,13 @@ describe('streamFactory', () => { const streamResult = decoded.toString('utf8'); - expect(responseWithHeaders.headers).toStrictEqual({ 'content-encoding': 'gzip' }); + expect(responseWithHeaders.headers).toStrictEqual({ + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'content-encoding': 'gzip', + 'Transfer-Encoding': 'chunked', + 'X-Accel-Buffering': 'no', + }); expect(streamResult).toBe('push1push2'); done(); @@ -143,7 +159,13 @@ describe('streamFactory', () => { const parsedItems = streamItems.map((d) => JSON.parse(d)); - expect(responseWithHeaders.headers).toStrictEqual({ 'content-encoding': 'gzip' }); + expect(responseWithHeaders.headers).toStrictEqual({ + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'content-encoding': 'gzip', + 'Transfer-Encoding': 'chunked', + 'X-Accel-Buffering': 'no', + }); expect(parsedItems).toHaveLength(2); expect(parsedItems[0]).toStrictEqual(mockItem1); expect(parsedItems[1]).toStrictEqual(mockItem2); diff --git a/x-pack/packages/ml/aiops_utils/src/stream_factory.ts b/x-pack/packages/ml/aiops_utils/src/stream_factory.ts index 9df9702eb0870..7d685369e4d10 100644 --- a/x-pack/packages/ml/aiops_utils/src/stream_factory.ts +++ b/x-pack/packages/ml/aiops_utils/src/stream_factory.ts @@ -106,13 +106,16 @@ export function streamFactory( const responseWithHeaders: StreamFactoryReturnType['responseWithHeaders'] = { body: stream, - ...(isCompressed - ? { - headers: { - 'content-encoding': 'gzip', - }, - } - : {}), + headers: { + ...(isCompressed ? { 'content-encoding': 'gzip' } : {}), + + // This disables response buffering on proxy servers (Nginx, uwsgi, fastcgi, etc.) + // Otherwise, those proxies buffer responses up to 4/8 KiB. + 'X-Accel-Buffering': 'no', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Transfer-Encoding': 'chunked', + }, }; return { DELIMITER, end, push, responseWithHeaders }; diff --git a/x-pack/packages/ml/is_populated_object/kibana.jsonc b/x-pack/packages/ml/is_populated_object/kibana.jsonc new file mode 100644 index 0000000000000..44240b60c4198 --- /dev/null +++ b/x-pack/packages/ml/is_populated_object/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-is-populated-object", + "owner": "@elastic/ml-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/x-pack/packages/ml/string_hash/kibana.jsonc b/x-pack/packages/ml/string_hash/kibana.jsonc new file mode 100644 index 0000000000000..0986bb25375c5 --- /dev/null +++ b/x-pack/packages/ml/string_hash/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-string-hash", + "owner": "@elastic/ml-ui", + "runtimeDeps": [], + "typeDeps": [] +} diff --git a/x-pack/plugins/aiops/kibana.json b/x-pack/plugins/aiops/kibana.json index 7c3a76deae6ee..6648816b07843 100755 --- a/x-pack/plugins/aiops/kibana.json +++ b/x-pack/plugins/aiops/kibana.json @@ -12,11 +12,9 @@ "requiredPlugins": [ "charts", "data", - "discover", - "licensing", - "unifiedSearch" + "licensing" ], "optionalPlugins": [], - "requiredBundles": ["kibanaReact", "fieldFormats"], + "requiredBundles": ["fieldFormats"], "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/aiops/public/application/services/time_field_range.ts b/x-pack/plugins/aiops/public/application/services/time_field_range.ts index 380538f723687..845ec7f71f5f2 100644 --- a/x-pack/plugins/aiops/public/application/services/time_field_range.ts +++ b/x-pack/plugins/aiops/public/application/services/time_field_range.ts @@ -10,7 +10,8 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { getCoreStart } from '../../kibana_services'; + +import type { HttpStart } from '@kbn/core/public'; export interface GetTimeFieldRangeResponse { success: boolean; @@ -23,16 +24,17 @@ export async function getTimeFieldRange({ timeFieldName, query, runtimeMappings, + http, }: { index: string; timeFieldName?: string; query?: QueryDslQueryContainer; runtimeMappings?: estypes.MappingRuntimeFields; + http: HttpStart; }) { const body = JSON.stringify({ index, timeFieldName, query, runtimeMappings }); - const coreStart = getCoreStart(); - return await coreStart.http.fetch({ + return await http.fetch({ path: `/internal/file_upload/time_field_range`, method: 'POST', body, diff --git a/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx index 2072d7df7cfe6..605f130594bc8 100644 --- a/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx @@ -16,8 +16,8 @@ import { EuiSuperDatePicker, OnRefreshProps } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public'; -import { useUrlState } from '../../hooks/url_state'; -import { useAiOpsKibana } from '../../kibana_context'; +import { useUrlState } from '../../hooks/use_url_state'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { aiopsRefresh$ } from '../../application/services/timefilter_refresh_service'; interface TimePickerQuickRange { @@ -54,9 +54,8 @@ function updateLastRefresh(timeRange: OnRefreshProps) { } export const DatePickerWrapper: FC = () => { - const { services } = useAiOpsKibana(); - const config = services.uiSettings; - const { timefilter, history } = services.data.query.timefilter; + const { uiSettings, data } = useAiopsAppContext(); + const { timefilter, history } = data.query.timefilter; const [globalState, setGlobalState] = useUrlState('_g'); const getRecentlyUsedRanges = getRecentlyUsedRangesFactory(history); @@ -81,8 +80,8 @@ export const DatePickerWrapper: FC = () => { timefilter.isTimeRangeSelectorEnabled() ); - const dateFormat = config.get('dateFormat'); - const timePickerQuickRanges = config.get( + const dateFormat = uiSettings.get('dateFormat'); + const timePickerQuickRanges = uiSettings.get( UI_SETTINGS.TIMEPICKER_QUICK_RANGES ); diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx index dfb94fe549d3b..5f024ac393c50 100644 --- a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -29,7 +29,7 @@ import type { WindowParameters } from '@kbn/aiops-utils'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; import type { ChangePoint } from '@kbn/ml-agg-utils'; -import { useAiOpsKibana } from '../../../kibana_context'; +import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; import { BrushBadge } from './brush_badge'; @@ -103,9 +103,7 @@ export const DocumentCountChart: FC = ({ changePoint, isBrushCleared, }) => { - const { - services: { data, uiSettings, fieldFormats, charts }, - } = useAiOpsKibana(); + const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext(); const chartTheme = charts.theme.useChartsTheme(); const chartBaseTheme = charts.theme.useChartsBaseTheme(); diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx index 063d27acff53e..8e7907382175b 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx @@ -19,7 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { ChangePoint } from '@kbn/ml-agg-utils'; import type { Query } from '@kbn/es-query'; -import { useAiOpsKibana } from '../../kibana_context'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { initialState, streamReducer } from '../../../common/api/stream_reducer'; import type { ApiExplainLogRateSpikes } from '../../../common/api'; @@ -53,8 +53,8 @@ export const ExplainLogRateSpikesAnalysis: FC onSelectedChangePoint, selectedChangePoint, }) => { - const { services } = useAiOpsKibana(); - const basePath = services.http?.basePath.get() ?? ''; + const { http } = useAiopsAppContext(); + const basePath = http.basePath.get() ?? ''; const [currentAnalysisWindowParameters, setCurrentAnalysisWindowParameters] = useState< WindowParameters | undefined diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx index 56135d4eb7b73..9b9395721e535 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx @@ -32,7 +32,9 @@ import { isRisonSerializationRequired, getNestedProperty, SetUrlState, -} from '../../hooks/url_state'; +} from '../../hooks/use_url_state'; +import type { AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; +import { AiopsAppContext } from '../../hooks/use_aiops_app_context'; import { ExplainLogRateSpikesPage } from './explain_log_rate_spikes_page'; @@ -41,6 +43,8 @@ export interface ExplainLogRateSpikesAppStateProps { dataView: DataView; /** The saved search to analyze. */ savedSearch: SavedSearch | SavedSearchSavedObject | null; + /** App dependencies */ + appDependencies: AiopsAppDependencies; } const defaultSearchQuery = { @@ -69,6 +73,7 @@ export const restorableDefaults = getDefaultAiOpsListState(); export const ExplainLogRateSpikesAppState: FC = ({ dataView, savedSearch, + appDependencies, }) => { const history = useHistory(); const { search: urlSearchString } = useLocation(); @@ -157,8 +162,10 @@ export const ExplainLogRateSpikesAppState: FC } return ( - - - + + + + + ); }; diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx index 3afea37406f05..bdc91175c2913 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx @@ -26,9 +26,9 @@ import { Filter, FilterStateStore, Query } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedSearch } from '@kbn/discover-plugin/public'; -import { useAiOpsKibana } from '../../kibana_context'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { SearchQueryLanguage, SavedSearchSavedObject } from '../../application/utils/search_utils'; -import { useUrlState, usePageUrlState, AppStateKey } from '../../hooks/url_state'; +import { useUrlState, usePageUrlState, AppStateKey } from '../../hooks/use_url_state'; import { useData } from '../../hooks/use_data'; import { FullTimeRangeSelector } from '../full_time_range_selector'; import { DocumentCountContent } from '../document_count_content/document_count_content'; @@ -55,8 +55,7 @@ export const ExplainLogRateSpikesPage: FC = ({ dataView, savedSearch, }) => { - const { services } = useAiOpsKibana(); - const { data: dataService } = services; + const { data: dataService } = useAiopsAppContext(); const [aiopsListState, setAiopsListState] = usePageUrlState(AppStateKey, restorableDefaults); const [globalState, setGlobalState] = useUrlState('_g'); diff --git a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx index 7b0b54680b7b7..1e248552b8484 100644 --- a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx +++ b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx @@ -25,7 +25,7 @@ import { EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useAiOpsKibana } from '../../kibana_context'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { setFullTimeRange } from './full_time_range_selector_service'; import { AIOPS_FROZEN_TIER_PREFERENCE, useStorage } from '../../hooks/use_storage'; @@ -52,16 +52,22 @@ export const FullTimeRangeSelector: FC = ({ callback, }) => { const { - services: { - notifications: { toasts }, - }, - } = useAiOpsKibana(); + http, + notifications: { toasts }, + } = useAiopsAppContext(); // wrapper around setFullTimeRange to allow for the calling of the optional callBack prop const setRange = useCallback( async (i: DataView, q?: QueryDslQueryContainer, excludeFrozenData?: boolean) => { try { - const fullTimeRange = await setFullTimeRange(timefilter, i, q, excludeFrozenData, toasts); + const fullTimeRange = await setFullTimeRange( + timefilter, + i, + toasts, + http, + q, + excludeFrozenData + ); if (typeof callback === 'function') { callback(fullTimeRange); } @@ -76,7 +82,7 @@ export const FullTimeRangeSelector: FC = ({ ); } }, - [callback, timefilter, toasts] + [callback, http, timefilter, toasts] ); const [isPopoverOpen, setPopover] = useState(false); diff --git a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts index 8514e170aca28..f6d6488567f10 100644 --- a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts +++ b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts @@ -13,7 +13,7 @@ import { TimefilterContract } from '@kbn/data-plugin/public'; import dateMath from '@kbn/datemath'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; -import type { ToastsStart } from '@kbn/core/public'; +import type { ToastsStart, HttpStart } from '@kbn/core/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { getTimeFieldRange } from '../../application/services/time_field_range'; @@ -33,9 +33,10 @@ export interface TimeRange { export async function setFullTimeRange( timefilter: TimefilterContract, dataView: DataView, + toasts: ToastsStart, + http: HttpStart, query?: QueryDslQueryContainer, - excludeFrozenData?: boolean, - toasts?: ToastsStart + excludeFrozenData?: boolean ): Promise { const runtimeMappings = dataView.getRuntimeMappings(); const resp = await getTimeFieldRange({ @@ -43,6 +44,7 @@ export async function setFullTimeRange( timeFieldName: dataView.timeFieldName, query: excludeFrozenData ? addExcludeFrozenToQuery(query) : query, ...(isPopulatedObject(runtimeMappings) ? { runtimeMappings } : {}), + http, }); if (resp.start.epoch && resp.end.epoch) { @@ -51,7 +53,7 @@ export async function setFullTimeRange( to: moment(resp.end.epoch).toISOString(), }); } else { - toasts?.addWarning({ + toasts.addWarning({ title: i18n.translate('xpack.aiops.index.fullTimeRangeSelector.noResults', { defaultMessage: 'No results match your search criteria', }), diff --git a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx index 2940983aaabbf..ba1e54550ccb0 100644 --- a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx +++ b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx @@ -14,7 +14,7 @@ import { EuiLoadingChart, EuiTextColor } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { ChangePointHistogramItem } from '@kbn/ml-agg-utils'; -import { useAiOpsKibana } from '../../kibana_context'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { useEuiTheme } from '../../hooks/use_eui_theme'; interface MiniHistogramProps { @@ -24,9 +24,7 @@ interface MiniHistogramProps { } export const MiniHistogram: FC = ({ chartData, isLoading, label }) => { - const { - services: { charts }, - } = useAiOpsKibana(); + const { charts } = useAiopsAppContext(); const euiTheme = useEuiTheme(); const defaultChartTheme = charts.theme.useChartsTheme(); diff --git a/x-pack/plugins/aiops/public/components/search_panel/search_panel.tsx b/x-pack/plugins/aiops/public/components/search_panel/search_panel.tsx index bac4268da6ab2..9388ba27af0b8 100644 --- a/x-pack/plugins/aiops/public/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/aiops/public/components/search_panel/search_panel.tsx @@ -12,8 +12,7 @@ import { Query, Filter } from '@kbn/es-query'; import type { TimeRange } from '@kbn/es-query'; import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { SearchQueryLanguage } from '../../application/utils/search_utils'; -import { useAiOpsKibana } from '../../kibana_context'; -import { getPluginsStart } from '../../kibana_services'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { createMergedEsQuery } from '../../application/utils/search_utils'; interface Props { dataView: DataView; @@ -40,15 +39,15 @@ export const SearchPanel: FC = ({ searchQueryLanguage, setSearchParams, }) => { - const { unifiedSearch } = getPluginsStart(); - const { SearchBar } = unifiedSearch?.ui; const { - services: { - uiSettings, - notifications: { toasts }, - data: { query: queryManager }, + uiSettings, + unifiedSearch: { + ui: { SearchBar }, }, - } = useAiOpsKibana(); + notifications: { toasts }, + data: { query: queryManager }, + } = useAiopsAppContext(); + // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState({ query: searchString || '', diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index b35fbe971b011..8a65a938d38c9 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -6,6 +6,8 @@ */ import React, { FC, useCallback, useMemo, useState } from 'react'; +import { sortBy } from 'lodash'; + import { EuiBadge, EuiBasicTable, @@ -14,16 +16,17 @@ import { EuiTableSortingType, EuiToolTip, } from '@elastic/eui'; -import { sortBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { escapeKuery } from '@kbn/es-query'; import type { ChangePoint } from '@kbn/ml-agg-utils'; + +import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { useEuiTheme } from '../../hooks/use_eui_theme'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; + import { MiniHistogram } from '../mini_histogram'; -import { useAiOpsKibana } from '../../kibana_context'; -import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label'; @@ -64,10 +67,7 @@ export const SpikeAnalysisTable: FC = ({ const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); - const aiOpsKibana = useAiOpsKibana(); - const { - services: { application, share, data }, - } = aiOpsKibana; + const { application, share, data } = useAiopsAppContext(); const discoverLocator = useMemo( () => share.url.locators.get('DISCOVER_APP_LOCATOR'), diff --git a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts new file mode 100644 index 0000000000000..3c44ae7bdb0a3 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.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 { createContext, useContext } from 'react'; + +import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; + +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { CoreStart, CoreSetup, HttpStart, IUiSettingsClient } from '@kbn/core/public'; + +export interface AiopsAppDependencies { + application: CoreStart['application']; + data: DataPublicPluginStart; + charts: ChartsPluginStart; + fieldFormats: FieldFormatsStart; + http: HttpStart; + notifications: CoreSetup['notifications']; + storage: IStorageWrapper; + uiSettings: IUiSettingsClient; + unifiedSearch: UnifiedSearchPublicPluginStart; + share: SharePluginStart; +} + +export const AiopsAppContext = createContext(undefined); + +export const useAiopsAppContext = (): AiopsAppDependencies => { + const aiopsAppContext = useContext(AiopsAppContext); + + // if `undefined`, throw an error + if (aiopsAppContext === undefined) { + throw new Error('useAiopsAppContext was used outside of its Provider'); + } + + return aiopsAppContext; +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index 0c3012c6603ce..471196334e15c 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -16,7 +16,7 @@ import type { SavedSearch } from '@kbn/discover-plugin/public'; import { TimeBuckets } from '../../common/time_buckets'; -import { useAiOpsKibana } from '../kibana_context'; +import { useAiopsAppContext } from './use_aiops_app_context'; import { aiopsRefresh$ } from '../application/services/timefilter_refresh_service'; import type { DocumentStatsSearchStrategyParams } from '../get_document_stats'; import type { AiOpsIndexBasedAppState } from '../components/explain_log_rate_spikes/explain_log_rate_spikes_app_state'; @@ -27,7 +27,7 @@ import { import { useTimefilter } from './use_time_filter'; import { useDocumentCountStats } from './use_document_count_stats'; -import type { Dictionary } from './url_state'; +import type { Dictionary } from './use_url_state'; export const useData = ( { @@ -38,8 +38,12 @@ export const useData = ( onUpdate: (params: Dictionary) => void, selectedChangePoint?: ChangePoint ) => { - const { services } = useAiOpsKibana(); - const { uiSettings, data } = services; + const { + uiSettings, + data: { + query: { filterManager }, + }, + } = useAiopsAppContext(); const [lastRefresh, setLastRefresh] = useState(0); const [fieldStatsRequest, setFieldStatsRequest] = useState< DocumentStatsSearchStrategyParams | undefined @@ -47,12 +51,11 @@ export const useData = ( /** Prepare required params to pass to search strategy **/ const { searchQueryLanguage, searchString, searchQuery } = useMemo(() => { - const filterManager = data.query.filterManager; const searchData = getEsQueryFromSavedSearch({ dataView: currentDataView, uiSettings, savedSearch: currentSavedSearch, - filterManager: data.query.filterManager, + filterManager, }); if (searchData === undefined || aiopsListState.searchString !== '') { diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index 48929c25e800a..1d443d98491b8 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -12,7 +12,6 @@ import { i18n } from '@kbn/i18n'; import type { ToastsStart } from '@kbn/core/public'; import { stringHash } from '@kbn/ml-string-hash'; -import { useAiOpsKibana } from '../kibana_context'; import { extractErrorProperties } from '../application/utils/error_utils'; import { DocumentCountStats, @@ -21,6 +20,8 @@ import { DocumentStatsSearchStrategyParams, } from '../get_document_stats'; +import { useAiopsAppContext } from './use_aiops_app_context'; + export interface DocumentStats { totalCount: number; documentCountStats?: DocumentCountStats; @@ -59,11 +60,9 @@ export function useDocumentCountStats (uiSettings.get('theme:darkMode') ? euiThemeDark : euiThemeLight), diff --git a/x-pack/plugins/aiops/public/hooks/use_storage.ts b/x-pack/plugins/aiops/public/hooks/use_storage.ts index 59325a529728b..c377a85b3e84c 100644 --- a/x-pack/plugins/aiops/public/hooks/use_storage.ts +++ b/x-pack/plugins/aiops/public/hooks/use_storage.ts @@ -6,7 +6,7 @@ */ import { useCallback, useState } from 'react'; -import { useAiOpsKibana } from '../kibana_context'; +import { useAiopsAppContext } from './use_aiops_app_context'; export const AIOPS_FROZEN_TIER_PREFERENCE = 'aiops.frozenDataTierPreference'; @@ -22,9 +22,7 @@ export type AiOpsKey = keyof Exclude; * @param initValue */ export function useStorage(key: AiOpsKey, initValue?: T): [T, (value: T) => void] { - const { - services: { storage }, - } = useAiOpsKibana(); + const { storage } = useAiopsAppContext(); const [val, setVal] = useState(storage.get(key) ?? initValue); diff --git a/x-pack/plugins/aiops/public/hooks/use_time_filter.ts b/x-pack/plugins/aiops/public/hooks/use_time_filter.ts index 1dcdf3a2539b6..4f2a33966f71e 100644 --- a/x-pack/plugins/aiops/public/hooks/use_time_filter.ts +++ b/x-pack/plugins/aiops/public/hooks/use_time_filter.ts @@ -6,7 +6,7 @@ */ import { useEffect } from 'react'; -import { useAiOpsKibana } from '../kibana_context'; +import { useAiopsAppContext } from './use_aiops_app_context'; interface UseTimefilterOptions { timeRangeSelector?: boolean; @@ -17,8 +17,13 @@ export const useTimefilter = ({ timeRangeSelector, autoRefreshSelector, }: UseTimefilterOptions = {}) => { - const { services } = useAiOpsKibana(); - const { timefilter } = services.data.query.timefilter; + const { + data: { + query: { + timefilter: { timefilter }, + }, + }, + } = useAiopsAppContext(); useEffect(() => { if (timeRangeSelector === true && !timefilter.isTimeRangeSelectorEnabled()) { diff --git a/x-pack/plugins/aiops/public/hooks/url_state.ts b/x-pack/plugins/aiops/public/hooks/use_url_state.ts similarity index 99% rename from x-pack/plugins/aiops/public/hooks/url_state.ts rename to x-pack/plugins/aiops/public/hooks/use_url_state.ts index 97699f3a8f2eb..21d58c5766c52 100644 --- a/x-pack/plugins/aiops/public/hooks/url_state.ts +++ b/x-pack/plugins/aiops/public/hooks/use_url_state.ts @@ -13,7 +13,7 @@ export interface Dictionary { [id: string]: TValue; } -// duplicate of ml/object_utils +// TODO duplicate of ml/object_utils export const getNestedProperty = ( obj: Record, accessor: string, diff --git a/x-pack/plugins/aiops/public/index.ts b/x-pack/plugins/aiops/public/index.ts index 5810d38d57308..1f8373839493e 100755 --- a/x-pack/plugins/aiops/public/index.ts +++ b/x-pack/plugins/aiops/public/index.ts @@ -14,4 +14,3 @@ export function plugin() { } export { ExplainLogRateSpikes } from './shared_lazy_components'; -export type { AiopsPluginSetup, AiopsPluginStart } from './types'; diff --git a/x-pack/plugins/aiops/public/kibana_context.ts b/x-pack/plugins/aiops/public/kibana_context.ts deleted file mode 100644 index feb0c4ce16c62..0000000000000 --- a/x-pack/plugins/aiops/public/kibana_context.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 { CoreStart } from '@kbn/core/public'; -import { KibanaReactContextValue, useKibana } from '@kbn/kibana-react-plugin/public'; -import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import type { AiOpsStartDependencies } from './plugin'; - -export type StartServices = CoreStart & - AiOpsStartDependencies & { - storage: IStorageWrapper; - }; -export type AiOpsKibanaReactContextValue = KibanaReactContextValue; -export const useAiOpsKibana = () => useKibana(); diff --git a/x-pack/plugins/aiops/public/kibana_services.ts b/x-pack/plugins/aiops/public/kibana_services.ts deleted file mode 100644 index 2219dfa4677a9..0000000000000 --- a/x-pack/plugins/aiops/public/kibana_services.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 { CoreStart } from '@kbn/core/public'; -import { AiOpsStartDependencies } from './plugin'; - -let coreStart: CoreStart; -let pluginsStart: AiOpsStartDependencies; -export function setStartServices(core: CoreStart, plugins: AiOpsStartDependencies) { - coreStart = core; - pluginsStart = plugins; -} - -export const getCoreStart = () => coreStart; -export const getPluginsStart = () => pluginsStart; diff --git a/x-pack/plugins/aiops/public/plugin.ts b/x-pack/plugins/aiops/public/plugin.ts index c6126ec2db7a9..0232473a40f11 100755 --- a/x-pack/plugins/aiops/public/plugin.ts +++ b/x-pack/plugins/aiops/public/plugin.ts @@ -5,28 +5,10 @@ * 2.0. */ -import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; +import { Plugin } from '@kbn/core/public'; -import { AiopsPluginSetup, AiopsPluginStart } from './types'; -import { setStartServices } from './kibana_services'; - -export interface AiOpsStartDependencies { - data: DataPublicPluginStart; - charts: ChartsPluginStart; - fieldFormats: FieldFormatsStart; - unifiedSearch: UnifiedSearchPublicPluginStart; - share: SharePluginStart; -} - -export class AiopsPlugin implements Plugin { - public setup(core: CoreSetup) {} - public start(core: CoreStart, plugins: AiOpsStartDependencies) { - setStartServices(core, plugins); - } +export class AiopsPlugin implements Plugin { + public setup() {} + public start() {} public stop() {} } diff --git a/x-pack/plugins/aiops/public/types.ts b/x-pack/plugins/aiops/public/types.ts deleted file mode 100755 index 453142258be2d..0000000000000 --- a/x-pack/plugins/aiops/public/types.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 type { AiopsPlugin } from './plugin'; - -/** - * aiops plugin public setup contract - */ -export type AiopsPluginSetup = ReturnType; - -/** - * aiops plugin public start contract - */ -export type AiopsPluginStart = ReturnType; - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type AppPluginStartDependencies = {}; diff --git a/x-pack/plugins/alerting/common/execution_log_types.ts b/x-pack/plugins/alerting/common/execution_log_types.ts index 74f2d7a000e27..41406bb6c9dc1 100644 --- a/x-pack/plugins/alerting/common/execution_log_types.ts +++ b/x-pack/plugins/alerting/common/execution_log_types.ts @@ -48,6 +48,7 @@ export interface IExecutionLog { es_search_duration_ms: number; schedule_delay_ms: number; timed_out: boolean; + rule_id: string; } export interface IExecutionErrors { diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index c9f127a63d8c5..bd3fc05c6d8f7 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { fromKueryExpression } from '@kbn/es-query'; import { getNumExecutions, getExecutionLogAggregation, @@ -271,7 +272,421 @@ describe('getExecutionLogAggregation', () => { top_hits: { size: 1, _source: { - includes: ['event.outcome', 'message', 'error.message', 'kibana.version'], + includes: [ + 'event.outcome', + 'message', + 'error.message', + 'kibana.version', + 'rule.id', + ], + }, + }, + }, + }, + }, + timeoutMessage: { + filter: { + bool: { + must: [ + { match: { 'event.action': 'execute-timeout' } }, + { match: { 'event.provider': 'alerting' } }, + ], + }, + }, + }, + }, + }, + }, + }, + }); + }); + + test('should correctly generate aggregation with a defined filter in the form of a string', () => { + expect( + getExecutionLogAggregation({ + page: 2, + perPage: 10, + sort: [{ timestamp: { order: 'asc' } }, { execution_duration: { order: 'desc' } }], + filter: 'test:test', + }) + ).toEqual({ + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuidCardinality: { + aggs: { + executionUuidCardinality: { + cardinality: { field: 'kibana.alert.rule.execution.uuid' }, + }, + }, + filter: { + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + }, + executionUuid: { + terms: { + field: 'kibana.alert.rule.execution.uuid', + size: 1000, + order: [ + { 'ruleExecution>executeStartTime': 'asc' }, + { 'ruleExecution>executionDuration': 'desc' }, + ], + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + sort: [ + { 'ruleExecution>executeStartTime': { order: 'asc' } }, + { 'ruleExecution>executionDuration': { order: 'desc' } }, + ], + from: 10, + size: 10, + gap_policy: 'insert_zeros', + }, + }, + actionExecution: { + filter: { + bool: { + must: [ + { match: { 'event.action': 'execute' } }, + { match: { 'event.provider': 'actions' } }, + ], + }, + }, + aggs: { actionOutcomes: { terms: { field: 'event.outcome', size: 2 } } }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + ruleExecution: { + filter: { + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + bool: { + must: [ + { match: { 'event.action': 'execute' } }, + { match: { 'event.provider': 'alerting' } }, + ], + }, + }, + ], + }, + }, + aggs: { + executeStartTime: { min: { field: 'event.start' } }, + scheduleDelay: { + max: { + field: 'kibana.task.schedule_delay', + }, + }, + totalSearchDuration: { + max: { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms' }, + }, + esSearchDuration: { + max: { field: 'kibana.alert.rule.execution.metrics.es_search_duration_ms' }, + }, + numTriggeredActions: { + max: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + }, + }, + numGeneratedActions: { + max: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + }, + }, + numActiveAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + }, + }, + numRecoveredAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + }, + }, + numNewAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + }, + }, + executionDuration: { max: { field: 'event.duration' } }, + outcomeAndMessage: { + top_hits: { + size: 1, + _source: { + includes: [ + 'event.outcome', + 'message', + 'error.message', + 'kibana.version', + 'rule.id', + ], + }, + }, + }, + }, + }, + timeoutMessage: { + filter: { + bool: { + must: [ + { match: { 'event.action': 'execute-timeout' } }, + { match: { 'event.provider': 'alerting' } }, + ], + }, + }, + }, + }, + }, + }, + }, + }); + }); + + test('should correctly generate aggregation with a defined filter in the form of a KueryNode', () => { + expect( + getExecutionLogAggregation({ + page: 2, + perPage: 10, + sort: [{ timestamp: { order: 'asc' } }, { execution_duration: { order: 'desc' } }], + filter: fromKueryExpression('test:test'), + }) + ).toEqual({ + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuidCardinality: { + aggs: { + executionUuidCardinality: { + cardinality: { field: 'kibana.alert.rule.execution.uuid' }, + }, + }, + filter: { + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + }, + executionUuid: { + terms: { + field: 'kibana.alert.rule.execution.uuid', + size: 1000, + order: [ + { 'ruleExecution>executeStartTime': 'asc' }, + { 'ruleExecution>executionDuration': 'desc' }, + ], + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + sort: [ + { 'ruleExecution>executeStartTime': { order: 'asc' } }, + { 'ruleExecution>executionDuration': { order: 'desc' } }, + ], + from: 10, + size: 10, + gap_policy: 'insert_zeros', + }, + }, + actionExecution: { + filter: { + bool: { + must: [ + { match: { 'event.action': 'execute' } }, + { match: { 'event.provider': 'actions' } }, + ], + }, + }, + aggs: { actionOutcomes: { terms: { field: 'event.outcome', size: 2 } } }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + ruleExecution: { + filter: { + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + bool: { + must: [ + { match: { 'event.action': 'execute' } }, + { match: { 'event.provider': 'alerting' } }, + ], + }, + }, + ], + }, + }, + aggs: { + executeStartTime: { min: { field: 'event.start' } }, + scheduleDelay: { + max: { + field: 'kibana.task.schedule_delay', + }, + }, + totalSearchDuration: { + max: { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms' }, + }, + esSearchDuration: { + max: { field: 'kibana.alert.rule.execution.metrics.es_search_duration_ms' }, + }, + numTriggeredActions: { + max: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + }, + }, + numGeneratedActions: { + max: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + }, + }, + numActiveAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + }, + }, + numRecoveredAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + }, + }, + numNewAlerts: { + max: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + }, + }, + executionDuration: { max: { field: 'event.duration' } }, + outcomeAndMessage: { + top_hits: { + size: 1, + _source: { + includes: [ + 'event.outcome', + 'message', + 'error.message', + 'kibana.version', + 'rule.id', + ], }, }, }, @@ -361,6 +776,7 @@ describe('formatExecutionLogResult', () => { _id: 'S4wIZX8B8TGQpG7XQZns', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -444,6 +860,8 @@ describe('formatExecutionLogResult', () => { _id: 'a4wIZX8B8TGQpG7Xwpnz', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, + event: { outcome: 'success', }, @@ -521,6 +939,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3074, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -541,6 +960,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }); @@ -595,6 +1015,7 @@ describe('formatExecutionLogResult', () => { _id: 'S4wIZX8B8TGQpG7XQZns', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'failure', }, @@ -681,6 +1102,7 @@ describe('formatExecutionLogResult', () => { _id: 'a4wIZX8B8TGQpG7Xwpnz', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -758,6 +1180,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3074, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -778,6 +1201,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }); @@ -832,6 +1256,7 @@ describe('formatExecutionLogResult', () => { _id: 'dJkWa38B1ylB1EvsAckB', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -910,6 +1335,7 @@ describe('formatExecutionLogResult', () => { _id: 'a4wIZX8B8TGQpG7Xwpnz', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -987,6 +1413,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: true, schedule_delay_ms: 3074, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1007,6 +1434,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }); @@ -1061,6 +1489,7 @@ describe('formatExecutionLogResult', () => { _id: '7xKcb38BcntAq5ycFwiu', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -1144,6 +1573,7 @@ describe('formatExecutionLogResult', () => { _id: 'zRKbb38BcntAq5ycOwgk', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -1221,6 +1651,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '61bb867b-661a-471f-bf92-23471afa10b3', @@ -1241,6 +1672,7 @@ describe('formatExecutionLogResult', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3133, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index 7365c9d125d00..14d67807a86af 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { KueryNode } from '@kbn/core-saved-objects-api-server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import Boom from '@hapi/boom'; import { flatMap, get } from 'lodash'; @@ -15,6 +16,7 @@ import { IExecutionLog, IExecutionLogResult } from '../../common'; const DEFAULT_MAX_BUCKETS_LIMIT = 1000; // do not retrieve more than this number of executions +const RULE_ID_FIELD = 'rule.id'; const PROVIDER_FIELD = 'event.provider'; const START_FIELD = 'event.start'; const ACTION_FIELD = 'event.action'; @@ -80,7 +82,7 @@ interface ExcludeExecuteStartAggResult extends estypes.AggregationsAggregateBase }; } export interface IExecutionLogAggOptions { - filter?: string; + filter?: string | KueryNode; page: number; perPage: number; sort: estypes.Sort; @@ -129,7 +131,8 @@ export function getExecutionLogAggregation({ let dslFilterQuery: estypes.QueryDslBoolQuery['filter']; try { - dslFilterQuery = filter ? toElasticsearchQuery(fromKueryExpression(filter)) : undefined; + const filterKueryNode = typeof filter === 'string' ? fromKueryExpression(filter) : filter; + dslFilterQuery = filter ? toElasticsearchQuery(filterKueryNode) : undefined; } catch (err) { throw Boom.badRequest(`Invalid kuery syntax for filter ${filter}`); } @@ -256,7 +259,13 @@ export function getExecutionLogAggregation({ top_hits: { size: 1, _source: { - includes: [OUTCOME_FIELD, MESSAGE_FIELD, ERROR_MESSAGE_FIELD, VERSION_FIELD], + includes: [ + OUTCOME_FIELD, + MESSAGE_FIELD, + ERROR_MESSAGE_FIELD, + VERSION_FIELD, + RULE_ID_FIELD, + ], }, }, }, @@ -325,6 +334,8 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio ? `${outcomeAndMessage?.message ?? ''} - ${outcomeAndMessage?.error?.message ?? ''}` : outcomeAndMessage?.message ?? ''; const version = outcomeAndMessage ? outcomeAndMessage?.kibana?.version ?? '' : ''; + + const ruleId = outcomeAndMessage ? outcomeAndMessage?.rule?.id ?? '' : ''; return { id: bucket?.key ?? '', timestamp: bucket?.ruleExecution?.executeStartTime.value_as_string ?? '', @@ -343,6 +354,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio es_search_duration_ms: bucket?.ruleExecution?.esSearchDuration?.value ?? 0, schedule_delay_ms: scheduleDelayUs / Millis2Nanos, timed_out: timedOut, + rule_id: ruleId, }; } diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts new file mode 100644 index 0000000000000..4c7f6c0a2750e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { rulesClientMock } from '../rules_client.mock'; +import { IExecutionLogResult } from '../../common'; + +const rulesClient = rulesClientMock.create(); +jest.mock('../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getRuleExecutionLogRoute', () => { + const dateString = new Date().toISOString(); + const mockedExecutionLog: IExecutionLogResult = { + total: 374, + data: [ + { + id: '6705da7d-2635-499d-a6a8-1aee1ae1eac9', + timestamp: '2022-03-07T15:38:32.617Z', + duration_ms: 1056, + status: 'success', + message: + "rule executed: example.always-firing:a348a740-9e2c-11ec-bd64-774ed95c43ef: 'test rule'", + version: '8.2.0', + num_active_alerts: 5, + num_new_alerts: 5, + num_recovered_alerts: 0, + num_triggered_actions: 5, + num_generated_actions: 5, + num_succeeded_actions: 5, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + timed_out: false, + schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', + }, + { + id: '41b2755e-765a-4044-9745-b03875d5e79a', + timestamp: '2022-03-07T15:39:05.604Z', + duration_ms: 1165, + status: 'success', + message: + "rule executed: example.always-firing:a348a740-9e2c-11ec-bd64-774ed95c43ef: 'test rule'", + version: '8.2.0', + num_active_alerts: 5, + num_new_alerts: 5, + num_recovered_alerts: 5, + num_triggered_actions: 5, + num_generated_actions: 5, + num_succeeded_actions: 5, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + timed_out: false, + schedule_delay_ms: 3008, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', + }, + ], + }; + + it('gets global execution logs', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getGlobalExecutionLogRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/_global_execution_logs"`); + + rulesClient.getGlobalExecutionLogWithAuth.mockResolvedValue(mockedExecutionLog); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + date_start: dateString, + per_page: 10, + page: 1, + sort: [{ timestamp: { order: 'desc' } }], + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(rulesClient.getGlobalExecutionLogWithAuth).toHaveBeenCalledTimes(1); + expect(rulesClient.getGlobalExecutionLogWithAuth.mock.calls[0]).toEqual([ + { + dateStart: dateString, + page: 1, + perPage: 10, + sort: [{ timestamp: { order: 'desc' } }], + }, + ]); + + expect(res.ok).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.ts new file mode 100644 index 0000000000000..4695e5e7bdf89 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.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 { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { GetGlobalExecutionLogParams } from '../rules_client'; +import { RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; + +const sortOrderSchema = schema.oneOf([schema.literal('asc'), schema.literal('desc')]); + +const sortFieldSchema = schema.oneOf([ + schema.object({ timestamp: schema.object({ order: sortOrderSchema }) }), + schema.object({ execution_duration: schema.object({ order: sortOrderSchema }) }), + schema.object({ total_search_duration: schema.object({ order: sortOrderSchema }) }), + schema.object({ es_search_duration: schema.object({ order: sortOrderSchema }) }), + schema.object({ schedule_delay: schema.object({ order: sortOrderSchema }) }), + schema.object({ num_triggered_actions: schema.object({ order: sortOrderSchema }) }), + schema.object({ num_generated_actions: schema.object({ order: sortOrderSchema }) }), + schema.object({ num_active_alerts: schema.object({ order: sortOrderSchema }) }), + schema.object({ num_recovered_alerts: schema.object({ order: sortOrderSchema }) }), + schema.object({ num_new_alerts: schema.object({ order: sortOrderSchema }) }), +]); + +const sortFieldsSchema = schema.arrayOf(sortFieldSchema, { + defaultValue: [{ timestamp: { order: 'desc' } }], +}); + +const querySchema = schema.object({ + date_start: schema.string(), + date_end: schema.maybe(schema.string()), + filter: schema.maybe(schema.string()), + per_page: schema.number({ defaultValue: 10, min: 1 }), + page: schema.number({ defaultValue: 1, min: 1 }), + sort: sortFieldsSchema, +}); + +const rewriteReq: RewriteRequestCase = ({ + date_start: dateStart, + date_end: dateEnd, + per_page: perPage, + ...rest +}) => ({ + ...rest, + dateStart, + dateEnd, + perPage, +}); + +export const getGlobalExecutionLogRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/_global_execution_logs`, + validate: { + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const rulesClient = (await context.alerting).getRulesClient(); + return res.ok({ + body: await rulesClient.getGlobalExecutionLogWithAuth(rewriteReq(req.query)), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts index 5271cd7373697..9c1be8628c823 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts @@ -46,6 +46,7 @@ describe('getRuleExecutionLogRoute', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -66,6 +67,7 @@ describe('getRuleExecutionLogRoute', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3008, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index ef837a83ad9a4..7d524a190122a 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -21,6 +21,7 @@ import { enableRuleRoute } from './enable_rule'; import { findRulesRoute, findInternalRulesRoute } from './find_rules'; import { getRuleAlertSummaryRoute } from './get_rule_alert_summary'; import { getRuleExecutionLogRoute } from './get_rule_execution_log'; +import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; import { getActionErrorLogRoute } from './get_action_error_log'; import { getRuleStateRoute } from './get_rule_state'; import { healthRoute } from './health'; @@ -59,6 +60,7 @@ export function defineRoutes(opts: RouteOptions) { findInternalRulesRoute(router, licenseState, usageCounter); getRuleAlertSummaryRoute(router, licenseState); getRuleExecutionLogRoute(router, licenseState); + getGlobalExecutionLogRoute(router, licenseState); getActionErrorLogRoute(router, licenseState); getRuleStateRoute(router, licenseState); healthRoute(router, licenseState, encryptedSavedObjects); diff --git a/x-pack/plugins/alerting/server/rules_client.mock.ts b/x-pack/plugins/alerting/server/rules_client.mock.ts index 394f43ab389e4..9ecbc21ccb085 100644 --- a/x-pack/plugins/alerting/server/rules_client.mock.ts +++ b/x-pack/plugins/alerting/server/rules_client.mock.ts @@ -30,6 +30,7 @@ const createRulesClientMock = () => { listAlertTypes: jest.fn(), getAlertSummary: jest.fn(), getExecutionLogForRule: jest.fn(), + getGlobalExecutionLogWithAuth: jest.fn(), getActionErrorLog: jest.fn(), getSpaceId: jest.fn(), bulkEdit: jest.fn(), diff --git a/x-pack/plugins/alerting/server/rules_client/audit_events.ts b/x-pack/plugins/alerting/server/rules_client/audit_events.ts index 215e16f32f29a..c830330a77ca2 100644 --- a/x-pack/plugins/alerting/server/rules_client/audit_events.ts +++ b/x-pack/plugins/alerting/server/rules_client/audit_events.ts @@ -25,6 +25,7 @@ export enum RuleAuditAction { AGGREGATE = 'rule_aggregate', BULK_EDIT = 'rule_bulk_edit', GET_EXECUTION_LOG = 'rule_get_execution_log', + GET_GLOBAL_EXECUTION_LOG = 'rule_get_global_execution_log', GET_ACTION_ERROR_LOG = 'rule_get_action_error_log', SNOOZE = 'rule_snooze', UNSNOOZE = 'rule_unsnooze', @@ -53,6 +54,11 @@ const eventVerbs: Record = { 'accessing execution log for', 'accessed execution log for', ], + rule_get_global_execution_log: [ + 'access execution log', + 'accessing execution log', + 'accessed execution log', + ], rule_get_action_error_log: [ 'access action error log for', 'accessing action error log for', @@ -79,6 +85,7 @@ const eventTypes: Record = { rule_alert_unmute: 'change', rule_aggregate: 'access', rule_get_execution_log: 'access', + rule_get_global_execution_log: 'access', rule_get_action_error_log: 'access', rule_snooze: 'change', rule_unsnooze: 'change', 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 35462f9cc4e75..f0cf88615047d 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -365,6 +365,15 @@ export interface GetExecutionLogByIdParams { sort: estypes.Sort; } +export interface GetGlobalExecutionLogParams { + dateStart: string; + dateEnd?: string; + filter?: string; + page: number; + perPage: number; + sort: estypes.Sort; +} + export interface GetActionErrorLogByIdParams { id: string; dateStart: string; @@ -875,6 +884,100 @@ export class RulesClient { } } + public async getGlobalExecutionLogWithAuth({ + dateStart, + dateEnd, + filter, + page, + perPage, + sort, + }: GetGlobalExecutionLogParams): Promise { + this.logger.debug(`getGlobalExecutionLogWithAuth(): getting global execution log`); + + let authorizationTuple; + try { + authorizationTuple = await this.authorization.getFindAuthorizationFilter( + AlertingAuthorizationEntity.Alert, + { + type: AlertingAuthorizationFilterType.KQL, + fieldNames: { + ruleTypeId: 'kibana.alert.rule.rule_type_id', + consumer: 'kibana.alert.rule.consumer', + }, + } + ); + } catch (error) { + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_GLOBAL_EXECUTION_LOG, + error, + }) + ); + throw error; + } + + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_GLOBAL_EXECUTION_LOG, + }) + ); + + const dateNow = new Date(); + const parsedDateStart = parseDate(dateStart, 'dateStart', dateNow); + const parsedDateEnd = parseDate(dateEnd, 'dateEnd', dateNow); + + const eventLogClient = await this.getEventLogClient(); + + try { + const aggResult = await eventLogClient.aggregateEventsWithAuthFilter( + 'alert', + authorizationTuple.filter as KueryNode, + { + start: parsedDateStart.toISOString(), + end: parsedDateEnd.toISOString(), + aggs: getExecutionLogAggregation({ + filter, + page, + perPage, + sort, + }), + } + ); + + const formattedResult = formatExecutionLogResult(aggResult); + const ruleIds = [...new Set(formattedResult.data.map((l) => l.rule_id))].filter( + Boolean + ) as string[]; + const ruleNameIdEntries = await Promise.all( + ruleIds.map(async (id) => { + try { + const result = await this.get({ id }); + return [id, result.name]; + } catch (e) { + return [id, id]; + } + }) + ); + const ruleNameIdMap: Record = ruleNameIdEntries.reduce( + (result, [key, val]) => ({ ...result, [key]: val }), + {} + ); + + return { + ...formattedResult, + data: formattedResult.data.map((entry) => ({ + ...entry, + rule_name: ruleNameIdMap[entry.rule_id!], + })), + }; + } catch (err) { + this.logger.debug( + `rulesClient.getGlobalExecutionLogWithAuth(): error searching global event log: ${err.message}` + ); + throw err; + } + } + public async getActionErrorLog({ id, dateStart, 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 5cdf4c7744220..cf525f59e9448 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 @@ -21,6 +21,7 @@ import { RawRule } from '../../types'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; import { getExecutionLogAggregation } from '../../lib/get_execution_log_aggregation'; +import { fromKueryExpression } from '@kbn/es-query'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -142,6 +143,9 @@ const aggregateResults = { _id: 'S4wIZX8B8TGQpG7XQZns', _score: 1.0, _source: { + rule: { + id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', + }, event: { outcome: 'success', }, @@ -171,6 +175,25 @@ const aggregateResults = { value: 1.646667512617e12, value_as_string: '2022-03-07T15:38:32.617Z', }, + ruleId: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'S4wIZX8B8TGQpG7XQZns', + _score: 1.0, + _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, + }, + }, + ], + }, + }, }, actionExecution: { meta: {}, @@ -225,6 +248,7 @@ const aggregateResults = { _id: 'a4wIZX8B8TGQpG7Xwpnz', _score: 1.0, _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, event: { outcome: 'success', }, @@ -254,6 +278,25 @@ const aggregateResults = { value: 1.646667545604e12, value_as_string: '2022-03-07T15:39:05.604Z', }, + ruleId: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'S4wIZX8B8TGQpG7XQZns', + _score: 1.0, + _source: { + rule: { id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef' }, + }, + }, + ], + }, + }, }, actionExecution: { meta: {}, @@ -333,6 +376,7 @@ describe('getExecutionLogForRule()', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -353,6 +397,7 @@ describe('getExecutionLogForRule()', () => { es_search_duration_ms: 0, timed_out: false, schedule_delay_ms: 3345, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', }, ], }); @@ -623,3 +668,72 @@ describe('getExecutionLogForRule()', () => { }); }); }); + +describe('getGlobalExecutionLogWithAuth()', () => { + let rulesClient: RulesClient; + + beforeEach(() => { + rulesClient = new RulesClient(rulesClientParams); + }); + + test('runs as expected with some event log aggregation data', async () => { + const ruleSO = getRuleSavedObject({}); + authorization.getFindAuthorizationFilter.mockResolvedValue({ + filter: fromKueryExpression('*'), + ensureRuleTypeIsAuthorized() {}, + }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(ruleSO); + eventLogClient.aggregateEventsWithAuthFilter.mockResolvedValueOnce(aggregateResults); + + const result = await rulesClient.getGlobalExecutionLogWithAuth(getExecutionLogByIdParams()); + expect(result).toEqual({ + total: 374, + data: [ + { + id: '6705da7d-2635-499d-a6a8-1aee1ae1eac9', + timestamp: '2022-03-07T15:38:32.617Z', + duration_ms: 1056, + status: 'success', + message: + "rule executed: example.always-firing:a348a740-9e2c-11ec-bd64-774ed95c43ef: 'test rule'", + version: '8.2.0', + num_active_alerts: 5, + num_new_alerts: 5, + num_recovered_alerts: 0, + num_triggered_actions: 5, + num_generated_actions: 5, + num_succeeded_actions: 5, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + timed_out: false, + schedule_delay_ms: 3126, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', + rule_name: 'rule-name', + }, + { + id: '41b2755e-765a-4044-9745-b03875d5e79a', + timestamp: '2022-03-07T15:39:05.604Z', + duration_ms: 1165, + status: 'success', + message: + "rule executed: example.always-firing:a348a740-9e2c-11ec-bd64-774ed95c43ef: 'test rule'", + version: '8.2.0', + num_active_alerts: 5, + num_new_alerts: 5, + num_recovered_alerts: 5, + num_triggered_actions: 5, + num_generated_actions: 5, + num_succeeded_actions: 5, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + timed_out: false, + schedule_delay_ms: 3345, + rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', + rule_name: 'rule-name', + }, + ], + }); + }); +}); 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 a90c274ce2e86..4ce85f54c3dc5 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 @@ -6,7 +6,6 @@ */ import sinon from 'sinon'; -import { schema } from '@kbn/config-schema'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; import { RuleExecutorOptions, @@ -1447,105 +1446,6 @@ describe('Task Runner', () => { expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); - test('validates params before running the rule type', async () => { - const taskRunner = new TaskRunner( - { - ...ruleType, - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - }, - { - ...mockedTaskInstance, - params: { - ...mockedTaskInstance.params, - spaceId: 'foo', - }, - }, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); - const runnerResult = await taskRunner.run(); - expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const loggerCall = logger.error.mock.calls[0][0]; - const loggerMeta = logger.error.mock.calls[0][1]; - const loggerCallPrefix = (loggerCall as string).split('-'); - expect(loggerCallPrefix[0].trim()).toMatchInlineSnapshot( - `"Executing Rule foo:test:1 has resulted in Error: params invalid: [param1]: expected value of type [string] but got [undefined]"` - ); - expect(loggerMeta?.tags).toEqual(['test', '1', 'rule-run-failed']); - expect(loggerMeta?.error?.stack_trace).toBeDefined(); - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - - test('uses API key when provided', async () => { - const taskRunner = new TaskRunner( - ruleType, - mockedTaskInstance, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); - - await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.getRulesClientWithRequest).toHaveBeenCalledWith( - expect.objectContaining({ - headers: { - // base64 encoded "123:abc" - authorization: 'ApiKey MTIzOmFiYw==', - }, - }) - ); - const [request] = taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mock.calls[0]; - - expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( - request, - '/' - ); - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - - test(`doesn't use API key when not provided`, async () => { - const taskRunner = new TaskRunner( - ruleType, - mockedTaskInstance, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - ...SAVED_OBJECT, - attributes: { enabled: true }, - }); - - await taskRunner.run(); - - expect(taskRunnerFactoryInitializerParams.getRulesClientWithRequest).toHaveBeenCalledWith( - expect.objectContaining({ - headers: {}, - }) - ); - - const [request] = taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mock.calls[0]; - - expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( - request, - '/' - ); - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - test('rescheduled the rule if the schedule has update during a task run', async () => { const taskRunner = new TaskRunner( ruleType, @@ -1618,95 +1518,8 @@ describe('Task Runner', () => { expect(logger.error).toBeCalledTimes(1); }); - test('recovers gracefully when the Alert Task Runner throws an exception when fetching the encrypted attributes', async () => { - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockImplementation(() => { - throw new Error(GENERIC_ERROR_MESSAGE); - }); - - const taskRunner = new TaskRunner( - ruleType, - mockedTaskInstance, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - - const runnerResult = await taskRunner.run(); - - expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - - testAlertingEventLogCalls({ - setRuleName: false, - status: 'error', - errorReason: 'decrypt', - executionStatus: 'not-reached', - }); - - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - - test('recovers gracefully when the Alert Task Runner throws an exception when license is higher than supported', async () => { - ruleTypeRegistry.ensureRuleTypeEnabled.mockImplementation(() => { - throw new Error(GENERIC_ERROR_MESSAGE); - }); - - const taskRunner = new TaskRunner( - ruleType, - mockedTaskInstance, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); - - const runnerResult = await taskRunner.run(); - - expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - - testAlertingEventLogCalls({ - status: 'error', - errorReason: 'license', - executionStatus: 'not-reached', - }); - - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - - test('recovers gracefully when the Alert Task Runner throws an exception when getting internal Services', async () => { - taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockImplementation(() => { - throw new Error(GENERIC_ERROR_MESSAGE); - }); - - const taskRunner = new TaskRunner( - ruleType, - mockedTaskInstance, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); - - const runnerResult = await taskRunner.run(); - - expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - - testAlertingEventLogCalls({ - setRuleName: false, - status: 'error', - errorReason: 'unknown', - executionStatus: 'not-reached', - }); - - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); - - test('recovers gracefully when the Alert Task Runner throws an exception when fetching attributes', async () => { + test('recovers gracefully when the Alert Task Runner throws an exception when loading rule to prepare for run', async () => { + // used in loadRule() which is called in prepareToRun() rulesClient.get.mockImplementation(() => { throw new Error(GENERIC_ERROR_MESSAGE); }); @@ -2381,42 +2194,6 @@ describe('Task Runner', () => { expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); - test('successfully bails on execution if the rule is disabled', async () => { - const state = { - ...mockedTaskInstance.state, - previousStartedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(), - }; - const taskRunner = new TaskRunner( - ruleType, - { - ...mockedTaskInstance, - state, - }, - taskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - ...SAVED_OBJECT, - attributes: { ...SAVED_OBJECT.attributes, enabled: false }, - }); - const runnerResult = await taskRunner.run(); - expect(runnerResult.state.previousStartedAt?.toISOString()).toBe(state.previousStartedAt); - expect(runnerResult.schedule).toStrictEqual(mockedTaskInstance.schedule); - - 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(); - }); - test('successfully stores successful runs', async () => { const taskRunner = new TaskRunner( ruleType, diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts index ee2aade9ec1df..5172a5f167fc9 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts @@ -55,4 +55,14 @@ describe('Transaction details', () => { .click(); cy.url().should('include', 'opbeans-java/errors'); }); + + describe('when navigating to a trace sample', () => { + it('keeps the same trace sample after reloading the page', () => { + cy.get('[data-test-subj="pagination-button-last"]').click(); + cy.url().then((url) => { + cy.reload(); + cy.url().should('eq', url); + }); + }); + }); }); diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx index 4f0c64b226b09..5b8c044d2276c 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx @@ -6,22 +6,69 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiEmptyPrompt, + EuiLoadingSpinner, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { IndexLifecyclePhaseSelect } from './index_lifecycle_phase_select'; import { ServicesTable } from './services_table'; import { SearchBar } from '../../shared/search_bar'; import { StorageChart } from './storage_chart'; +import { PermissionDenied } from './prompts/permission_denied'; +import { useFetcher, FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { SummaryStats } from './summary_stats'; +import { ApmEnvironmentFilter } from '../../shared/environment_filter'; + +const INITIAL_DATA = { hasPrivileges: false }; export function StorageExplorer() { + const { data: { hasPrivileges } = INITIAL_DATA, status } = useFetcher( + (callApmApi) => { + return callApmApi('GET /internal/apm/storage_explorer/privileges'); + }, + [] + ); + + const loading = status === FETCH_STATUS.LOADING; + + if (loading) { + return ( + } + titleSize="xs" + title={ +

+ {i18n.translate('xpack.apm.storageExplorer.loadingPromptTitle', { + defaultMessage: 'Loading Storage Explorer...', + })} +

+ } + /> + ); + } + + if (!hasPrivileges) { + return ; + } + return ( <> + + + + + diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx index 6a602edc42b56..124579e0562e2 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx @@ -24,13 +24,13 @@ export function IndexLifecyclePhaseSelect() { { value: IndexLifecyclePhaseSelectOption.All, label: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.all.label', + 'xpack.apm.storageExplorer.indexLifecyclePhase.all.label', { defaultMessage: 'All', } ), description: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.all.description', + 'xpack.apm.storageExplorer.indexLifecyclePhase.all.description', { defaultMessage: 'Search data in all lifecycle phases.', } @@ -39,13 +39,13 @@ export function IndexLifecyclePhaseSelect() { { value: IndexLifecyclePhaseSelectOption.Hot, label: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.hot.label', + 'xpack.apm.storageExplorer.indexLifecyclePhase.hot.label', { defaultMessage: 'Hot', } ), description: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.hot.description', + 'xpack.apm.storageExplorer.indexLifecyclePhase.hot.description', { defaultMessage: 'Holds your most-recent, most-frequently-searched data.', @@ -55,13 +55,13 @@ export function IndexLifecyclePhaseSelect() { { value: IndexLifecyclePhaseSelectOption.Warm, label: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.warm.label', + 'xpack.apm.storageExplorer.indexLifecyclePhase.warm.label', { defaultMessage: 'Warm', } ), description: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.warm.description', + 'xpack.apm.storageExplorer.indexLifecyclePhase.warm.description', { defaultMessage: 'Holds data from recent weeks. Updates are still allowed, but likely infrequent.', @@ -71,13 +71,13 @@ export function IndexLifecyclePhaseSelect() { { value: IndexLifecyclePhaseSelectOption.Cold, label: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.cold.label', + 'xpack.apm.storageExplorer.indexLifecyclePhase.cold.label', { defaultMessage: 'Cold', } ), description: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.cold.description', + 'xpack.apm.storageExplorer.indexLifecyclePhase.cold.description', { defaultMessage: 'While still searchable, this tier is typically optimized for lower storage costs rather than search speed.', @@ -87,13 +87,13 @@ export function IndexLifecyclePhaseSelect() { { value: IndexLifecyclePhaseSelectOption.Frozen, label: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.frozen.label', + 'xpack.apm.storageExplorer.indexLifecyclePhase.frozen.label', { defaultMessage: 'Frozen', } ), description: i18n.translate( - 'xpack.apm.settings.storageExplorer.indexLifecyclePhase.frozen.description', + 'xpack.apm.storageExplorer.indexLifecyclePhase.frozen.description', { defaultMessage: 'Holds data that are no longer being queried, or being queried rarely.', @@ -116,7 +116,7 @@ export function IndexLifecyclePhaseSelect() { return ( + {i18n.translate( + 'xpack.apm.storageExplorer.noPermissionToViewIndicesStatsTitle', + { + defaultMessage: 'You need permission to view index statistics', + } + )} + + } + body={ +

+ {i18n.translate( + 'xpack.apm.storageExplorer.noPermissionToViewIndicesStatsDescription', + { + defaultMessage: 'Contact your system administrator', + } + )} +

+ } + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx index 950996540d2f9..7f2dd2d8a096f 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/index.tsx @@ -106,7 +106,7 @@ export function ServicesTable() { { field: 'serviceName', name: i18n.translate( - 'xpack.apm.settings.storageExplorer.table.serviceColumnName', + 'xpack.apm.storageExplorer.table.serviceColumnName', { defaultMessage: 'Service', } @@ -140,7 +140,7 @@ export function ServicesTable() { { field: 'environment', name: i18n.translate( - 'xpack.apm.settings.storageExplorer.table.environmentColumnName', + 'xpack.apm.storageExplorer.table.environmentColumnName', { defaultMessage: 'Environment', } @@ -156,7 +156,7 @@ export function ServicesTable() { name: ( <> {i18n.translate( - 'xpack.apm.settings.storageExplorer.table.samplingColumnName', + 'xpack.apm.storageExplorer.table.samplingColumnName', { defaultMessage: 'Sample rate', } @@ -194,12 +194,9 @@ export function ServicesTable() { name: ( - {i18n.translate( - 'xpack.apm.settings.storageExplorer.table.expandRow', - { - defaultMessage: 'Expand row', - } - )} + {i18n.translate('xpack.apm.storageExplorer.table.expandRow', { + defaultMessage: 'Expand row', + })} ), @@ -210,18 +207,12 @@ export function ServicesTable() { onClick={() => toggleRowDetails(serviceName)} aria-label={ itemIdToExpandedRowMap[serviceName] - ? i18n.translate( - 'xpack.apm.settings.storageExplorer.table.collapse', - { - defaultMessage: 'Collapse', - } - ) - : i18n.translate( - 'xpack.apm.settings.storageExplorer.table.expand', - { - defaultMessage: 'Expand', - } - ) + ? i18n.translate('xpack.apm.storageExplorer.table.collapse', { + defaultMessage: 'Collapse', + }) + : i18n.translate('xpack.apm.storageExplorer.table.expand', { + defaultMessage: 'Expand', + }) } iconType={ itemIdToExpandedRowMap[serviceName] ? 'arrowUp' : 'arrowDown' @@ -234,12 +225,9 @@ export function ServicesTable() { return ( ); diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx index 98a796df005e7..a8b562fa4b9fa 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/size_label.tsx @@ -13,14 +13,14 @@ export function SizeLabel() { return ( <> - {i18n.translate('xpack.apm.settings.storageExplorer.sizeLabel.title', { + {i18n.translate('xpack.apm.storageExplorer.sizeLabel.title', { defaultMessage: 'Size', })}{' '}

{i18n.translate( - 'xpack.apm.settings.storageExplorer.serviceDetails.title', + 'xpack.apm.storageExplorer.serviceDetails.title', { defaultMessage: 'Service storage details', } @@ -173,7 +173,7 @@ export function StorageDetailsPerService({ {i18n.translate( - 'xpack.apm.settings.storageExplorer.serviceDetails.serviceOverviewLink', + 'xpack.apm.storageExplorer.serviceDetails.serviceOverviewLink', { defaultMessage: 'Go to service overview', } diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx new file mode 100644 index 0000000000000..c5ffb336ca549 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiText, + useEuiFontSize, + EuiLink, + EuiLoadingSpinner, +} from '@elastic/eui'; +import { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; +import { useTimeRange } from '../../../hooks/use_time_range'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { asDynamicBytes } from '../../../../common/utils/formatters'; +import { useApmRouter } from '../../../hooks/use_apm_router'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { asTransactionRate } from '../../../../common/utils/formatters'; + +const INITIAL_DATA = { + estimatedSize: 0, + dailyDataGeneration: 0, + tracesPerMinute: 0, + numberOfServices: 0, +}; + +export function SummaryStats() { + const router = useApmRouter(); + const { core } = useApmPluginContext(); + const { euiTheme } = useEuiTheme(); + + const { + query: { + rangeFrom, + rangeTo, + environment, + kuery, + indexLifecyclePhase, + comparisonEnabled, + }, + } = useApmParams('/storage-explorer'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const serviceInventoryLink = router.link('/services', { + query: { + rangeFrom, + rangeTo, + environment, + comparisonEnabled, + kuery, + serviceGroup: '', + }, + }); + + const { data = INITIAL_DATA, status } = useProgressiveFetcher( + (callApmApi) => { + return callApmApi('GET /internal/apm/storage_explorer_summary_stats', { + params: { + query: { + indexLifecyclePhase, + environment, + kuery, + start, + end, + }, + }, + }); + }, + [indexLifecyclePhase, environment, kuery, start, end] + ); + + const loading = status === FETCH_STATUS.LOADING; + + return ( + + {loading && ( + + + + )} + {!loading && ( + + + + + + + + + + + + + + + {i18n.translate( + 'xpack.apm.storageExplorer.summary.serviceInventoryLink', + { + defaultMessage: 'Go to Service Inventory', + } + )} + + + + + {i18n.translate( + 'xpack.apm.storageExplorer.summary.indexManagementLink', + { + defaultMessage: 'Go to Index Management', + } + )} + + + + + + )} + + ); +} + +function SummaryMetric({ + label, + value, + color, +}: { + label: string; + value: string; + color: string; +}) { + const xxlFontSize = useEuiFontSize('xxl', { measurement: 'px' }); + const { euiTheme } = useEuiTheme(); + + return ( + + + {label} + + + {value} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx index c8092689b9d2e..742cd6f9e7a97 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx @@ -22,6 +22,7 @@ import { failedTransactionsCorrelationsTab } from './failed_transactions_correla import { latencyCorrelationsTab } from './latency_correlations_tab'; import { traceSamplesTab } from './trace_samples_tab'; import { useSampleChartSelection } from '../../../hooks/use_sample_chart_selection'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; const tabs = [ traceSamplesTab, @@ -69,7 +70,7 @@ export function TransactionDetailsTabs() { sample.transactionId === transactionId && sample.traceId === traceId ); - if (!selectedSample) { + if (traceSamplesStatus === FETCH_STATUS.SUCCESS && !selectedSample) { // selected sample was not found. select a new one: const preferredSample = maybe(traceSamples[0]); @@ -84,7 +85,7 @@ export function TransactionDetailsTabs() { }), }); } - }, [history, traceSamples, transactionId, traceId]); + }, [history, traceSamples, transactionId, traceId, traceSamplesStatus]); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx index f9642630766bd..61b814a598e87 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx @@ -91,12 +91,14 @@ export function WaterfallWithSummary({ - + {traceSamples.length > 0 && ( + + )} diff --git a/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx index b27cc2cf77bc8..c4bc86f766811 100644 --- a/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx +++ b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import * as t from 'io-ts'; +import { EuiLink } from '@elastic/eui'; import { StorageExplorer } from '../../app/storage_explorer'; import { BetaBadge } from '../../shared/beta_badge'; import { ApmMainTemplate } from '../templates/apm_main_template'; @@ -28,26 +29,42 @@ export const storageExplorer = { href="/storage-explorer" > - - -

- {i18n.translate('xpack.apm.views.storageExplorer.title', { - defaultMessage: 'Storage explorer', - })} -

-
-
- - - -
- } + pageHeader={{ + alignItems: 'center', + pageTitle: ( + + + +

+ {i18n.translate('xpack.apm.views.storageExplorer.title', { + defaultMessage: 'Storage explorer', + })} +

+
+
+ + + +
+ ), + rightSideItems: [ + + {i18n.translate( + 'xpack.apm.views.storageExplorer.giveFeedback', + { + defaultMessage: 'Give feedback', + } + )} + , + ], + }} > diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts index e04735821aaec..f0906eef3ab09 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts @@ -46,9 +46,9 @@ export function useTransactionTraceSamplesFetcher({ status, error, } = useFetcher( - async (callApmApi) => { + (callApmApi) => { if (serviceName && start && end && transactionType && transactionName) { - const response = await callApmApi( + return callApmApi( 'GET /internal/apm/services/{serviceName}/transactions/traces/samples', { params: { @@ -70,8 +70,6 @@ export function useTransactionTraceSamplesFetcher({ }, } ); - - return response; } }, // the samples should not be refetched if the transactionId or traceId changes diff --git a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts index 35d2a063325cf..7c9781e08db31 100644 --- a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts +++ b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts @@ -12,7 +12,7 @@ import { joinByKey } from '../../../../common/utils/join_by_key'; import { Setup } from '../../helpers/setup_request'; import { getStats } from './get_stats'; import { getDestinationMap } from './get_destination_map'; -import { calculateThroughput } from '../../helpers/calculate_throughput'; +import { calculateThroughputWithRange } from '../../helpers/calculate_throughput'; import { withApmSpan } from '../../../utils/with_apm_span'; export function getConnectionStats({ @@ -124,7 +124,7 @@ export function getConnectionStats({ throughput: { value: mergedStats.value.count > 0 - ? calculateThroughput({ + ? calculateThroughputWithRange({ start, end, value: mergedStats.value.count, @@ -134,7 +134,11 @@ export function getConnectionStats({ x: point.x, y: point.count > 0 - ? calculateThroughput({ start, end, value: point.count }) + ? calculateThroughputWithRange({ + start, + end, + value: point.count, + }) : null, })), }, diff --git a/x-pack/plugins/apm/server/lib/helpers/calculate_throughput.ts b/x-pack/plugins/apm/server/lib/helpers/calculate_throughput.ts index 9c99f94f82861..55f25c648b4df 100644 --- a/x-pack/plugins/apm/server/lib/helpers/calculate_throughput.ts +++ b/x-pack/plugins/apm/server/lib/helpers/calculate_throughput.ts @@ -5,22 +5,6 @@ * 2.0. */ -/** - * @deprecated use calculateThroughputWithRange instead - */ -export function calculateThroughput({ - start, - end, - value, -}: { - start: number; - end: number; - value: number; -}) { - const durationAsMinutes = (end - start) / 1000 / 60; - return value / durationAsMinutes; -} - export function calculateThroughputWithRange({ start, end, diff --git a/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts index afaaf37c3362c..cfd7f6102b00c 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/transactions/index.ts @@ -11,6 +11,8 @@ import { SearchAggregatedTransactionSetting } from '../../../../common/aggregate import { TRANSACTION_DURATION, TRANSACTION_DURATION_HISTOGRAM, + TRANSACTION_ROOT, + PARENT_ID, } from '../../../../common/elasticsearch_fieldnames'; import { APMConfig } from '../../..'; import { APMEventClient } from '../create_es_client/create_apm_event_client'; @@ -106,3 +108,19 @@ export function getProcessorEventForTransactions( ? ProcessorEvent.metric : ProcessorEvent.transaction; } + +export function isRootTransaction(searchAggregatedTransactions: boolean) { + return searchAggregatedTransactions + ? { + term: { + [TRANSACTION_ROOT]: true, + }, + } + : { + bool: { + must_not: { + exists: { field: PARENT_ID }, + }, + }, + }; +} diff --git a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts index 8231675baff8e..d99a3f1d22da3 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_instances/get_service_instances_transaction_statistics.ts @@ -21,7 +21,7 @@ import { getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; -import { calculateThroughput } from '../../../lib/helpers/calculate_throughput'; +import { calculateThroughputWithRange } from '../../../lib/helpers/calculate_throughput'; import { getBucketSizeForAggregatedTransactions } from '../../../lib/helpers/get_bucket_size_for_aggregated_transactions'; import { getLatencyAggregation, @@ -202,7 +202,7 @@ export async function getServiceInstancesTransactionStatistics< aggregation: latency, latencyAggregationType, }), - throughput: calculateThroughput({ + throughput: calculateThroughputWithRange({ start: startWithOffset, end: endWithOffset, value: count, diff --git a/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts index 985b5bc3e1dc4..cb068d5fb8442 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_transaction_groups.ts @@ -20,7 +20,7 @@ import { getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../lib/helpers/transactions'; -import { calculateThroughput } from '../../lib/helpers/calculate_throughput'; +import { calculateThroughputWithRange } from '../../lib/helpers/calculate_throughput'; import { getLatencyAggregation, getLatencyValue, @@ -126,7 +126,7 @@ export async function getServiceTransactionGroups({ latencyAggregationType, aggregation: bucket.latency, }), - throughput: calculateThroughput({ + throughput: calculateThroughputWithRange({ start, end, value: bucket.doc_count, diff --git a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts index 4a7c5c85436e1..600d3c71f7e08 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts @@ -22,7 +22,7 @@ import { getDurationFieldForTransactions, getProcessorEventForTransactions, } from '../../../lib/helpers/transactions'; -import { calculateThroughput } from '../../../lib/helpers/calculate_throughput'; +import { calculateThroughputWithRange } from '../../../lib/helpers/calculate_throughput'; import { getBucketSizeForAggregatedTransactions } from '../../../lib/helpers/get_bucket_size_for_aggregated_transactions'; import { Setup } from '../../../lib/helpers/setup_request'; import { @@ -163,7 +163,7 @@ export async function getServiceTransactionDetailedStatistics({ throughput: topTransactionTypeBucket.timeseries.buckets.map( (dateBucket) => ({ x: dateBucket.key + offsetInMs, - y: calculateThroughput({ + y: calculateThroughputWithRange({ start, end, value: dateBucket.doc_count, diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts index 548946288a530..169bff0cd7113 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_service_statistics.ts @@ -56,7 +56,7 @@ async function getMainServiceStatistics({ }) { const { apmEventClient } = setup; - const [allIndicesStats, response] = await Promise.all([ + const [{ indices: allIndicesStats }, response] = await Promise.all([ getTotalIndicesStats({ context, setup }), apmEventClient.search('get_main_service_statistics', { apm: { @@ -69,6 +69,7 @@ async function getMainServiceStatistics({ }, body: { size: 0, + track_total_hits: false, query: { bool: { filter: [ diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_size_timeseries.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_size_timeseries.ts index 2f0934566f62d..f513efe059f71 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/get_size_timeseries.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_size_timeseries.ts @@ -59,7 +59,7 @@ export async function getSizeTimeseries({ searchAggregatedTransactions, }); - const [allIndicesStats, res] = await Promise.all([ + const [{ indices: allIndicesStats }, res] = await Promise.all([ getTotalIndicesStats({ setup, context }), apmEventClient.search('get_storage_timeseries', { apm: { @@ -72,6 +72,7 @@ export async function getSizeTimeseries({ }, body: { size: 0, + track_total_hits: false, query: { bool: { filter: [ @@ -94,7 +95,7 @@ export async function getSizeTimeseries({ services: { terms: { field: SERVICE_NAME, - size: 500, + size: 50, }, aggs: { storageTimeSeries: { diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts index 474158b41736f..95f575dff9600 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_storage_details_per_processor_event.ts @@ -54,7 +54,7 @@ export async function getStorageDetailsPerProcessorEvent({ }) { const { apmEventClient } = setup; - const [allIndicesStats, response] = await Promise.all([ + const [{ indices: allIndicesStats }, response] = await Promise.all([ getTotalIndicesStats({ setup, context }), apmEventClient.search('get_storage_details_per_processor_event', { apm: { @@ -67,6 +67,7 @@ export async function getStorageDetailsPerProcessorEvent({ }, body: { size: 0, + track_total_hits: false, query: { bool: { filter: [ diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_summary_statistics.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_summary_statistics.ts new file mode 100644 index 0000000000000..dfa510e8f0890 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_summary_statistics.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 { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { + getTotalIndicesStats, + getEstimatedSizeForDocumentsInIndex, +} from './indices_stats_helpers'; +import { Setup } from '../../lib/helpers/setup_request'; +import { ApmPluginRequestHandlerContext } from '../typings'; +import { + IndexLifecyclePhaseSelectOption, + indexLifeCyclePhaseToDataTier, +} from '../../../common/storage_explorer_types'; +import { RandomSampler } from '../../lib/helpers/get_random_sampler'; +import { + SERVICE_NAME, + TIER, + INDEX, +} from '../../../common/elasticsearch_fieldnames'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { + getDocumentTypeFilterForTransactions, + getProcessorEventForTransactions, + getDurationFieldForTransactions, + isRootTransaction, +} from '../../lib/helpers/transactions'; +import { calculateThroughputWithRange } from '../../lib/helpers/calculate_throughput'; + +export async function getTracesPerMinute({ + setup, + indexLifecyclePhase, + start, + end, + environment, + kuery, + searchAggregatedTransactions, +}: { + setup: Setup; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + start: number; + end: number; + environment: string; + kuery: string; + searchAggregatedTransactions: boolean; +}) { + const { apmEventClient } = setup; + + const response = await apmEventClient.search('get_traces_per_minute', { + apm: { + events: [getProcessorEventForTransactions(searchAggregatedTransactions)], + }, + body: { + size: 0, + track_total_hits: false, + query: { + bool: { + filter: [ + ...getDocumentTypeFilterForTransactions( + searchAggregatedTransactions + ), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...rangeQuery(start, end), + ...(indexLifecyclePhase !== IndexLifecyclePhaseSelectOption.All + ? termQuery( + TIER, + indexLifeCyclePhaseToDataTier[indexLifecyclePhase] + ) + : []), + isRootTransaction(searchAggregatedTransactions), + ], + }, + }, + aggs: { + traces_count: { + value_count: { + field: getDurationFieldForTransactions( + searchAggregatedTransactions + ), + }, + }, + }, + }, + }); + + return calculateThroughputWithRange({ + start, + end, + value: response?.aggregations?.traces_count.value ?? 0, + }); +} + +export async function getMainSummaryStats({ + setup, + context, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, +}: { + setup: Setup; + context: ApmPluginRequestHandlerContext; + indexLifecyclePhase: IndexLifecyclePhaseSelectOption; + randomSampler: RandomSampler; + start: number; + end: number; + environment: string; + kuery: string; +}) { + const { apmEventClient } = setup; + + const [{ indices: allIndicesStats }, res] = await Promise.all([ + getTotalIndicesStats({ context, setup }), + apmEventClient.search('get_storage_explorer_main_summary_stats', { + apm: { + events: [ + ProcessorEvent.span, + ProcessorEvent.transaction, + ProcessorEvent.error, + ProcessorEvent.metric, + ], + }, + body: { + size: 0, + track_total_hits: false, + query: { + bool: { + filter: [ + ...environmentQuery(environment), + ...kqlQuery(kuery), + ...rangeQuery(start, end), + ...(indexLifecyclePhase !== IndexLifecyclePhaseSelectOption.All + ? termQuery( + TIER, + indexLifeCyclePhaseToDataTier[indexLifecyclePhase] + ) + : []), + ] as QueryDslQueryContainer[], + }, + }, + aggs: { + services_count: { + cardinality: { + field: SERVICE_NAME, + }, + }, + sample: { + random_sampler: randomSampler, + aggs: { + indices: { + terms: { + field: INDEX, + size: 500, + }, + aggs: { + number_of_metric_docs: { + value_count: { + field: INDEX, + }, + }, + }, + }, + }, + }, + }, + }, + }), + ]); + + const estimatedSize = allIndicesStats + ? res.aggregations?.sample.indices.buckets.reduce((prev, curr) => { + return ( + prev + + getEstimatedSizeForDocumentsInIndex({ + allIndicesStats, + indexName: curr.key as string, + numberOfDocs: curr.number_of_metric_docs.value, + }) + ); + }, 0) ?? 0 + : 0; + + const durationAsDays = (end - start) / 1000 / 60 / 60 / 24; + + return { + numberOfServices: res.aggregations?.services_count.value ?? 0, + estimatedSize, + dailyDataGeneration: estimatedSize / durationAsDays, + }; +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts b/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts index d479c54aae2c9..793c69ab71b4c 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/get_total_transactions_per_service.ts @@ -54,6 +54,7 @@ export async function getTotalTransactionsPerService({ }, body: { size: 0, + track_total_hits: false, query: { bool: { filter: [ diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/has_storage_explorer_privileges.ts b/x-pack/plugins/apm/server/routes/storage_explorer/has_storage_explorer_privileges.ts new file mode 100644 index 0000000000000..714c0eee1c62c --- /dev/null +++ b/x-pack/plugins/apm/server/routes/storage_explorer/has_storage_explorer_privileges.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { every } from 'lodash'; +import { uniq } from 'lodash'; +import { ApmPluginRequestHandlerContext } from '../typings'; +import { Setup } from '../../lib/helpers/setup_request'; + +export async function hasStorageExplorerPrivileges({ + context, + setup, +}: { + context: ApmPluginRequestHandlerContext; + setup: Setup; +}) { + const { + indices: { transaction, span, metric, error }, + } = setup; + + const names = uniq( + [transaction, span, metric, error].flatMap((indexPatternString) => + indexPatternString.split(',').map((indexPattern) => indexPattern.trim()) + ) + ); + + const esClient = (await context.core).elasticsearch.client; + const { index } = await esClient.asCurrentUser.security.hasPrivileges({ + body: { + index: [ + { + names, + privileges: ['monitor'], + }, + ], + }, + }); + + const hasPrivileges = every(index, 'monitor'); + return hasPrivileges; +} diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts b/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts index 7abd7df856e30..aa9e854cc4a29 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/indices_stats_helpers.ts @@ -21,10 +21,8 @@ export async function getTotalIndicesStats({ } = setup; const index = uniq([transaction, span, metric, error]).join(); const esClient = (await context.core).elasticsearch.client; - const indicesStats = (await esClient.asCurrentUser.indices.stats({ index })) - .indices; - - return indicesStats; + const totalStats = await esClient.asCurrentUser.indices.stats({ index }); + return totalStats; } export function getEstimatedSizeForDocumentsInIndex({ diff --git a/x-pack/plugins/apm/server/routes/storage_explorer/route.ts b/x-pack/plugins/apm/server/routes/storage_explorer/route.ts index 2441940f10bce..7ee36fae5493c 100644 --- a/x-pack/plugins/apm/server/routes/storage_explorer/route.ts +++ b/x-pack/plugins/apm/server/routes/storage_explorer/route.ts @@ -7,6 +7,8 @@ import * as t from 'io-ts'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import Boom from '@hapi/boom'; +import { i18n } from '@kbn/i18n'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { getSearchAggregatedTransactions } from '../../lib/helpers/transactions'; import { setupRequest } from '../../lib/helpers/setup_request'; @@ -22,6 +24,11 @@ import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; import { getStorageDetailsPerProcessorEvent } from './get_storage_details_per_processor_event'; import { getRandomSampler } from '../../lib/helpers/get_random_sampler'; import { getSizeTimeseries } from './get_size_timeseries'; +import { hasStorageExplorerPrivileges } from './has_storage_explorer_privileges'; +import { + getMainSummaryStats, + getTracesPerMinute, +} from './get_summary_statistics'; const storageExplorerRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/storage_explorer', @@ -226,8 +233,117 @@ const storageChartRoute = createApmServerRoute({ }, }); +const storageExplorerPrivilegesRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/storage_explorer/privileges', + options: { tags: ['access:apm'] }, + + handler: async (resources): Promise<{ hasPrivileges: boolean }> => { + const { + plugins: { security }, + context, + } = resources; + + if (!security) { + throw Boom.internal(SECURITY_REQUIRED_MESSAGE); + } + + const setup = await setupRequest(resources); + const hasPrivileges = await hasStorageExplorerPrivileges({ + context, + setup, + }); + + return { hasPrivileges }; + }, +}); + +const storageExplorerSummaryStatsRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/storage_explorer_summary_stats', + options: { tags: ['access:apm'] }, + params: t.type({ + query: t.intersection([ + indexLifecyclePhaseRt, + probabilityRt, + environmentRt, + kueryRt, + rangeRt, + ]), + }), + handler: async ( + resources + ): Promise<{ + tracesPerMinute: number; + numberOfServices: number; + estimatedSize: number; + dailyDataGeneration: number; + }> => { + const { + params, + context, + request, + plugins: { security }, + } = resources; + + const { + query: { + indexLifecyclePhase, + probability, + environment, + kuery, + start, + end, + }, + } = params; + + const [setup, randomSampler] = await Promise.all([ + setupRequest(resources), + getRandomSampler({ security, request, probability }), + ]); + + const searchAggregatedTransactions = await getSearchAggregatedTransactions({ + apmEventClient: setup.apmEventClient, + config: setup.config, + kuery, + }); + + const [mainSummaryStats, tracesPerMinute] = await Promise.all([ + getMainSummaryStats({ + setup, + context, + indexLifecyclePhase, + randomSampler, + start, + end, + environment, + kuery, + }), + getTracesPerMinute({ + setup, + indexLifecyclePhase, + start, + end, + environment, + kuery, + searchAggregatedTransactions, + }), + ]); + + return { + ...mainSummaryStats, + tracesPerMinute, + }; + }, +}); + export const storageExplorerRouteRepository = { ...storageExplorerRoute, ...storageExplorerServiceDetailsRoute, ...storageChartRoute, + ...storageExplorerPrivilegesRoute, + ...storageExplorerSummaryStatsRoute, }; + +const SECURITY_REQUIRED_MESSAGE = i18n.translate( + 'xpack.apm.api.storageExplorer.securityRequired', + { defaultMessage: 'Security plugin is required' } +); diff --git a/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts b/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts index 17925a25a36c5..344cb8ff824c8 100644 --- a/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts +++ b/x-pack/plugins/apm/server/routes/traces/get_top_traces_primary_stats.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { sortBy } from 'lodash'; import { kqlQuery, @@ -23,14 +22,13 @@ import { getDurationFieldForTransactions, getDocumentTypeFilterForTransactions, getProcessorEventForTransactions, + isRootTransaction, } from '../../lib/helpers/transactions'; import { AGENT_NAME, - PARENT_ID, SERVICE_NAME, TRANSACTION_TYPE, TRANSACTION_NAME, - TRANSACTION_ROOT, } from '../../../common/elasticsearch_fieldnames'; import { RandomSampler } from '../../lib/helpers/get_random_sampler'; @@ -80,26 +78,7 @@ export async function getTopTracesPrimaryStats({ ...rangeQuery(start, end), ...environmentQuery(environment), ...kqlQuery(kuery), - ...(searchAggregatedTransactions - ? [ - { - term: { - [TRANSACTION_ROOT]: true, - }, - }, - ] - : []), - ] as estypes.QueryDslQueryContainer[], - must_not: [ - ...(!searchAggregatedTransactions - ? [ - { - exists: { - field: PARENT_ID, - }, - }, - ] - : []), + isRootTransaction(searchAggregatedTransactions), ], }, }, diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index 26b7ef917f435..2f5b47c824c9d 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -30,6 +30,14 @@ export enum SyncStatus { COMPLETED = 'completed', ERROR = 'error', } + +export interface IngestPipelineParams { + extract_binary_content: boolean; + name: string; + reduce_whitespace: boolean; + run_ml_inference: boolean; +} + export interface Connector { api_key_id: string | null; configuration: ConnectorConfiguration; @@ -42,6 +50,7 @@ export interface Connector { last_sync_status: SyncStatus | null; last_synced: string | null; name: string; + pipeline?: IngestPipelineParams | null; scheduling: { enabled: boolean; interval: string; // crontab syntax diff --git a/x-pack/plugins/enterprise_search/common/types/indices.ts b/x-pack/plugins/enterprise_search/common/types/indices.ts index d047ec9ba36d7..78831e1615004 100644 --- a/x-pack/plugins/enterprise_search/common/types/indices.ts +++ b/x-pack/plugins/enterprise_search/common/types/indices.ts @@ -36,9 +36,13 @@ export interface ElasticsearchIndex { export interface ConnectorIndex extends ElasticsearchIndex { connector: Connector; } -export interface CrawlerIndex extends ElasticsearchIndex { +export interface ConnectorCrawlerIndex extends ElasticsearchIndex { + connector: Connector; crawler: Crawler; +} +export interface CrawlerIndex extends ElasticsearchIndex { connector?: Connector; + crawler: Crawler; } export interface ElasticsearchIndexWithPrivileges extends ElasticsearchIndex { diff --git a/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts b/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts index bccb83e63e01b..1007c3f4421af 100644 --- a/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts +++ b/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts @@ -6,4 +6,4 @@ */ export const enterpriseSearchFeatureId = 'enterpriseSearch'; -export const enableIndexTransformsTab = 'enterpriseSearch:enableIndexTransformsTab'; +export const enableIndexPipelinesTab = 'enterpriseSearch:enableIndexTransformsTab'; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index 0a4524ef06d71..8d3ff402c41f8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -19,6 +19,7 @@ export const mockKibanaValues = { deployment_url: 'https://cloud.elastic.co/deployments/some-id', }, history: mockHistory, + isCloud: false, navigateToUrl: jest.fn(), productAccess: { hasAppSearchAccess: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/licensing_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/licensing_callout.tsx new file mode 100644 index 0000000000000..8325b1a5305ee --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/licensing_callout.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 React from 'react'; + +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { docLinks } from '../../../shared/doc_links/doc_links'; + +export enum LICENSING_FEATURE { + NATIVE_CONNECTOR = 'nativeConnector', + CRAWLER = 'crawler', + INFERENCE = 'inference', +} + +type ContentBlock = Record; + +export const LicensingCallout: React.FC<{ feature: LICENSING_FEATURE }> = ({ feature }) => { + const firstContentBlock: ContentBlock = { + [LICENSING_FEATURE.NATIVE_CONNECTOR]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.nativeConnector.contentOne', + { + defaultMessage: + 'Built-in connectors require a Platinum license or higher and are not available to Standard license self-managed deployments. You need to upgrade to use this feature.', + } + ), + [LICENSING_FEATURE.CRAWLER]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.crawler.contentOne', + { + defaultMessage: + 'The web crawler requires a Platinum license or higher and is not available to Standard license self-managed deployments. You need to upgrade to use this feature.', + } + ), + [LICENSING_FEATURE.INFERENCE]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.inference.contentOne', + { + defaultMessage: + 'Inference processors require a Platinum license or higher and are not available to Standard license self-managed deployments. You need to upgrade to use this feature.', + } + ), + }; + + const secondContentBlock: ContentBlock = { + [LICENSING_FEATURE.NATIVE_CONNECTOR]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.contentTwo', + { + defaultMessage: + "Did you know that built-in connectors are available with a Standard Elastic Cloud license? Elastic Cloud gives you the flexibility to run where you want. Deploy our managed service on Google Cloud, Microsoft Azure, or Amazon Web Services, and we'll handle the maintenance and upkeep for you.", + } + ), + [LICENSING_FEATURE.CRAWLER]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.crawler.contentTwo', + { + defaultMessage: + "Did you know that web crawlers are available with a Standard Elastic Cloud license? Elastic Cloud gives you the flexibility to run where you want. Deploy our managed service on Google Cloud, Microsoft Azure, or Amazon Web Services, and we'll handle the maintenance and upkeep for you.", + } + ), + [LICENSING_FEATURE.INFERENCE]: i18n.translate( + 'xpack.enterpriseSearch.content.licensingCallout.inference.contentTwo', + { + defaultMessage: + "Did you know that inference processors are available with a Standard Elastic Cloud license? Elastic Cloud gives you the flexibility to run where you want. Deploy our managed service on Google Cloud, Microsoft Azure, or Amazon Web Services, and we'll handle the maintenance and upkeep for you.", + } + ), + }; + + return ( + +

{firstContentBlock[feature]}

+

{secondContentBlock[feature]}

+ + + + {i18n.translate('xpack.enterpriseSearch.workplaceSearch.explorePlatinumFeatures.link', { + defaultMessage: 'Explore Platinum features', + })} + + + + + {i18n.translate('xpack.enterpriseSearch.content.licensingCallout.contentCloudTrial', { + defaultMessage: 'Sign up for a free 14-day Elastic Cloud trial.', + })} + + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx index fe62dd439e3a3..70cf83fbb9764 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx @@ -9,7 +9,14 @@ import React from 'react'; import { useActions, useValues } from 'kea'; -import { EuiConfirmModal, EuiLink, EuiSteps, EuiText } from '@elastic/eui'; +import { + EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSteps, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -17,8 +24,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Status } from '../../../../../../common/types/api'; import { docLinks } from '../../../../shared/doc_links'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { LicensingLogic } from '../../../../shared/licensing'; import { AddConnectorApiLogic } from '../../../api/connector/add_connector_api_logic'; +import { LicensingCallout, LICENSING_FEATURE } from '../licensing_callout'; import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexLogic } from '../new_search_index_logic'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -33,134 +43,151 @@ export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) = const { isModalVisible } = useValues(AddConnectorLogic); const { setIsModalVisible } = useActions(AddConnectorLogic); const { fullIndexName, language } = useValues(NewSearchIndexLogic); + const { isCloud } = useValues(KibanaLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); + + const isGated = isNative && !isCloud && !hasPlatinumLicense; return ( - { - apiReset(); - }} - onSubmit={(name, lang) => makeRequest({ indexName: name, isNative, language: lang })} - buttonLoading={status === Status.LOADING} - > - -

- -

- - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title', - { - defaultMessage: 'Use a pre-built connector to populate your index', - } - ), - titleSize: 'xs', - } - : { - children: isNative ? ( - -

- -

-
- ) : ( - -

- - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink', - { defaultMessage: 'Bulk API' } - )} - - ), - }} - /> -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title', - { - defaultMessage: 'Build and configure a connector', - } - ), - titleSize: 'xs', - }, - BUILD_SEARCH_EXPERIENCE_STEP, - ]} - /> - {isModalVisible && ( - + {isGated && ( + + + + )} + + { - event?.preventDefault(); - setIsModalVisible(false); + type="connector" + onNameChange={() => { + apiReset(); }} - onConfirm={(event) => { - event.preventDefault(); - makeRequest({ - deleteExistingConnector: true, - indexName: fullIndexName, - isNative, - language, - }); - }} - cancelButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label', - { - defaultMessage: 'Cancel', - } - )} - confirmButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label', - { - defaultMessage: 'Replace configuration', - } - )} - defaultFocusedButton="confirm" + onSubmit={(name, lang) => makeRequest({ indexName: name, isNative, language: lang })} + buttonLoading={status === Status.LOADING} > - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description', - { - defaultMessage: - 'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?', - values: { - indexName: fullIndexName, - }, - } + +

+ +

+ + ), + status: 'incomplete', + title: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title', + { + defaultMessage: 'Use a pre-built connector to populate your index', + } + ), + titleSize: 'xs', + } + : { + children: isNative ? ( + +

+ +

+
+ ) : ( + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink', + { defaultMessage: 'Bulk API' } + )} + + ), + }} + /> +

+
+ ), + status: 'incomplete', + title: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title', + { + defaultMessage: 'Build and configure a connector', + } + ), + titleSize: 'xs', + }, + BUILD_SEARCH_EXPERIENCE_STEP, + ]} + /> + {isModalVisible && ( + { + event?.preventDefault(); + setIsModalVisible(false); + }} + onConfirm={(event) => { + event.preventDefault(); + makeRequest({ + deleteExistingConnector: true, + indexName: fullIndexName, + isNative, + language, + }); + }} + cancelButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label', + { + defaultMessage: 'Cancel', + } + )} + confirmButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label', + { + defaultMessage: 'Replace configuration', + } + )} + defaultFocusedButton="confirm" + > + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description', + { + defaultMessage: + 'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?', + values: { + indexName: fullIndexName, + }, + } + )} + )} -
- )} -
+ +
+ ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx index 4c947ddb0bb3a..b96afab404e68 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx @@ -9,13 +9,16 @@ import React from 'react'; import { useValues, useActions } from 'kea'; -import { EuiSteps, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSteps, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Status } from '../../../../../../common/types/api'; import { docLinks } from '../../../../shared/doc_links'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { LicensingLogic } from '../../../../shared/licensing'; import { CreateCrawlerIndexApiLogic } from '../../../api/crawler/create_crawler_index_api_logic'; +import { LicensingCallout, LICENSING_FEATURE } from '../licensing_callout'; import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -24,51 +27,65 @@ import { MethodCrawlerLogic } from './method_crawler_logic'; export const MethodCrawler: React.FC = () => { const { status } = useValues(CreateCrawlerIndexApiLogic); const { makeRequest } = useActions(CreateCrawlerIndexApiLogic); + const { isCloud } = useValues(KibanaLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); + + const isGated = !isCloud && !hasPlatinumLicense; MethodCrawlerLogic.mount(); return ( - + {isGated && ( + + + )} - type="crawler" - onSubmit={(indexName, language) => makeRequest({ indexName, language })} - buttonLoading={status === Status.LOADING} - docsUrl={docLinks.crawlerOverview} - > - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content', - { - defaultMessage: - 'Configure the domains you’d like to crawl, and when ready trigger your first crawl. Let Enterprise Search do the rest.', - } - )} -

- - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title', + + makeRequest({ indexName, language })} + disabled={isGated} + buttonLoading={status === Status.LOADING} + docsUrl={docLinks.crawlerOverview} + > + - + children: ( + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content', + { + defaultMessage: + 'Configure the domains you’d like to crawl, and when ready trigger your first crawl. Let Enterprise Search do the rest.', + } + )} +

+
+ ), + status: 'incomplete', + title: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title', + { + defaultMessage: 'Configure ingestion settings', + } + ), + titleSize: 'xs', + }, + BUILD_SEARCH_EXPERIENCE_STEP, + ]} + /> +
+ + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx index 69baaee26488f..6401db2a7974d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx @@ -32,6 +32,7 @@ import { LanguageForOptimization } from './types'; export interface Props { buttonLoading?: boolean; + disabled?: boolean; docsUrl?: string; error?: string | React.ReactNode; onNameChange?(name: string): void; @@ -42,6 +43,7 @@ export interface Props { export const NewSearchIndexTemplate: React.FC = ({ children, + disabled, docsUrl, error, title, @@ -101,12 +103,12 @@ export const NewSearchIndexTemplate: React.FC = ({ return ( { event.preventDefault(); onSubmit(fullIndexName, language); }} - component="form" - id="enterprise-search-add-connector" > @@ -118,6 +120,7 @@ export const NewSearchIndexTemplate: React.FC = ({ = ({ } )} fullWidth + disabled={disabled} isInvalid={false} value={rawName} onChange={handleNameChange} @@ -164,6 +168,7 @@ export const NewSearchIndexTemplate: React.FC = ({ = ({ )} > = ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx index ca9a415c4c958..9c22ecd8572ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx @@ -34,7 +34,7 @@ import { UpdateConnectorSchedulingApiLogic } from '../../../api/connector/update import { SEARCH_INDEX_TAB_PATH } from '../../../routes'; import { IngestionStatus } from '../../../types'; -import { isConnectorIndex } from '../../../utils/indices'; +import { isConnectorIndex, isConnectorCrawlerIndex } from '../../../utils/indices'; import { IndexViewLogic } from '../index_view_logic'; @@ -61,7 +61,7 @@ export const ConnectorSchedulingComponent: React.FC = () => { frequency: schedulingInput?.interval ? cronToFrequency(schedulingInput.interval) : 'HOUR', }); - if (!isConnectorIndex(index)) { + if (!isConnectorIndex(index) && !isConnectorCrawlerIndex(index)) { return <>; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx new file mode 100644 index 0000000000000..c80f4cd669273 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.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 { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { DataPanel } from '../../../../shared/data_panel/data_panel'; + +export const SearchIndexPipelines: React.FC = () => { + return ( + <> + + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.title', + { + defaultMessage: 'Ingest Pipelines', + } + )} +

+ } + iconType="logstashInput" + > +
+ + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.mlInferencePipelines.title', + { + defaultMessage: 'ML Inference pipelines', + } + )} + + } + iconType="compute" + > +
+ + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx index 14be5f36b5e22..b998fa5d10db2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx @@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { Status } from '../../../../../common/types/api'; -import { enableIndexTransformsTab } from '../../../../../common/ui_settings_keys'; +import { enableIndexPipelinesTab } from '../../../../../common/ui_settings_keys'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; import { FetchIndexApiLogic } from '../../api/index/fetch_index_api_logic'; @@ -32,23 +32,23 @@ import { IndexCreatedCallout } from './components/index_created_callout/callout' import { IndexCreatedCalloutLogic } from './components/index_created_callout/callout_logic'; import { ConnectorConfiguration } from './connector/connector_configuration'; import { ConnectorSchedulingComponent } from './connector/connector_scheduling'; -import { AutomaticCrawlScheduler } from './crawler/automatic_crawl_scheduler/automatic_crawl_scheduler'; import { CrawlCustomSettingsFlyout } from './crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout'; import { SearchIndexDomainManagement } from './crawler/domain_management/domain_management'; import { SearchIndexDocuments } from './documents'; import { SearchIndexIndexMappings } from './index_mappings'; import { IndexNameLogic } from './index_name_logic'; import { SearchIndexOverview } from './overview'; +import { SearchIndexPipelines } from './pipelines/pipelines'; export enum SearchIndexTabId { // all indices OVERVIEW = 'overview', DOCUMENTS = 'documents', INDEX_MAPPINGS = 'index_mappings', + PIPELINES = 'pipelines', // connector indices CONFIGURATION = 'configuration', SCHEDULING = 'scheduling', - TRANSFORMS = 'transforms', // crawler indices DOMAIN_MANAGEMENT = 'domain_management', } @@ -65,7 +65,7 @@ export const SearchIndex: React.FC = () => { const { indexName } = useValues(IndexNameLogic); - const transformsEnabled = uiSettings?.get(enableIndexTransformsTab) ?? false; + const pipelinesEnabled = uiSettings?.get(enableIndexPipelinesTab) ?? false; const ALL_INDICES_TABS: EuiTabbedContentTab[] = [ { @@ -117,7 +117,7 @@ export const SearchIndex: React.FC = () => { }), }, { - content: , + content: , id: SearchIndexTabId.SCHEDULING, name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.schedulingTabLabel', { defaultMessage: 'Scheduling', @@ -125,12 +125,12 @@ export const SearchIndex: React.FC = () => { }, ]; - const TRANSFORMS_TAB: EuiTabbedContentTab[] = [ + const PIPELINES_TAB: EuiTabbedContentTab[] = [ { - content:
, - id: SearchIndexTabId.TRANSFORMS, - name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.transformsTabLabel', { - defaultMessage: 'Transforms', + content: , + id: SearchIndexTabId.PIPELINES, + name: i18n.translate('xpack.enterpriseSearch.content.searchIndex.pipelinesTabLabel', { + defaultMessage: 'Pipelines', }), }, ]; @@ -139,7 +139,7 @@ export const SearchIndex: React.FC = () => { ...ALL_INDICES_TABS, ...(isConnectorIndex(indexData) ? CONNECTOR_TABS : []), ...(isCrawlerIndex(indexData) ? CRAWLER_TABS : []), - ...(transformsEnabled && isConnectorIndex(indexData) ? TRANSFORMS_TAB : []), + ...(pipelinesEnabled ? PIPELINES_TAB : []), ]; const selectedTab = tabs.find((tab) => tab.id === tabId); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx index 8ff7550b61ffd..b060296835238 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx @@ -161,7 +161,6 @@ export const IndicesTable: React.FC = ({ type: 'icon', }, { - available: (index) => !isCrawlerIndex(index), color: 'danger', description: 'Delete this index', icon: 'trash', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.test.ts index ce6e443a25025..b023aff4b9e08 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.test.ts @@ -23,6 +23,7 @@ import { getLastUpdated, indexToViewIndex, isConnectorIndex, + isConnectorCrawlerIndex, isCrawlerIndex, isApiIndex, isConnectorViewIndex, @@ -144,6 +145,20 @@ describe('Indices util functions', () => { expect(isConnectorIndex(apiIndex)).toEqual(false); }); }); + describe('isConnectorCrawlerIndex', () => { + it('should return false for connector indices', () => { + expect(isConnectorCrawlerIndex(connectorIndex)).toEqual(false); + }); + it('should return false for connector-crawler indices', () => { + expect(isConnectorCrawlerIndex(connectorCrawlerIndex)).toEqual(true); + }); + it('should return false for crawler indices', () => { + expect(isConnectorCrawlerIndex(crawlerIndex)).toEqual(false); + }); + it('should return false for API indices', () => { + expect(isConnectorCrawlerIndex(apiIndex)).toEqual(false); + }); + }); describe('isCrawlerIndex', () => { it('should return true for crawler indices', () => { expect(isCrawlerIndex(crawlerIndex)).toEqual(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.ts index 9a17f7fe84f4d..540ad2b2db69e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/indices.ts @@ -13,6 +13,7 @@ import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../../../co import { SyncStatus, ConnectorStatus } from '../../../../common/types/connectors'; import { ConnectorIndex, + ConnectorCrawlerIndex, CrawlerIndex, ElasticsearchIndexWithIngestion, } from '../../../../common/types/indices'; @@ -36,6 +37,16 @@ export function isConnectorIndex( ); } +export function isConnectorCrawlerIndex( + index: ElasticsearchIndexWithIngestion | undefined +): index is ConnectorCrawlerIndex { + const crawlerIndex = index as CrawlerIndex; + return ( + !!crawlerIndex?.connector && + crawlerIndex.connector.service_type === ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE + ); +} + export function isCrawlerIndex( index: ElasticsearchIndexWithIngestion | undefined ): index is CrawlerIndex { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx index 4cdeec3048f8b..1bb6bb3777d81 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx @@ -145,7 +145,7 @@ export const ProductSelector: React.FC = ({ defaultMessage: 'Set up a language client', } ), - to: docLinks.start, + to: docLinks.languageClients, }, { label: i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index ad7205ced01c2..ac5a5d05ef60f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -70,6 +70,7 @@ class DocLinks { public enterpriseSearchUsersAccess: string; public kibanaSecurity: string; public languageAnalyzers: string; + public languageClients: string; public licenseManagement: string; public pluginsIngestAttachment: string; public queryDsl: string; @@ -175,6 +176,7 @@ class DocLinks { this.enterpriseSearchUsersAccess = ''; this.kibanaSecurity = ''; this.languageAnalyzers = ''; + this.languageClients = ''; this.licenseManagement = ''; this.pluginsIngestAttachment = ''; this.queryDsl = ''; @@ -282,6 +284,7 @@ class DocLinks { this.enterpriseSearchUsersAccess = docLinks.links.enterpriseSearch.usersAccess; this.kibanaSecurity = docLinks.links.kibana.xpackSecurity; this.languageAnalyzers = docLinks.links.enterpriseSearch.languageAnalyzers; + this.languageClients = docLinks.links.enterpriseSearch.languageClients; this.licenseManagement = docLinks.links.enterpriseSearch.licenseManagement; this.pluginsIngestAttachment = docLinks.links.plugins.ingestAttachment; this.queryDsl = docLinks.links.query.queryDsl; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index d579cf17e5be9..37ab1e972bbd5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -39,8 +39,9 @@ interface KibanaLogicProps { cloud?: CloudSetup; } export interface KibanaValues extends Omit { - navigateToUrl(path: string, options?: CreateHrefOptions): Promise; cloud: Partial; + isCloud: boolean; + navigateToUrl(path: string, options?: CreateHrefOptions): Promise; } export const KibanaLogic = kea>({ @@ -59,11 +60,14 @@ export const KibanaLogic = kea>({ {}, ], productAccess: [props.productAccess, {}], + renderHeaderActions: [props.renderHeaderActions, {}], security: [props.security, {}], setBreadcrumbs: [props.setBreadcrumbs, {}], setChromeIsVisible: [props.setChromeIsVisible, {}], setDocTitle: [props.setDocTitle, {}], - renderHeaderActions: [props.renderHeaderActions, {}], + }), + selectors: ({ selectors }) => ({ + isCloud: [() => [selectors.cloud], (cloud?: Partial) => !!cloud?.isCloudEnabled], }), }); diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts index 63b777d0dff31..59e7edf1d21d5 100644 --- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts @@ -7,7 +7,7 @@ import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX, CONNECTORS_VERSION } from '..'; -import { setupConnectorsIndices } from './setup_indices'; +import { defaultConnectorsPipelineMeta, setupConnectorsIndices } from './setup_indices'; describe('Setup Indices', () => { const mockClient = { @@ -29,6 +29,7 @@ describe('Setup Indices', () => { const connectorsMappings = { _meta: { version: CONNECTORS_VERSION, + pipeline: defaultConnectorsPipelineMeta, }, properties: { api_key_id: { diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts index 08bcdbc38c3c5..b564d519e73f9 100644 --- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts @@ -59,11 +59,26 @@ const defaultSettings: IndicesIndexSettings = { number_of_replicas: 0, }; +export interface DefaultConnectorsPipelineMeta { + default_extract_binary_content: boolean; + default_name: string; + default_reduce_whitespace: boolean; + default_run_ml_inference: boolean; +} + +export const defaultConnectorsPipelineMeta: DefaultConnectorsPipelineMeta = { + default_extract_binary_content: true, + default_name: 'ent-search-generic-ingestion', + default_reduce_whitespace: true, + default_run_ml_inference: false, +}; + const indices: IndexDefinition[] = [ { aliases: ['.elastic-connectors'], mappings: { _meta: { + pipeline: defaultConnectorsPipelineMeta, version: '1', }, properties: connectorMappingsProperties, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts index 42d0235cbd3a3..24b01c5e0bf03 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts @@ -34,6 +34,7 @@ describe('addConnector lib function', () => { indices: { create: jest.fn(), exists: jest.fn(), + getMapping: jest.fn(), refresh: jest.fn(), }, }, @@ -49,6 +50,22 @@ describe('addConnector lib function', () => { jest.clearAllMocks(); }); + const connectorsIndicesMapping = { + '.elastic-connectors-v1': { + mappings: { + _meta: { + pipeline: { + default_extract_binary_content: true, + default_name: 'ent-search-generic-ingestion', + default_reduce_whitespace: true, + default_run_ml_inference: false, + }, + version: '1', + }, + }, + }, + }; + it('should add connector', async () => { mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' })); mockClient.asCurrentUser.indices.exists.mockImplementation( @@ -56,6 +73,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => undefined); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -76,6 +94,12 @@ describe('addConnector lib function', () => { last_sync_status: null, last_synced: null, name: 'index_name', + pipeline: { + extract_binary_content: true, + name: 'ent-search-generic-ingestion', + reduce_whitespace: true, + run_ml_inference: false, + }, scheduling: { enabled: false, interval: '0 0 0 * * ?' }, service_type: null, status: ConnectorStatus.CREATED, @@ -97,6 +121,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => undefined); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -115,6 +140,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => true); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -133,6 +159,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => undefined); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => true); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -151,6 +178,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => true); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -169,6 +197,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => ({ id: 'connectorId' })); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { @@ -194,6 +223,12 @@ describe('addConnector lib function', () => { last_sync_status: null, last_synced: null, name: 'index_name', + pipeline: { + extract_binary_content: true, + name: 'ent-search-generic-ingestion', + reduce_whitespace: true, + run_ml_inference: false, + }, scheduling: { enabled: false, interval: '0 0 0 * * ?' }, service_type: null, status: ConnectorStatus.CREATED, @@ -218,6 +253,7 @@ describe('addConnector lib function', () => { ); (fetchConnectorByIndexName as jest.Mock).mockImplementation(() => false); (fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined); + mockClient.asCurrentUser.indices.getMapping.mockImplementation(() => connectorsIndicesMapping); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { index_name: 'search-index_name', @@ -238,6 +274,12 @@ describe('addConnector lib function', () => { last_sync_status: null, last_synced: null, name: 'index_name', + pipeline: { + extract_binary_content: true, + name: 'ent-search-generic-ingestion', + reduce_whitespace: true, + run_ml_inference: false, + }, scheduling: { enabled: false, interval: '0 0 0 * * ?' }, service_type: null, status: ConnectorStatus.CREATED, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts index f3275c0b2d73b..6838ef95ce936 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts @@ -8,9 +8,13 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { CONNECTORS_INDEX } from '../..'; +import { CONNECTORS_VERSION } from '../..'; import { ConnectorDocument, ConnectorStatus } from '../../../common/types/connectors'; import { ErrorCode } from '../../../common/types/error_codes'; -import { setupConnectorsIndices } from '../../index_management/setup_indices'; +import { + DefaultConnectorsPipelineMeta, + setupConnectorsIndices, +} from '../../index_management/setup_indices'; import { fetchCrawlerByIndexName } from '../crawler/fetch_crawlers'; import { createIndex } from '../indices/create_index'; @@ -67,6 +71,19 @@ export const addConnector = async ( service_type?: string | null; } ): Promise<{ id: string; index_name: string }> => { + const connectorsIndexExists = await client.asCurrentUser.indices.exists({ + index: CONNECTORS_INDEX, + }); + if (!connectorsIndexExists) { + await setupConnectorsIndices(client.asCurrentUser); + } + const connectorsIndicesMapping = await client.asCurrentUser.indices.getMapping({ + index: CONNECTORS_INDEX, + }); + const connectorsPipelineMeta: DefaultConnectorsPipelineMeta = + connectorsIndicesMapping[`${CONNECTORS_INDEX}-v${CONNECTORS_VERSION}`]?.mappings?._meta + ?.pipeline; + const document: ConnectorDocument = { api_key_id: null, configuration: {}, @@ -78,16 +95,18 @@ export const addConnector = async ( last_sync_status: null, last_synced: null, name: input.index_name.startsWith('search-') ? input.index_name.substring(7) : input.index_name, + pipeline: connectorsPipelineMeta + ? { + extract_binary_content: connectorsPipelineMeta.default_extract_binary_content, + name: connectorsPipelineMeta.default_name, + reduce_whitespace: connectorsPipelineMeta.default_reduce_whitespace, + run_ml_inference: connectorsPipelineMeta.default_run_ml_inference, + } + : null, scheduling: { enabled: false, interval: '0 0 0 * * ?' }, service_type: input.service_type || null, status: ConnectorStatus.CREATED, sync_now: false, }; - const connectorsIndexExists = await client.asCurrentUser.indices.exists({ - index: CONNECTORS_INDEX, - }); - if (!connectorsIndexExists) { - await setupConnectorsIndices(client.asCurrentUser); - } return await createConnector(document, client, input.language, !!input.delete_existing_connector); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts index 183b7cb4b3438..fa56e19ed00a1 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts @@ -25,7 +25,11 @@ import { createIndexPipelineDefinitions } from '../../utils/create_pipeline_defi import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; import { isIndexNotFoundException } from '../../utils/identify_exceptions'; -export function registerIndexRoutes({ router, log }: RouteDependencies) { +export function registerIndexRoutes({ + router, + enterpriseSearchRequestHandler, + log, +}: RouteDependencies) { router.get( { path: '/internal/enterprise_search/search_indices', validate: false }, elasticsearchErrorHandler(log, async (context, _, response) => { @@ -141,15 +145,21 @@ export function registerIndexRoutes({ router, log }: RouteDependencies) { const { client } = (await context.core).elasticsearch; try { - const connector = await fetchConnectorByIndexName(client, indexName); const crawler = await fetchCrawlerByIndexName(client, indexName); + const connector = await fetchConnectorByIndexName(client, indexName); - if (connector) { - await deleteConnectorById(client, connector.id); + if (crawler) { + const crawlerRes = await enterpriseSearchRequestHandler.createRequest({ + path: `/api/ent/v1/internal/indices/${indexName}`, + })(context, request, response); + + if (crawlerRes.status !== 200) { + throw new Error(crawlerRes.payload.message); + } } - if (crawler) { - // do nothing for now because we don't have a way to delete a crawler yet + if (connector) { + await deleteConnectorById(client, connector.id); } await client.asCurrentUser.indices.delete({ index: indexName }); diff --git a/x-pack/plugins/enterprise_search/server/ui_settings.ts b/x-pack/plugins/enterprise_search/server/ui_settings.ts index 15241cc5fe890..0497aa54d2eec 100644 --- a/x-pack/plugins/enterprise_search/server/ui_settings.ts +++ b/x-pack/plugins/enterprise_search/server/ui_settings.ts @@ -9,19 +9,19 @@ import { schema } from '@kbn/config-schema'; import { UiSettingsParams } from '@kbn/core/types'; import { i18n } from '@kbn/i18n'; -import { enterpriseSearchFeatureId, enableIndexTransformsTab } from '../common/ui_settings_keys'; +import { enterpriseSearchFeatureId, enableIndexPipelinesTab } from '../common/ui_settings_keys'; /** * uiSettings definitions for Enterprise Search */ export const uiSettings: Record> = { - [enableIndexTransformsTab]: { + [enableIndexPipelinesTab]: { category: [enterpriseSearchFeatureId], - description: i18n.translate('xpack.enterpriseSearch.uiSettings.indexTransforms.description', { - defaultMessage: 'Enable the new index transforms tab in Enterprise Search.', + description: i18n.translate('xpack.enterpriseSearch.uiSettings.indexPipelines.description', { + defaultMessage: 'Enable the new index pipelines tab in Enterprise Search.', }), - name: i18n.translate('xpack.enterpriseSearch.uiSettings.indexTransforms.name', { - defaultMessage: 'Enable index transforms', + name: i18n.translate('xpack.enterpriseSearch.uiSettings.indexPipelines.name', { + defaultMessage: 'Enable index pipelines', }), requiresPageReload: false, schema: schema.boolean(), diff --git a/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.test.ts b/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.test.ts index 27208dbaed00e..6961086edac1b 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.test.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.test.ts @@ -8,6 +8,7 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { createIndexPipelineDefinitions } from './create_pipeline_definitions'; +import { formatMlPipelineBody } from './create_pipeline_definitions'; describe('createIndexPipelineDefinitions util function', () => { const indexName = 'my-index'; @@ -34,3 +35,163 @@ describe('createIndexPipelineDefinitions util function', () => { expect(mockClient.ingest.putPipeline).toHaveBeenCalledTimes(3); }); }); + +describe('formatMlPipelineBody util function', () => { + const modelId = 'my-model-id'; + let modelInputField = 'my-model-input-field'; + const modelType = 'my-model-type'; + const modelVersion = 3; + const sourceField = 'my-source-field'; + const destField = 'my-dest-field'; + + const mockClient = { + ml: { + getTrainedModels: jest.fn(), + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return the pipeline body', async () => { + const expectedResult = { + description: '', + version: 1, + processors: [ + { + remove: { + field: `ml.inference.${destField}`, + ignore_missing: true, + }, + }, + { + inference: { + model_id: modelId, + target_field: `ml.inference.${destField}`, + field_map: { + sourceField: modelInputField, + }, + }, + }, + { + append: { + field: '_source._ingest.processors', + value: [ + { + type: modelType, + model_id: modelId, + model_version: modelVersion, + processed_timestamp: '{{{ _ingest.timestamp }}}', + }, + ], + }, + }, + ], + }; + + const mockResponse = { + count: 1, + trained_model_configs: [ + { + model_id: modelId, + version: modelVersion, + model_type: modelType, + input: { field_names: [modelInputField] }, + }, + ], + }; + mockClient.ml.getTrainedModels.mockImplementation(() => Promise.resolve(mockResponse)); + const actualResult = await formatMlPipelineBody( + modelId, + sourceField, + destField, + mockClient as unknown as ElasticsearchClient + ); + expect(actualResult).toEqual(expectedResult); + expect(mockClient.ml.getTrainedModels).toHaveBeenCalledTimes(1); + }); + + it('should raise an error if no model found', async () => { + const mockResponse = { + error: { + root_cause: [ + { + type: 'resource_not_found_exception', + reason: 'No known trained model with model_id [my-model-id]', + }, + ], + type: 'resource_not_found_exception', + reason: 'No known trained model with model_id [my-model-id]', + }, + status: 404, + }; + mockClient.ml.getTrainedModels.mockImplementation(() => Promise.resolve(mockResponse)); + const asyncCall = formatMlPipelineBody( + modelId, + sourceField, + destField, + mockClient as unknown as ElasticsearchClient + ); + await expect(asyncCall).rejects.toThrow(Error); + expect(mockClient.ml.getTrainedModels).toHaveBeenCalledTimes(1); + }); + + it('should insert a placeholder if model has no input fields', async () => { + modelInputField = 'MODEL_INPUT_FIELD'; + const expectedResult = { + description: '', + version: 1, + processors: [ + { + remove: { + field: `ml.inference.${destField}`, + ignore_missing: true, + }, + }, + { + inference: { + model_id: modelId, + target_field: `ml.inference.${destField}`, + field_map: { + sourceField: modelInputField, + }, + }, + }, + { + append: { + field: '_source._ingest.processors', + value: [ + { + type: modelType, + model_id: modelId, + model_version: modelVersion, + processed_timestamp: '{{{ _ingest.timestamp }}}', + }, + ], + }, + }, + ], + }; + const mockResponse = { + count: 1, + trained_model_configs: [ + { + model_id: modelId, + version: modelVersion, + model_type: modelType, + input: { field_names: [] }, + }, + ], + }; + mockClient.ml.getTrainedModels.mockImplementation(() => Promise.resolve(mockResponse)); + const actualResult = await formatMlPipelineBody( + modelId, + sourceField, + destField, + mockClient as unknown as ElasticsearchClient + ); + expect(actualResult).toEqual(expectedResult); + expect(mockClient.ml.getTrainedModels).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.ts b/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.ts index 377f12fd63208..666588dd09886 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_pipeline_definitions.ts @@ -5,12 +5,17 @@ * 2.0. */ +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core/server'; export interface CreatedPipelines { created: string[]; } +export interface MlInferencePipeline extends IngestPipeline { + version?: number; +} + /** * Used to create index-specific Ingest Pipelines to be used in conjunction with Enterprise Search * ingestion mechanisms. Three pipelines are created: @@ -225,3 +230,64 @@ export const createIndexPipelineDefinitions = ( }); return { created: [indexName, `${indexName}@custom`, `${indexName}@ml-inference`] }; }; + +/** + * Format the body of an ML inference pipeline for a specified model. + * Does not create the pipeline, only returns JSON for the user to preview. + * @param modelId modelId selected by user. + * @param sourceField The document field that model will read. + * @param destinationField The document field that the model will write to. + * @param esClient the Elasticsearch Client to use when retrieving model details. + */ +export const formatMlPipelineBody = async ( + modelId: string, + sourceField: string, + destinationField: string, + esClient: ElasticsearchClient +): Promise => { + const models = await esClient.ml.getTrainedModels({ model_id: modelId }); + // if we didn't find this model, we can't return anything useful + if (models.trained_model_configs === undefined || models.trained_model_configs.length === 0) { + throw new Error(`Couldn't find any trained models with id [${modelId}]`); + } + const model = models.trained_model_configs[0]; + // if model returned no input field, insert a placeholder + const modelInputField = + model.input?.field_names?.length > 0 ? model.input.field_names[0] : 'MODEL_INPUT_FIELD'; + const modelType = model.model_type; + const modelVersion = model.version; + return { + description: '', + version: 1, + processors: [ + { + remove: { + field: `ml.inference.${destinationField}`, + ignore_missing: true, + }, + }, + { + inference: { + model_id: modelId, + target_field: `ml.inference.${destinationField}`, + field_map: { + sourceField: modelInputField, + }, + }, + }, + { + append: { + field: '_source._ingest.processors', + value: [ + { + type: modelType, + model_id: modelId, + model_version: modelVersion, + processed_timestamp: '{{{ _ingest.timestamp }}}', + }, + ], + }, + }, + ], + }; +}; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts index 53a1b9501b432..3cb1b8d12c0b1 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts @@ -25,6 +25,7 @@ const createClusterClientMock = () => { setIndexAliasToHidden: jest.fn(), queryEventsBySavedObjects: jest.fn(), aggregateEventsBySavedObjects: jest.fn(), + aggregateEventsWithAuthFilter: jest.fn(), shutdown: jest.fn(), }; return mock; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index ed740a27b81e4..ea3e98e599ab5 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -13,11 +13,14 @@ import { getQueryBody, FindEventsOptionsBySavedObjectFilter, AggregateEventsOptionsBySavedObjectFilter, + AggregateEventsWithAuthFilter, + getQueryBodyWithAuthFilter, } from './cluster_client_adapter'; import { AggregateOptionsType, queryOptionsSchema } from '../event_log_client'; import { delay } from '../lib/delay'; import { pick, times } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; +import { fromKueryExpression } from '@kbn/es-query'; type MockedLogger = ReturnType; @@ -728,6 +731,104 @@ describe('aggregateEventsBySavedObject', () => { }); }); +describe('aggregateEventsWithAuthFilter', () => { + const DEFAULT_OPTIONS = { + ...queryOptionsSchema.validate({}), + aggs: { + genericAgg: { + term: { + field: 'event.action', + size: 10, + }, + }, + }, + }; + + test('should call cluster with correct options', async () => { + clusterClient.search.mockResponse({ + aggregations: { + genericAgg: { + buckets: [ + { + key: 'execute', + doc_count: 10, + }, + { + key: 'execute-start', + doc_count: 10, + }, + { + key: 'new-instance', + doc_count: 2, + }, + ], + }, + }, + hits: { + hits: [], + total: { relation: 'eq', value: 0 }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + successful: 0, + total: 0, + skipped: 0, + }, + }); + const options: AggregateEventsWithAuthFilter = { + index: 'index-name', + namespace: 'namespace', + type: 'saved-object-type', + aggregateOptions: DEFAULT_OPTIONS as AggregateOptionsType, + authFilter: fromKueryExpression('test:test'), + }; + const result = await clusterClientAdapter.aggregateEventsWithAuthFilter(options); + + const [query] = clusterClient.search.mock.calls[0]; + expect(query).toEqual({ + index: 'index-name', + body: { + size: 0, + query: getQueryBodyWithAuthFilter( + logger, + options, + pick(options.aggregateOptions, ['start', 'end', 'filter']) + ), + aggs: { + genericAgg: { + term: { + field: 'event.action', + size: 10, + }, + }, + }, + }, + }); + expect(result).toEqual({ + aggregations: { + genericAgg: { + buckets: [ + { + key: 'execute', + doc_count: 10, + }, + { + key: 'execute-start', + doc_count: 10, + }, + { + key: 'new-instance', + doc_count: 2, + }, + ], + }, + }, + }); + }); +}); + describe('getQueryBody', () => { const options = { index: 'index-name', @@ -1411,6 +1512,431 @@ describe('getQueryBody', () => { }); }); +describe('getQueryBodyWithAuthFilter', () => { + const options = { + index: 'index-name', + namespace: undefined, + type: 'saved-object-type', + authFilter: fromKueryExpression('test:test'), + }; + test('should correctly build query with namespace filter when namespace is undefined', () => { + expect( + getQueryBodyWithAuthFilter(logger, options as AggregateEventsWithAuthFilter, {}) + ).toEqual({ + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'kibana.saved_objects.namespace', + }, + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }); + }); + + test('should correctly build query with namespace filter when namespace is specified', () => { + expect( + getQueryBodyWithAuthFilter( + logger, + { ...options, namespace: 'namespace' } as AggregateEventsWithAuthFilter, + {} + ) + ).toEqual({ + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + term: { + 'kibana.saved_objects.namespace': { + value: 'namespace', + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }); + }); + + test('should correctly build query when filter is specified', () => { + expect( + getQueryBodyWithAuthFilter(logger, options as AggregateEventsWithAuthFilter, { + filter: 'event.provider: alerting AND event.action:execute', + }) + ).toEqual({ + bool: { + filter: { + bool: { + filter: [ + { + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + 'event.action': 'execute', + }, + }, + ], + }, + }, + ], + }, + }, + { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'kibana.saved_objects.namespace', + }, + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }); + }); + + test('should correctly build query when start is specified', () => { + expect( + getQueryBodyWithAuthFilter(logger, options as AggregateEventsWithAuthFilter, { + start: '2020-07-08T00:52:28.350Z', + }) + ).toEqual({ + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'kibana.saved_objects.namespace', + }, + }, + }, + }, + ], + }, + }, + }, + }, + { + range: { + '@timestamp': { + gte: '2020-07-08T00:52:28.350Z', + }, + }, + }, + ], + }, + }); + }); + + test('should correctly build query when end is specified', () => { + expect( + getQueryBodyWithAuthFilter(logger, options as AggregateEventsWithAuthFilter, { + end: '2020-07-10T00:52:28.350Z', + }) + ).toEqual({ + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'kibana.saved_objects.namespace', + }, + }, + }, + }, + ], + }, + }, + }, + }, + { + range: { + '@timestamp': { + lte: '2020-07-10T00:52:28.350Z', + }, + }, + }, + ], + }, + }); + }); + + test('should correctly build query when start and end are specified', () => { + expect( + getQueryBodyWithAuthFilter(logger, options as AggregateEventsWithAuthFilter, { + start: '2020-07-08T00:52:28.350Z', + end: '2020-07-10T00:52:28.350Z', + }) + ).toEqual({ + bool: { + filter: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + test: 'test', + }, + }, + ], + }, + }, + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'kibana.saved_objects.namespace', + }, + }, + }, + }, + ], + }, + }, + }, + }, + { + range: { + '@timestamp': { + gte: '2020-07-08T00:52:28.350Z', + }, + }, + }, + { + range: { + '@timestamp': { + lte: '2020-07-10T00:52:28.350Z', + }, + }, + }, + ], + }, + }); + }); +}); + type RetryableFunction = () => boolean; const RETRY_UNTIL_DEFAULT_COUNT = 20; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index d097bb9ee1957..e807899d6290b 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -12,7 +12,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { Logger, ElasticsearchClient } from '@kbn/core/server'; import util from 'util'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { fromKueryExpression, toElasticsearchQuery, KueryNode, nodeBuilder } from '@kbn/es-query'; import { IEvent, IValidatedEvent, SAVED_OBJECT_REL_PRIMARY } from '../types'; import { AggregateOptionsType, FindOptionsType, QueryOptionsType } from '../event_log_client'; import { ParsedIndexAlias } from './init'; @@ -50,6 +50,14 @@ interface QueryOptionsEventsBySavedObjectFilter { legacyIds?: string[]; } +export interface AggregateEventsWithAuthFilter { + index: string; + namespace: string | undefined; + type: string; + authFilter: KueryNode; + aggregateOptions: AggregateOptionsType; +} + export type FindEventsOptionsBySavedObjectFilter = QueryOptionsEventsBySavedObjectFilter & { findOptions: FindOptionsType; }; @@ -415,6 +423,126 @@ export class ClusterClientAdapter { + const { index, type, aggregateOptions } = queryOptions; + const { aggs } = aggregateOptions; + + const esClient = await this.elasticsearchClientPromise; + + const query = getQueryBodyWithAuthFilter( + this.logger, + queryOptions, + pick(queryOptions.aggregateOptions, ['start', 'end', 'filter']) + ); + + const body: estypes.SearchRequest['body'] = { + size: 0, + query, + aggs, + }; + + try { + const { aggregations } = await esClient.search({ + index, + body, + }); + return { + aggregations, + }; + } catch (err) { + throw new Error( + `querying for Event Log by for type "${type}" and auth filter failed with: ${err.message}` + ); + } + } +} + +export function getQueryBodyWithAuthFilter( + logger: Logger, + opts: AggregateEventsWithAuthFilter, + queryOptions: QueryOptionsType +) { + const { namespace, type, authFilter } = opts; + const { start, end, filter } = queryOptions ?? {}; + + const namespaceQuery = getNamespaceQuery(namespace); + let dslFilterQuery: estypes.QueryDslBoolQuery['filter']; + try { + const filterKueryNode = filter ? fromKueryExpression(filter) : null; + const queryFilter = filterKueryNode + ? nodeBuilder.and([filterKueryNode, authFilter as KueryNode]) + : authFilter; + dslFilterQuery = queryFilter ? toElasticsearchQuery(queryFilter) : undefined; + } catch (err) { + logger.debug( + `esContext: Invalid kuery syntax for the filter (${filter}) error: ${JSON.stringify({ + message: err.message, + statusCode: err.statusCode, + })}` + ); + throw err; + } + + const savedObjectsQueryMust: estypes.QueryDslQueryContainer[] = [ + { + term: { + 'kibana.saved_objects.rel': { + value: SAVED_OBJECT_REL_PRIMARY, + }, + }, + }, + { + term: { + 'kibana.saved_objects.type': { + value: type, + }, + }, + }, + // @ts-expect-error undefined is not assignable as QueryDslTermQuery value + namespaceQuery, + ]; + + const musts: estypes.QueryDslQueryContainer[] = [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: reject(savedObjectsQueryMust, isUndefined), + }, + }, + }, + }, + ]; + + if (start) { + musts.push({ + range: { + '@timestamp': { + gte: start, + }, + }, + }); + } + if (end) { + musts.push({ + range: { + '@timestamp': { + lte: end, + }, + }, + }); + } + + return { + bool: { + ...(dslFilterQuery ? { filter: dslFilterQuery } : {}), + must: reject(musts, isUndefined), + }, + }; } function getNamespaceQuery(namespace?: string) { @@ -446,9 +574,15 @@ export function getQueryBody( const { start, end, filter } = queryOptions ?? {}; const namespaceQuery = getNamespaceQuery(namespace); + let filterKueryNode; + try { + filterKueryNode = JSON.parse(filter ?? ''); + } catch (e) { + filterKueryNode = filter ? fromKueryExpression(filter) : null; + } let dslFilterQuery: estypes.QueryDslBoolQuery['filter']; try { - dslFilterQuery = filter ? toElasticsearchQuery(fromKueryExpression(filter)) : undefined; + dslFilterQuery = filterKueryNode ? toElasticsearchQuery(filterKueryNode) : undefined; } catch (err) { logger.debug( `esContext: Invalid kuery syntax for the filter (${filter}) error: ${JSON.stringify({ @@ -492,6 +626,7 @@ export function getQueryBody( ]; const shouldQuery = []; + shouldQuery.push({ bool: { must: [ diff --git a/x-pack/plugins/event_log/server/event_log_client.mock.ts b/x-pack/plugins/event_log/server/event_log_client.mock.ts index 7129bb9513856..0e11ded65be65 100644 --- a/x-pack/plugins/event_log/server/event_log_client.mock.ts +++ b/x-pack/plugins/event_log/server/event_log_client.mock.ts @@ -11,6 +11,7 @@ const createEventLogClientMock = () => { const mock: jest.Mocked = { findEventsBySavedObjectIds: jest.fn(), aggregateEventsBySavedObjectIds: jest.fn(), + aggregateEventsWithAuthFilter: jest.fn(), }; return mock; }; 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 89f6df538aca5..5e4fd08819fb3 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 @@ -12,6 +12,7 @@ import { contextMock } from './es/context.mock'; import { merge } from 'lodash'; import moment from 'moment'; import { IClusterClientAdapter } from './es/cluster_client_adapter'; +import { fromKueryExpression } from '@kbn/es-query'; const expectedSavedObject = { id: 'saved-object-id', @@ -240,6 +241,38 @@ describe('EventLogStart', () => { }); }); }); + describe('aggregateEventsWithAuthFilter', () => { + const testAuthFilter = fromKueryExpression('test:test'); + test('throws when no aggregation is defined in options', async () => { + savedObjectGetter.mockResolvedValueOnce(expectedSavedObject); + await expect( + eventLogClient.aggregateEventsWithAuthFilter('saved-object-type', testAuthFilter) + ).rejects.toMatchInlineSnapshot(`[Error: No aggregation defined!]`); + }); + test('calls aggregateEventsWithAuthFilter with given aggregation', async () => { + savedObjectGetter.mockResolvedValueOnce(expectedSavedObject); + await eventLogClient.aggregateEventsWithAuthFilter('saved-object-type', testAuthFilter, { + aggs: { myAgg: {} }, + }); + expect(esContext.esAdapter.aggregateEventsWithAuthFilter).toHaveBeenCalledWith({ + index: esContext.esNames.indexPattern, + namespace: undefined, + type: 'saved-object-type', + authFilter: testAuthFilter, + aggregateOptions: { + aggs: { myAgg: {} }, + page: 1, + per_page: 10, + sort: [ + { + sort_field: '@timestamp', + sort_order: 'asc', + }, + ], + }, + }); + }); + }); }); function fakeEvent(overrides = {}) { diff --git a/x-pack/plugins/event_log/server/event_log_client.ts b/x-pack/plugins/event_log/server/event_log_client.ts index 13aa2e3eadec0..e23b5f137eef1 100644 --- a/x-pack/plugins/event_log/server/event_log_client.ts +++ b/x-pack/plugins/event_log/server/event_log_client.ts @@ -12,6 +12,7 @@ import { IClusterClient, KibanaRequest } from '@kbn/core/server'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { SpacesServiceStart } from '@kbn/spaces-plugin/server'; +import { KueryNode } from '@kbn/core-saved-objects-api-server'; import { EsContext } from './es'; import { IEventLogClient } from './types'; import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter'; @@ -138,6 +139,32 @@ export class EventLogClient implements IEventLogClient { }); } + public async aggregateEventsWithAuthFilter( + type: string, + authFilter: KueryNode, + options?: AggregateOptionsType + ) { + if (!authFilter) { + throw new Error('No authorization filter defined!'); + } + + const aggs = options?.aggs; + if (!aggs) { + throw new Error('No aggregation defined!'); + } + + // validate other query options separately from + const aggregateOptions = queryOptionsSchema.validate(omit(options, 'aggs') ?? {}); + + return await this.esContext.esAdapter.aggregateEventsWithAuthFilter({ + index: this.esContext.esNames.indexPattern, + namespace: await this.getNamespace(), + type, + authFilter, + aggregateOptions: { ...aggregateOptions, aggs } as AggregateOptionsType, + }); + } + private async getNamespace() { const space = await this.spacesService?.getActiveSpace(this.request); return space && this.spacesService?.spaceIdToNamespace(space.id); diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index 3291f162c09df..d610a8bff9c2a 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -7,6 +7,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import type { IRouter, KibanaRequest, CustomRequestHandlerContext } from '@kbn/core/server'; +import { KueryNode } from '@kbn/es-query'; export type { IEvent, IValidatedEvent } from '../generated/schemas'; export { EventSchema, ECS_VERSION } from '../generated/schemas'; @@ -62,6 +63,11 @@ export interface IEventLogClient { options?: Partial, legacyIds?: string[] ): Promise; + aggregateEventsWithAuthFilter( + type: string, + authFilter: KueryNode, + options?: Partial + ): Promise; } export interface IEventLogger { diff --git a/x-pack/plugins/files/server/routes/common.test.ts b/x-pack/plugins/files/server/routes/common.test.ts index a8a1a5403c891..2c4d302d04625 100644 --- a/x-pack/plugins/files/server/routes/common.test.ts +++ b/x-pack/plugins/files/server/routes/common.test.ts @@ -20,6 +20,7 @@ describe('getDownloadHeadersForFile', () => { 'content-type': contentType, 'content-disposition': `attachment; filename="${contentDisposition}"`, 'cache-control': 'max-age=31536000, immutable', + 'x-content-type-options': 'nosniff', }; } diff --git a/x-pack/plugins/files/server/routes/common.ts b/x-pack/plugins/files/server/routes/common.ts index 8bfc7753efe3f..0730a6435de02 100644 --- a/x-pack/plugins/files/server/routes/common.ts +++ b/x-pack/plugins/files/server/routes/common.ts @@ -15,6 +15,8 @@ export function getDownloadHeadersForFile(file: File, fileName?: string): Respon // Note, this name can be overridden by the client if set via a "download" attribute on the HTML tag. 'content-disposition': `attachment; filename="${fileName || getDownloadedFileName(file)}"`, 'cache-control': 'max-age=31536000, immutable', + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options + 'x-content-type-options': 'nosniff', }; } diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 9b1789ed6c5d5..1f953e9010d9b 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -2731,28 +2731,37 @@ } }, "requestBody": { + "description": "You should use inputs as an object and not use the deprecated inputs array.", "content": { "application/json": { "schema": { - "allOf": [ + "oneOf": [ { - "$ref": "#/components/schemas/new_package_policy" + "$ref": "#/components/schemas/simplified_package_policy" }, { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - { - "type": "object", - "properties": { - "force": { - "type": "boolean" + "deprecated": true, + "allOf": [ + { + "$ref": "#/components/schemas/new_package_policy" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "force": { + "type": "boolean" + } + } } - } + ] } ] } @@ -3054,18 +3063,25 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "item": { - "$ref": "#/components/schemas/package_policy" + "oneOf": [ + { + "$ref": "#/components/schemas/simplified_package_policy" }, - "sucess": { - "type": "boolean" + { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/package_policy" + }, + "sucess": { + "type": "boolean" + } + }, + "required": [ + "item", + "sucess" + ] } - }, - "required": [ - "item", - "sucess" ] } } @@ -4335,6 +4351,72 @@ "warning" ] }, + "agent_component_status": { + "title": "Agent component status", + "type": "string", + "enum": [ + "starting", + "configuring", + "healthy", + "degraded", + "failed", + "stopping", + "stopped" + ] + }, + "agent_component_unit_type": { + "title": "Agent component unit type", + "type": "string", + "enum": [ + "input", + "output" + ] + }, + "agent_component_unit": { + "title": "Agent component unit", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/agent_component_unit_type" + }, + "status": { + "$ref": "#/components/schemas/agent_component_status" + }, + "message": { + "type": "string" + }, + "payload": { + "type": "object" + } + } + }, + "agent_component": { + "title": "Agent component", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/agent_component_status" + }, + "message": { + "type": "string" + }, + "units": { + "type": "array", + "items": { + "$ref": "#/components/schemas/agent_component_unit" + } + } + } + }, "agent": { "title": "Agent", "type": "object", @@ -4386,6 +4468,12 @@ }, "default_api_key": { "type": "string" + }, + "components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/agent_component" + } } }, "required": [ @@ -4841,6 +4929,96 @@ "created_at" ] }, + "simplified_package_policy": { + "title": "Simplified Package Policy", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Package policy unique identifier" + }, + "name": { + "type": "string", + "description": "Package policy name (should be unique)" + }, + "description": { + "type": "string", + "description": "Package policy description" + }, + "namespace": { + "type": "string", + "description": "namespace by default \"default\"" + }, + "policy_id": { + "type": "string", + "description": "Agent policy ID where that package policy will be added" + }, + "package": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Package name" + }, + "version": { + "type": "string", + "description": "Package version" + } + }, + "required": [ + "name", + "version" + ] + }, + "vars": { + "type": "object", + "description": "Package root level variable (see integration documentation for more information)" + }, + "inputs": { + "type": "object", + "description": "Package policy inputs (see integration documentation to know what inputs are available)", + "additionalProperties": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "enable or disable that input, (default to true)" + }, + "vars": { + "type": "object", + "description": "Input level variable (see integration documentation for more information)" + }, + "streams": { + "type": "object", + "description": "Input streams (see integration documentation to know what streams are available)", + "additionalProperties": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "enable or disable that stream, (default to true)" + }, + "vars": { + "type": "object", + "description": "Stream level variable (see integration documentation for more information)" + } + } + } + } + } + } + }, + "force": { + "type": "boolean", + "description": "Force package policy creation even if package is not verified, or if the agent policy is managed." + } + }, + "required": [ + "name", + "policy_id", + "package" + ] + }, "upgrade_diff": { "title": "Package policy Upgrade dryrun", "type": "array", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 906b039e83509..b52604ab73d72 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -1686,19 +1686,25 @@ paths: required: - item requestBody: + description: >- + You should use inputs as an object and not use the deprecated inputs + array. content: application/json: schema: - allOf: - - $ref: '#/components/schemas/new_package_policy' - - type: object - properties: - id: - type: string - - type: object - properties: - force: - type: boolean + oneOf: + - $ref: '#/components/schemas/simplified_package_policy' + - deprecated: true + allOf: + - $ref: '#/components/schemas/new_package_policy' + - type: object + properties: + id: + type: string + - type: object + properties: + force: + type: boolean parameters: - $ref: '#/components/parameters/kbn_xsrf' /package_policies/_bulk_get: @@ -1884,15 +1890,17 @@ paths: content: application/json: schema: - type: object - properties: - item: - $ref: '#/components/schemas/package_policy' - sucess: - type: boolean - required: - - item - - sucess + oneOf: + - $ref: '#/components/schemas/simplified_package_policy' + - type: object + properties: + item: + $ref: '#/components/schemas/package_policy' + sucess: + type: boolean + required: + - item + - sucess parameters: - $ref: '#/components/parameters/kbn_xsrf' delete: @@ -2727,6 +2735,53 @@ components: - online - inactive - warning + agent_component_status: + title: Agent component status + type: string + enum: + - starting + - configuring + - healthy + - degraded + - failed + - stopping + - stopped + agent_component_unit_type: + title: Agent component unit type + type: string + enum: + - input + - output + agent_component_unit: + title: Agent component unit + type: object + properties: + id: + type: string + type: + $ref: '#/components/schemas/agent_component_unit_type' + status: + $ref: '#/components/schemas/agent_component_status' + message: + type: string + payload: + type: object + agent_component: + title: Agent component + type: object + properties: + id: + type: string + type: + type: string + status: + $ref: '#/components/schemas/agent_component_status' + message: + type: string + units: + type: array + items: + $ref: '#/components/schemas/agent_component_unit' agent: title: Agent type: object @@ -2763,6 +2818,10 @@ components: $ref: '#/components/schemas/agent_status' default_api_key: type: string + components: + type: array + items: + $ref: '#/components/schemas/agent_component' required: - type - active @@ -3062,6 +3121,83 @@ components: - api_key - active - created_at + simplified_package_policy: + title: Simplified Package Policy + type: object + properties: + id: + type: string + description: Package policy unique identifier + name: + type: string + description: Package policy name (should be unique) + description: + type: string + description: Package policy description + namespace: + type: string + description: namespace by default "default" + policy_id: + type: string + description: Agent policy ID where that package policy will be added + package: + type: object + properties: + name: + type: string + description: Package name + version: + type: string + description: Package version + required: + - name + - version + vars: + type: object + description: >- + Package root level variable (see integration documentation for more + information) + inputs: + type: object + description: >- + Package policy inputs (see integration documentation to know what + inputs are available) + additionalProperties: + type: object + properties: + enabled: + type: boolean + description: enable or disable that input, (default to true) + vars: + type: object + description: >- + Input level variable (see integration documentation for more + information) + streams: + type: object + description: >- + Input streams (see integration documentation to know what + streams are available) + additionalProperties: + type: object + properties: + enabled: + type: boolean + description: enable or disable that stream, (default to true) + vars: + type: object + description: >- + Stream level variable (see integration documentation for + more information) + force: + type: boolean + description: >- + Force package policy creation even if package is not verified, or if + the agent policy is managed. + required: + - name + - policy_id + - package upgrade_diff: title: Package policy Upgrade dryrun type: array diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/simplified_package_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/simplified_package_policy.yaml new file mode 100644 index 0000000000000..6ff537386f05b --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/simplified_package_policy.yaml @@ -0,0 +1,64 @@ +title: Simplified Package Policy +type: object +properties: + id: + type: string + description: Package policy unique identifier + name: + type: string + description: Package policy name (should be unique) + description: + type: string + description: Package policy description + namespace: + type: string + description: namespace by default "default" + policy_id: + type: string + description: Agent policy ID where that package policy will be added + package: + type: object + properties: + name: + type: string + description: Package name + version: + type: string + description: Package version + required: + - name + - version + vars: + type: object + description: Package root level variable (see integration documentation for more information) + inputs: + type: object + description: Package policy inputs (see integration documentation to know what inputs are available) + additionalProperties: + type: object + properties: + enabled: + type: boolean + description: enable or disable that input, (default to true) + vars: + type: object + description: Input level variable (see integration documentation for more information) + streams: + type: object + description: Input streams (see integration documentation to know what streams are available) + additionalProperties: + type: object + properties: + enabled: + type: boolean + description: enable or disable that stream, (default to true) + vars: + type: object + description: Stream level variable (see integration documentation for more information) + force: + type: boolean + description: Force package policy creation even if package is not verified, or if the agent policy is managed. +required: + - name + - policy_id + - package diff --git a/x-pack/plugins/fleet/common/openapi/paths/package_policies.yaml b/x-pack/plugins/fleet/common/openapi/paths/package_policies.yaml index 1d1263f16b01d..10e4fba447bc5 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/package_policies.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/package_policies.yaml @@ -41,18 +41,23 @@ post: required: - item requestBody: + description: You should use inputs as an object and not use the deprecated inputs array. content: application/json: schema: - allOf: - - $ref: ../components/schemas/new_package_policy.yaml - - type: object - properties: - id: - type: string - - type: object - properties: - force: - type: boolean + oneOf: + - $ref: ../components/schemas/simplified_package_policy.yaml + # Using inputs as an array is deprecated + - deprecated: true + allOf: + - $ref: ../components/schemas/new_package_policy.yaml + - type: object + properties: + id: + type: string + - type: object + properties: + force: + type: boolean parameters: - $ref: ../components/headers/kbn_xsrf.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml b/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml index 8dda212c409eb..bf3e0e0a6eeff 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml @@ -34,15 +34,17 @@ put: content: application/json: schema: - type: object - properties: - item: - $ref: ../components/schemas/package_policy.yaml - sucess: - type: boolean - required: - - item - - sucess + oneOf: + - $ref: ../components/schemas/simplified_package_policy.yaml + - type: object + properties: + item: + $ref: ../components/schemas/package_policy.yaml + sucess: + type: boolean + required: + - item + - sucess parameters: - $ref: ../components/headers/kbn_xsrf.yaml delete: diff --git a/x-pack/plugins/fleet/common/services/agent_status.ts b/x-pack/plugins/fleet/common/services/agent_status.ts index 7dbfb88192da8..7d7b11f5d8a5d 100644 --- a/x-pack/plugins/fleet/common/services/agent_status.ts +++ b/x-pack/plugins/fleet/common/services/agent_status.ts @@ -41,7 +41,7 @@ export function getAgentStatus(agent: Agent | FleetServerAgent): AgentStatus { ? agent.policy_revision_idx : undefined; - if (!policyRevision || (agent.upgrade_started_at && !agent.upgraded_at)) { + if (!policyRevision || (agent.upgrade_started_at && agent.upgrade_status !== 'completed')) { return 'updating'; } if (intervalsSinceLastCheckIn >= offlineTimeoutIntervalCount) { @@ -78,7 +78,7 @@ export function buildKueryForOfflineAgents(path: string = '') { } export function buildKueryForUpgradingAgents(path: string = '') { - return `(${path}upgrade_started_at:*) and not (${path}upgraded_at:*)`; + return `(${path}upgrade_started_at:*) and not (${path}upgrade_status:completed)`; } export function buildKueryForUpdatingAgents(path: string = '') { diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts index 2a523d1a2eabb..c4c9fa7e75a69 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts @@ -25,7 +25,7 @@ export function isAgentUpgradeable(agent: Agent, kibanaVersion: string, versionT return false; } // check that the agent is not already in the process of updating - if (agent.upgrade_started_at && !agent.upgraded_at) { + if (agent.upgrade_started_at && agent.upgrade_status !== 'completed') { return false; } if (versionToUpgrade !== undefined) { diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 3bcadfc863f84..9924413cb16bf 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -67,8 +67,9 @@ interface AgentBase { enrolled_at: string; unenrolled_at?: string; unenrollment_started_at?: string; - upgraded_at?: string | null; + upgraded_at?: string; upgrade_started_at?: string | null; + upgrade_status?: 'started' | 'completed'; access_api_key_id?: string; default_api_key?: string; default_api_key_id?: string; @@ -158,11 +159,15 @@ export interface FleetServerAgent { /** * Date/time the Elastic Agent was last upgraded */ - upgraded_at?: string | null; + upgraded_at?: string; /** * Date/time the Elastic Agent started the current upgrade */ upgrade_started_at?: string | null; + /** + * Upgrade status + */ + upgrade_status?: 'started' | 'completed'; /** * ID of the API key the Elastic Agent must used to contact Fleet Server */ @@ -197,7 +202,7 @@ export interface FleetServerAgent { */ last_checkin?: string; /** - * Lst checkin status + * Last checkin status */ last_checkin_status?: 'error' | 'online' | 'degraded' | 'updating'; /** diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 9d1cbac194bcb..657e802844bc6 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -37,8 +37,11 @@ import type { } from '../../../common/types'; import { installationStatuses } from '../../../common/constants'; import { defaultIngestErrorHandler, PackagePolicyNotFoundError } from '../../errors'; -import { getInstallations } from '../../services/epm/packages'; +import { getInstallations, getPackageInfo } from '../../services/epm/packages'; import { PACKAGES_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../constants'; +import { simplifiedPackagePolicytoNewPackagePolicy } from '../../services/package_policies/simplified_package_policy_helper'; + +import type { SimplifiedPackagePolicy } from '../../services/package_policies/simplified_package_policy_helper'; export const getPackagePoliciesHandler: RequestHandler< undefined, @@ -163,6 +166,17 @@ export const getOrphanedPackagePolicies: RequestHandler = } }; +function isSimplifiedCreatePackagePolicyRequest( + body: Omit, 'force' | 'package'> +): body is SimplifiedPackagePolicy { + // If `inputs` is not defined or if it's a non-array, the request body is using the new simplified API + if (body.inputs && Array.isArray(body.inputs)) { + return false; + } + + return true; +} + export const createPackagePolicyHandler: FleetRequestHandler< undefined, undefined, @@ -173,15 +187,30 @@ export const createPackagePolicyHandler: FleetRequestHandler< const soClient = fleetContext.epm.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; - const { force, ...newPolicy } = request.body; - // TODO Remove deprecated APIs https://github.com/elastic/kibana/issues/121485 - delete newPolicy.output_id; + const { force, package: pkg, ...newPolicy } = request.body; + if ('output_id' in newPolicy) { + // TODO Remove deprecated APIs https://github.com/elastic/kibana/issues/121485 + delete newPolicy.output_id; + } const spaceId = fleetContext.spaceId; try { - const newPackagePolicy = await packagePolicyService.enrichPolicyWithDefaultsFromPackage( - soClient, - newPolicy as NewPackagePolicy - ); + let newPackagePolicy: NewPackagePolicy; + if (isSimplifiedCreatePackagePolicyRequest(newPolicy)) { + if (!pkg) { + throw new Error('Package is required'); + } + const pkgInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: pkg.name, + pkgVersion: pkg.version, + }); + newPackagePolicy = simplifiedPackagePolicytoNewPackagePolicy(newPolicy, pkgInfo); + } else { + newPackagePolicy = await packagePolicyService.enrichPolicyWithDefaultsFromPackage(soClient, { + ...newPolicy, + package: pkg, + } as NewPackagePolicy); + } const newData = await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', @@ -235,37 +264,58 @@ export const updatePackagePolicyHandler: RequestHandler< throw Boom.notFound('Package policy not found'); } - const { force, ...body } = request.body; - // TODO Remove deprecated APIs https://github.com/elastic/kibana/issues/121485 - delete body.output_id; - // removed fields not recognized by schema - const packagePolicyInputs = packagePolicy.inputs.map((input) => { - const newInput = { - ...input, - streams: input.streams.map((stream) => { - const newStream = { ...stream }; - delete newStream.compiled_stream; - return newStream; - }), - }; - delete newInput.compiled_input; - return newInput; - }); - - // listing down accepted properties, because loaded packagePolicy contains some that are not accepted in update - let newData = { - ...body, - name: body.name ?? packagePolicy.name, - description: body.description ?? packagePolicy.description, - namespace: body.namespace ?? packagePolicy.namespace, - policy_id: body.policy_id ?? packagePolicy.policy_id, - enabled: body.enabled ?? packagePolicy.enabled, - package: body.package ?? packagePolicy.package, - inputs: body.inputs ?? packagePolicyInputs, - vars: body.vars ?? packagePolicy.vars, - } as NewPackagePolicy; - try { + const { force, package: pkg, ...body } = request.body; + // TODO Remove deprecated APIs https://github.com/elastic/kibana/issues/121485 + if ('output_id' in body) { + delete body.output_id; + } + + let newData: NewPackagePolicy; + + if ( + body.inputs && + isSimplifiedCreatePackagePolicyRequest(body as unknown as SimplifiedPackagePolicy) + ) { + if (!pkg) { + throw new Error('package is required'); + } + const pkgInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: pkg.name, + pkgVersion: pkg.version, + }); + newData = simplifiedPackagePolicytoNewPackagePolicy( + body as unknown as SimplifiedPackagePolicy, + pkgInfo + ); + } else { + // removed fields not recognized by schema + const packagePolicyInputs = packagePolicy.inputs.map((input) => { + const newInput = { + ...input, + streams: input.streams.map((stream) => { + const newStream = { ...stream }; + delete newStream.compiled_stream; + return newStream; + }), + }; + delete newInput.compiled_input; + return newInput; + }); + // listing down accepted properties, because loaded packagePolicy contains some that are not accepted in update + newData = { + ...body, + name: body.name ?? packagePolicy.name, + description: body.description ?? packagePolicy.description, + namespace: body.namespace ?? packagePolicy.namespace, + policy_id: body.policy_id ?? packagePolicy.policy_id, + enabled: 'enabled' in body ? body.enabled ?? packagePolicy.enabled : packagePolicy.enabled, + package: pkg ?? packagePolicy.package, + inputs: body.inputs ?? packagePolicyInputs, + vars: body.vars ?? packagePolicy.vars, + } as NewPackagePolicy; + } newData = await packagePolicyService.runExternalCallbacks( 'packagePolicyUpdate', newData, diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index c4f3530892543..f0e2d059a98a8 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -139,8 +139,8 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: hit._source.agents.map((agentId) => ({ agentId, data: { - upgraded_at: null, upgrade_started_at: null, + upgrade_status: 'completed', }, })) ); diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.ts index efe363b2b46b8..1083e8f728ee1 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.ts @@ -67,8 +67,8 @@ export async function sendUpgradeAgentAction({ type: 'UPGRADE', }); await updateAgent(esClient, agentId, { - upgraded_at: null, upgrade_started_at: now, + upgrade_status: 'started', }); } @@ -207,8 +207,8 @@ async function upgradeBatch( agentsToUpdate.map((agent) => ({ agentId: agent.id, data: { - upgraded_at: null, upgrade_started_at: now, + upgrade_status: 'started', }, })) ); diff --git a/x-pack/plugins/fleet/server/services/package_policies/__snapshots__/simplified_package_policy_helper.test.ts.snap b/x-pack/plugins/fleet/server/services/package_policies/__snapshots__/simplified_package_policy_helper.test.ts.snap new file mode 100644 index 0000000000000..200eed919407e --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/__snapshots__/simplified_package_policy_helper.test.ts.snap @@ -0,0 +1,239 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`toPackagePolicy With nginx package should work 1`] = ` +Object { + "description": "Test description", + "enabled": true, + "inputs": Array [ + Object { + "enabled": true, + "policy_template": "nginx", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "nginx.access", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "ignore_older": Object { + "type": "text", + "value": "72h", + }, + "paths": Object { + "type": "text", + "value": Array [ + "/var/log/nginx/access.log*", + ], + }, + "preserve_original_event": Object { + "type": "bool", + "value": false, + }, + "processors": Object { + "type": "yaml", + "value": undefined, + }, + "tags": Object { + "type": "text", + "value": Array [ + "nginx-access", + ], + }, + }, + }, + Object { + "data_stream": Object { + "dataset": "nginx.error", + "type": "logs", + }, + "enabled": true, + "vars": Object { + "ignore_older": Object { + "type": "text", + "value": "72h", + }, + "paths": Object { + "type": "text", + "value": Array [ + "/var/log/nginx/error.log*", + ], + }, + "preserve_original_event": Object { + "type": "bool", + "value": false, + }, + "processors": Object { + "type": "yaml", + "value": undefined, + }, + "tags": Object { + "type": "text", + "value": Array [ + "test", + "nginx-error", + ], + }, + }, + }, + ], + "type": "logfile", + }, + Object { + "enabled": false, + "policy_template": "nginx", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "nginx.access", + "type": "logs", + }, + "enabled": false, + "vars": Object { + "interval": Object { + "type": "text", + "value": "10s", + }, + "preserve_original_event": Object { + "type": "bool", + "value": false, + }, + "processors": Object { + "type": "yaml", + "value": undefined, + }, + "search": Object { + "type": "text", + "value": "search sourcetype=nginx:plus:access", + }, + "tags": Object { + "type": "text", + "value": Array [ + "forwarded", + "nginx-access", + ], + }, + }, + }, + Object { + "data_stream": Object { + "dataset": "nginx.error", + "type": "logs", + }, + "enabled": false, + "vars": Object { + "interval": Object { + "type": "text", + "value": "10s", + }, + "preserve_original_event": Object { + "type": "bool", + "value": false, + }, + "processors": Object { + "type": "yaml", + "value": undefined, + }, + "search": Object { + "type": "text", + "value": "search sourcetype=nginx:plus:error", + }, + "tags": Object { + "type": "text", + "value": Array [ + "forwarded", + "nginx-error", + ], + }, + }, + }, + ], + "type": "httpjson", + "vars": Object { + "password": Object { + "type": "password", + "value": undefined, + }, + "ssl": Object { + "type": "yaml", + "value": "#certificate_authorities: +# - | +# -----BEGIN CERTIFICATE----- +# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF +# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2 +# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB +# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n +# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl +# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t +# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP +# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41 +# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O +# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux +# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D +# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw +# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA +# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu +# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0 +# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk +# sxSmbIUfc2SGJGCJD4I= +# -----END CERTIFICATE----- +", + }, + "token": Object { + "type": "password", + "value": undefined, + }, + "url": Object { + "type": "text", + "value": "https://server.example.com:8089", + }, + "username": Object { + "type": "text", + "value": undefined, + }, + }, + }, + Object { + "enabled": true, + "policy_template": "nginx", + "streams": Array [ + Object { + "data_stream": Object { + "dataset": "nginx.stubstatus", + "type": "metrics", + }, + "enabled": true, + "vars": Object { + "period": Object { + "type": "text", + "value": "10s", + }, + "server_status_path": Object { + "type": "text", + "value": "/nginx_status", + }, + }, + }, + ], + "type": "nginx/metrics", + "vars": Object { + "hosts": Object { + "type": "text", + "value": Array [ + "http://127.0.0.1:80", + ], + }, + }, + }, + ], + "name": "nginx-1", + "namespace": "default", + "package": Object { + "name": "nginx", + "title": "Nginx", + "version": "1.5.0", + }, + "policy_id": "policy123", + "vars": undefined, +} +`; diff --git a/x-pack/plugins/fleet/server/services/package_policies/fixtures/package_info/nginx_1.5.0.json b/x-pack/plugins/fleet/server/services/package_policies/fixtures/package_info/nginx_1.5.0.json new file mode 100644 index 0000000000000..ad2418c052120 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/fixtures/package_info/nginx_1.5.0.json @@ -0,0 +1,658 @@ +{ + "name": "nginx", + "title": "Nginx", + "version": "1.5.0", + "release": "ga", + "description": "Collect logs and metrics from Nginx HTTP servers with Elastic Agent.", + "type": "integration", + "download": "/epr/nginx/nginx-1.5.0.zip", + "path": "/package/nginx/1.5.0", + "icons": [ + { + "src": "/img/logo_nginx.svg", + "path": "/package/nginx/1.5.0/img/logo_nginx.svg", + "title": "logo nginx", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "conditions": { "kibana": { "version": "^8.0.0" } }, + "owner": { "github": "elastic/obs-service-integrations" }, + "categories": ["web", "security"], + "format_version": "1.0.0", + "readme": "/package/nginx/1.5.0/docs/README.md", + "license": "basic", + "screenshots": [ + { + "src": "/img/nginx-metrics-overview.png", + "path": "/package/nginx/1.5.0/img/nginx-metrics-overview.png", + "title": "Nginx metrics overview", + "size": "3360x2302", + "type": "image/png" + }, + { + "src": "/img/nginx-logs-access-error.png", + "path": "/package/nginx/1.5.0/img/nginx-logs-access-error.png", + "title": "Nginx access and error logs", + "size": "3360x3590", + "type": "image/png" + }, + { + "src": "/img/nginx-logs-overview.png", + "path": "/package/nginx/1.5.0/img/nginx-logs-overview.png", + "title": "Nginx logs overview", + "size": "3360x3590", + "type": "image/png" + } + ], + "assets": { + "kibana": { + "dashboard": [ + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "dashboard", + "file": "nginx-023d2930-f1a5-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/dashboard/nginx-023d2930-f1a5-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "dashboard", + "file": "nginx-046212a0-a2a1-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/dashboard/nginx-046212a0-a2a1-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "dashboard", + "file": "nginx-55a9e6e0-a29e-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/dashboard/nginx-55a9e6e0-a29e-11e7-928f-5dbe6f6f5519.json" + } + ], + "map": [ + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "map", + "file": "nginx-4576daa0-e1da-11ec-baf0-970634a1784d.json", + "path": "nginx-1.5.0/kibana/map/nginx-4576daa0-e1da-11ec-baf0-970634a1784d.json" + } + ], + "ml_module": [ + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "ml_module", + "file": "nginx-Logs-ml.json", + "path": "nginx-1.5.0/kibana/ml_module/nginx-Logs-ml.json" + } + ], + "search": [ + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "search", + "file": "nginx-6d9e66d0-a1f0-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/search/nginx-6d9e66d0-a1f0-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "search", + "file": "nginx-9eb25600-a1f0-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/search/nginx-9eb25600-a1f0-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "search", + "file": "nginx-Logs-Nginx-integration.json", + "path": "nginx-1.5.0/kibana/search/nginx-Logs-Nginx-integration.json" + } + ], + "visualization": [ + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-0dd6f320-a29f-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-0dd6f320-a29f-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-1cfb1a80-a1f4-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-1cfb1a80-a1f4-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-46322e50-a1f6-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-46322e50-a1f6-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-47a8e0f0-f1a4-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-47a8e0f0-f1a4-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-555df8a0-f1a1-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-555df8a0-f1a1-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-7cc9ea40-3af8-11eb-94b7-0dab91df36a6.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-7cc9ea40-3af8-11eb-94b7-0dab91df36a6.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-823b3c80-3af9-11eb-94b7-0dab91df36a6.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-823b3c80-3af9-11eb-94b7-0dab91df36a6.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-9184fa00-a1f5-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-9184fa00-a1f5-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-9484ecf0-3af5-11eb-94b7-0dab91df36a6.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-9484ecf0-3af5-11eb-94b7-0dab91df36a6.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-97109780-a2a5-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-97109780-a2a5-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-Access-Browsers.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-Access-Browsers.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-Access-Map.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-Access-Map.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-Access-OSes.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-Access-OSes.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-a1d92240-f1a1-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-a1d92240-f1a1-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-b70b1b20-a1f4-11e7-928f-5dbe6f6f5519.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-b70b1b20-a1f4-11e7-928f-5dbe6f6f5519.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-d763a570-f1a1-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-d763a570-f1a1-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-dcbffe30-f1a4-11e7-a9ef-93c69af7b129.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-dcbffe30-f1a4-11e7-a9ef-93c69af7b129.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-e302b5a0-3afb-11eb-94b7-0dab91df36a6.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-e302b5a0-3afb-11eb-94b7-0dab91df36a6.json" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "kibana", + "type": "visualization", + "file": "nginx-ea7f9e10-3af6-11eb-94b7-0dab91df36a6.json", + "path": "nginx-1.5.0/kibana/visualization/nginx-ea7f9e10-3af6-11eb-94b7-0dab91df36a6.json" + } + ] + }, + "elasticsearch": { + "ingest_pipeline": [ + { + "pkgkey": "nginx-1.5.0", + "service": "elasticsearch", + "type": "ingest_pipeline", + "file": "default.yml", + "dataset": "access", + "path": "nginx-1.5.0/data_stream/access/elasticsearch/ingest_pipeline/default.yml" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "elasticsearch", + "type": "ingest_pipeline", + "file": "third-party.yml", + "dataset": "access", + "path": "nginx-1.5.0/data_stream/access/elasticsearch/ingest_pipeline/third-party.yml" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "elasticsearch", + "type": "ingest_pipeline", + "file": "default.yml", + "dataset": "error", + "path": "nginx-1.5.0/data_stream/error/elasticsearch/ingest_pipeline/default.yml" + }, + { + "pkgkey": "nginx-1.5.0", + "service": "elasticsearch", + "type": "ingest_pipeline", + "file": "third-party.yml", + "dataset": "error", + "path": "nginx-1.5.0/data_stream/error/elasticsearch/ingest_pipeline/third-party.yml" + } + ] + } + }, + "policy_templates": [ + { + "name": "nginx", + "title": "Nginx logs and metrics", + "description": "Collect logs and metrics from Nginx instances", + "inputs": [ + { + "type": "logfile", + "title": "Collect logs from Nginx instances", + "description": "Collecting Nginx access and error logs" + }, + { + "type": "httpjson", + "vars": [ + { + "name": "url", + "type": "text", + "title": "URL of Splunk Enterprise Server", + "description": "i.e. scheme://host:port, path is automatic", + "multi": false, + "required": true, + "show_user": true, + "default": "https://server.example.com:8089" + }, + { + "name": "username", + "type": "text", + "title": "Splunk REST API Username", + "multi": false, + "required": false, + "show_user": true + }, + { + "name": "password", + "type": "password", + "title": "Splunk REST API Password", + "multi": false, + "required": false, + "show_user": true + }, + { + "name": "token", + "type": "password", + "title": "Splunk Authorization Token", + "description": "Bearer Token or Session Key, e.g. \"Bearer eyJFd3e46...\"\nor \"Splunk 192fd3e...\". Cannot be used with username\nand password.\n", + "multi": false, + "required": false, + "show_user": true + }, + { + "name": "ssl", + "type": "yaml", + "title": "SSL Configuration", + "description": "i.e. certificate_authorities, supported_protocols, verification_mode etc.", + "multi": false, + "required": false, + "show_user": false, + "default": "#certificate_authorities:\n# - |\n# -----BEGIN CERTIFICATE-----\n# MIIDCjCCAfKgAwIBAgITJ706Mu2wJlKckpIvkWxEHvEyijANBgkqhkiG9w0BAQsF\n# ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTkwNzIyMTkyOTA0WhgPMjExOTA2\n# MjgxOTI5MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB\n# BQADggEPADCCAQoCggEBANce58Y/JykI58iyOXpxGfw0/gMvF0hUQAcUrSMxEO6n\n# fZRA49b4OV4SwWmA3395uL2eB2NB8y8qdQ9muXUdPBWE4l9rMZ6gmfu90N5B5uEl\n# 94NcfBfYOKi1fJQ9i7WKhTjlRkMCgBkWPkUokvBZFRt8RtF7zI77BSEorHGQCk9t\n# /D7BS0GJyfVEhftbWcFEAG3VRcoMhF7kUzYwp+qESoriFRYLeDWv68ZOvG7eoWnP\n# PsvZStEVEimjvK5NSESEQa9xWyJOmlOKXhkdymtcUd/nXnx6UTCFgnkgzSdTWV41\n# CI6B6aJ9svCTI2QuoIq2HxX/ix7OvW1huVmcyHVxyUECAwEAAaNTMFEwHQYDVR0O\n# BBYEFPwN1OceFGm9v6ux8G+DZ3TUDYxqMB8GA1UdIwQYMBaAFPwN1OceFGm9v6ux\n# 8G+DZ3TUDYxqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG5D\n# 874A4YI7YUwOVsVAdbWtgp1d0zKcPRR+r2OdSbTAV5/gcS3jgBJ3i1BN34JuDVFw\n# 3DeJSYT3nxy2Y56lLnxDeF8CUTUtVQx3CuGkRg1ouGAHpO/6OqOhwLLorEmxi7tA\n# H2O8mtT0poX5AnOAhzVy7QW0D/k4WaoLyckM5hUa6RtvgvLxOwA0U+VGurCDoctu\n# 8F4QOgTAWyh8EZIwaKCliFRSynDpv3JTUwtfZkxo6K6nce1RhCWFAsMvDZL8Dgc0\n# yvgJ38BRsFOtkRuAGSf6ZUwTO8JJRRIFnpUzXflAnGivK9M13D5GEQMmIl6U9Pvk\n# sxSmbIUfc2SGJGCJD4I=\n# -----END CERTIFICATE-----\n" + } + ], + "title": "Collect logs from third-party REST API (experimental)", + "description": "Collect logs from third-party REST API (experimental)" + }, + { + "type": "nginx/metrics", + "vars": [ + { + "name": "hosts", + "type": "text", + "title": "Hosts", + "multi": true, + "required": true, + "show_user": true, + "default": ["http://127.0.0.1:80"] + } + ], + "title": "Collect metrics from Nginx instances", + "description": "Collecting Nginx stub status metrics" + } + ], + "multiple": true + } + ], + "data_streams": [ + { + "type": "logs", + "dataset": "nginx.access", + "title": "Nginx access logs", + "release": "ga", + "ingest_pipeline": "default", + "streams": [ + { + "input": "logfile", + "vars": [ + { + "name": "paths", + "type": "text", + "title": "Paths", + "multi": true, + "required": true, + "show_user": true, + "default": ["/var/log/nginx/access.log*"] + }, + { + "name": "tags", + "type": "text", + "title": "Tags", + "multi": true, + "required": true, + "show_user": false, + "default": ["nginx-access"] + }, + { + "name": "preserve_original_event", + "type": "bool", + "title": "Preserve original event", + "description": "Preserves a raw copy of the original event, added to the field `event.original`", + "multi": false, + "required": true, + "show_user": true, + "default": false + }, + { + "name": "processors", + "type": "yaml", + "title": "Processors", + "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", + "multi": false, + "required": false, + "show_user": false + }, + { + "name": "ignore_older", + "type": "text", + "title": "Ignore events older than", + "description": "If this option is specified, events that are older than the specified amount of time are ignored. Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".", + "multi": false, + "required": false, + "show_user": false, + "default": "72h" + } + ], + "template_path": "stream.yml.hbs", + "title": "Nginx access logs", + "description": "Collect Nginx access logs", + "enabled": true + }, + { + "input": "httpjson", + "vars": [ + { + "name": "interval", + "type": "text", + "title": "Interval to query Splunk Enterprise REST API", + "description": "Go Duration syntax (eg. 10s)", + "multi": false, + "required": true, + "show_user": true, + "default": "10s" + }, + { + "name": "search", + "type": "text", + "title": "Splunk search string", + "multi": false, + "required": true, + "show_user": true, + "default": "search sourcetype=nginx:plus:access" + }, + { + "name": "tags", + "type": "text", + "title": "Tags", + "multi": true, + "required": false, + "show_user": false, + "default": ["forwarded", "nginx-access"] + }, + { + "name": "preserve_original_event", + "type": "bool", + "title": "Preserve original event", + "description": "Preserves a raw copy of the original event, added to the field `event.original`", + "multi": false, + "required": true, + "show_user": true, + "default": false + }, + { + "name": "processors", + "type": "yaml", + "title": "Processors", + "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.", + "multi": false, + "required": false, + "show_user": false + } + ], + "template_path": "httpjson.yml.hbs", + "title": "Nginx access logs via Splunk Enterprise REST API", + "description": "Collect Nginx access logs via Splunk Enterprise REST API", + "enabled": false + } + ], + "package": "nginx", + "path": "access" + }, + { + "type": "logs", + "dataset": "nginx.error", + "title": "Nginx error logs", + "release": "ga", + "ingest_pipeline": "default", + "streams": [ + { + "input": "logfile", + "vars": [ + { + "name": "paths", + "type": "text", + "title": "Paths", + "multi": true, + "required": true, + "show_user": true, + "default": ["/var/log/nginx/error.log*"] + }, + { + "name": "tags", + "type": "text", + "title": "Tags", + "multi": true, + "required": true, + "show_user": false, + "default": ["nginx-error"] + }, + { + "name": "preserve_original_event", + "type": "bool", + "title": "Preserve original event", + "description": "Preserves a raw copy of the original event, added to the field `event.original`", + "multi": false, + "required": true, + "show_user": true, + "default": false + }, + { + "name": "processors", + "type": "yaml", + "title": "Processors", + "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.\n", + "multi": false, + "required": false, + "show_user": false + }, + { + "name": "ignore_older", + "type": "text", + "title": "Ignore events older than", + "description": "If this option is specified, events that are older than the specified amount of time are ignored. Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".", + "multi": false, + "required": false, + "show_user": false, + "default": "72h" + } + ], + "template_path": "stream.yml.hbs", + "title": "Nginx error logs", + "description": "Collect Nginx error logs", + "enabled": true + }, + { + "input": "httpjson", + "vars": [ + { + "name": "interval", + "type": "text", + "title": "Interval to query REST API", + "description": "Go Duration syntax (eg. 10s)", + "multi": false, + "required": true, + "show_user": true, + "default": "10s" + }, + { + "name": "search", + "type": "text", + "title": "Search String", + "multi": false, + "required": true, + "show_user": true, + "default": "search sourcetype=nginx:plus:error" + }, + { + "name": "tags", + "type": "text", + "title": "Tags", + "multi": true, + "required": false, + "show_user": false, + "default": ["forwarded", "nginx-error"] + }, + { + "name": "preserve_original_event", + "type": "bool", + "title": "Preserve original event", + "description": "Preserves a raw copy of the original event, added to the field `event.original`", + "multi": false, + "required": true, + "show_user": true, + "default": false + }, + { + "name": "processors", + "type": "yaml", + "title": "Processors", + "description": "Processors are used to reduce the number of fields in the exported event or to enhance the event with metadata. This executes in the agent before the logs are parsed. See [Processors](https://www.elastic.co/guide/en/beats/filebeat/current/filtering-and-enhancing-data.html) for details.", + "multi": false, + "required": false, + "show_user": false + } + ], + "template_path": "httpjson.yml.hbs", + "title": "Nginx error logs via Splunk REST API", + "description": "Collect Nginx error logs via Splunk REST API", + "enabled": false + } + ], + "package": "nginx", + "path": "error" + }, + { + "type": "metrics", + "dataset": "nginx.stubstatus", + "title": "Nginx stubstatus metrics", + "release": "ga", + "streams": [ + { + "input": "nginx/metrics", + "vars": [ + { + "name": "period", + "type": "text", + "title": "Period", + "multi": false, + "required": true, + "show_user": true, + "default": "10s" + }, + { + "name": "server_status_path", + "type": "text", + "title": "Server Status Path", + "multi": false, + "required": true, + "show_user": false, + "default": "/nginx_status" + } + ], + "template_path": "stream.yml.hbs", + "title": "Nginx stub status metrics", + "description": "Collect Nginx stub status metrics", + "enabled": true + } + ], + "package": "nginx", + "path": "stubstatus" + } + ], + "latestVersion": "1.5.0", + "keepPoliciesUpToDate": false, + "status": "not_installed" +} diff --git a/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.test.ts b/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.test.ts new file mode 100644 index 0000000000000..15fd7c902b02e --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.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 type { NewPackagePolicy, PackageInfo } from '../../types'; + +import { + simplifiedPackagePolicytoNewPackagePolicy, + generateInputId, +} from './simplified_package_policy_helper'; +import nginxPackageInfo from './fixtures/package_info/nginx_1.5.0.json'; + +function getEnabledInputsAndStreams(newPackagePolicy: NewPackagePolicy) { + return newPackagePolicy.inputs + .filter((input) => input.enabled) + .reduce((acc, input) => { + const inputId = generateInputId(input); + + acc[inputId] = input.streams + .filter((stream) => stream.enabled) + .map((stream) => stream.data_stream.dataset); + + return acc; + }, {} as Record); +} + +describe('toPackagePolicy', () => { + describe('With nginx package', () => { + it('should work', () => { + const res = simplifiedPackagePolicytoNewPackagePolicy( + { + name: 'nginx-1', + namespace: 'default', + policy_id: 'policy123', + description: 'Test description', + inputs: { + 'nginx-logfile': { + streams: { + 'nginx.error': { + vars: { + tags: ['test', 'nginx-error'], + }, + }, + }, + }, + 'nginx-nginx/metrics': {}, + }, + }, + nginxPackageInfo as unknown as PackageInfo + ); + + expect(res).toMatchSnapshot(); + }); + + it('should enable default inputs streams', () => { + const res = simplifiedPackagePolicytoNewPackagePolicy( + { + name: 'nginx-1', + namespace: 'default', + policy_id: 'policy123', + description: 'Test description', + }, + nginxPackageInfo as unknown as PackageInfo + ); + + expect(getEnabledInputsAndStreams(res)).toEqual({ + 'nginx-logfile': ['nginx.access', 'nginx.error'], + 'nginx-nginx/metrics': ['nginx.stubstatus'], + }); + }); + + it('should allow user to disable inputs', () => { + const res = simplifiedPackagePolicytoNewPackagePolicy( + { + name: 'nginx-1', + namespace: 'default', + policy_id: 'policy123', + description: 'Test description', + inputs: { + 'nginx-logfile': { enabled: false }, + }, + }, + nginxPackageInfo as unknown as PackageInfo + ); + + expect(getEnabledInputsAndStreams(res)).toEqual({ + 'nginx-nginx/metrics': ['nginx.stubstatus'], + }); + }); + + it('should allow user to disable streams', () => { + const res = simplifiedPackagePolicytoNewPackagePolicy( + { + name: 'nginx-1', + namespace: 'default', + policy_id: 'policy123', + description: 'Test description', + inputs: { + 'nginx-logfile': { + streams: { + 'nginx.error': { enabled: false }, + }, + }, + }, + }, + nginxPackageInfo as unknown as PackageInfo + ); + + expect(getEnabledInputsAndStreams(res)).toEqual({ + 'nginx-logfile': ['nginx.access'], + 'nginx-nginx/metrics': ['nginx.stubstatus'], + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.ts b/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.ts new file mode 100644 index 0000000000000..12db049aef71e --- /dev/null +++ b/x-pack/plugins/fleet/server/services/package_policies/simplified_package_policy_helper.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 { packageToPackagePolicy } from '../../../common/services'; +import type { + NewPackagePolicyInput, + NewPackagePolicyInputStream, + PackagePolicyConfigRecord, +} from '../../../common/types'; +import { PackagePolicyValidationError } from '../../errors'; +import type { NewPackagePolicy, PackageInfo } from '../../types'; + +type SimplifiedVars = Record; + +export interface SimplifiedPackagePolicy { + id?: string; + policy_id: string; + namespace: string; + name: string; + description?: string; + vars?: SimplifiedVars; + inputs?: Record< + string, + { + enabled?: boolean | undefined; + vars?: SimplifiedVars; + streams?: Record< + string, + { + enabled?: undefined | boolean; + vars?: SimplifiedVars; + } + >; + } + >; +} + +export function generateInputId(input: NewPackagePolicyInput) { + return `${input.policy_template ? `${input.policy_template}-` : ''}${input.type}`; +} + +function assignVariables( + userProvidedVars: SimplifiedVars, + varsRecord?: PackagePolicyConfigRecord, + ctxMessage = '' +) { + Object.entries(userProvidedVars).forEach(([varKey, varValue]) => { + if (!varsRecord || !varsRecord[varKey]) { + throw new PackagePolicyValidationError(`Variable ${ctxMessage}:${varKey} not found`); + } + + varsRecord[varKey].value = varValue; + }); +} + +type StreamsMap = Map; +type InputMap = Map; + +export function simplifiedPackagePolicytoNewPackagePolicy( + data: SimplifiedPackagePolicy, + packageInfo: PackageInfo +): NewPackagePolicy { + const { + policy_id: policyId, + namespace, + name, + description, + inputs = {}, + vars: packageLevelVars, + } = data; + const packagePolicy = packageToPackagePolicy(packageInfo, policyId, namespace, name, description); + + // Build a input and streams Map to easily find package policy stream + const inputMap: InputMap = new Map(); + packagePolicy.inputs.forEach((input) => { + const streamMap: StreamsMap = new Map(); + input.streams.forEach((stream) => { + streamMap.set(stream.data_stream.dataset, stream); + }); + inputMap.set(generateInputId(input), { input, streams: streamMap }); + }); + + if (packageLevelVars) { + assignVariables(packageLevelVars, packagePolicy.vars); + } + + Object.entries(inputs).forEach(([inputId, val]) => { + const { enabled, streams = {}, vars: inputLevelVars } = val; + + const { input: packagePolicyInput, streams: streamsMap } = inputMap.get(inputId) ?? {}; + if (!packagePolicyInput || !streamsMap) { + throw new PackagePolicyValidationError(`Input not found: ${inputId}`); + } + + if (enabled === false) { + packagePolicyInput.enabled = false; + } else { + packagePolicyInput.enabled = true; + } + + if (inputLevelVars) { + assignVariables(inputLevelVars, packagePolicyInput.vars, `${inputId}`); + } + + Object.entries(streams).forEach(([streamId, streamVal]) => { + const { enabled: streamEnabled, vars: streamsLevelVars } = streamVal; + const packagePolicyStream = streamsMap.get(streamId); + if (!packagePolicyStream) { + throw new PackagePolicyValidationError(`Stream not found ${inputId}: ${streamId}`); + } + + if (streamEnabled === false) { + packagePolicyStream.enabled = false; + } else { + packagePolicyStream.enabled = true; + } + + if (streamsLevelVars) { + assignVariables(streamsLevelVars, packagePolicyStream.vars, `${inputId} ${streamId}`); + } + }); + }); + + return packagePolicy; +} diff --git a/x-pack/plugins/fleet/server/types/models/package_policy.ts b/x-pack/plugins/fleet/server/types/models/package_policy.ts index afceb08b3e89e..5220f18380dd1 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -129,6 +129,51 @@ export const CreatePackagePolicyRequestBodySchema = schema.object({ force: schema.maybe(schema.boolean()), }); +const SimplifiedVarsSchema = schema.recordOf( + schema.string(), + schema.nullable( + schema.oneOf([ + schema.boolean(), + schema.string(), + schema.number(), + schema.arrayOf(schema.string()), + schema.arrayOf(schema.number()), + ]) + ) +); + +export const SimplifiedCreatePackagePolicyRequestBodySchema = schema.object({ + id: schema.maybe(schema.string()), + name: schema.string(), + description: schema.maybe(schema.string()), + policy_id: schema.string(), + namespace: schema.string({ defaultValue: 'default' }), + package: schema.object({ + name: schema.string(), + version: schema.string(), + }), + force: schema.maybe(schema.boolean()), + vars: schema.maybe(SimplifiedVarsSchema), + inputs: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + enabled: schema.maybe(schema.boolean()), + vars: schema.maybe(SimplifiedVarsSchema), + streams: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + enabled: schema.maybe(schema.boolean()), + vars: schema.maybe(SimplifiedVarsSchema), + }) + ) + ), + }) + ) + ), +}); + export const UpdatePackagePolicyRequestBodySchema = schema.object({ ...CreatePackagePolicyProps, name: schema.maybe(schema.string()), diff --git a/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts b/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts index fa7cdce92400b..0f5542dd7e9bb 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { CreatePackagePolicyRequestBodySchema, + SimplifiedCreatePackagePolicyRequestBodySchema, UpdatePackagePolicyRequestBodySchema, } from '../models'; @@ -29,12 +30,18 @@ export const GetOnePackagePolicyRequestSchema = { }; export const CreatePackagePolicyRequestSchema = { - body: CreatePackagePolicyRequestBodySchema, + body: schema.oneOf([ + CreatePackagePolicyRequestBodySchema, + SimplifiedCreatePackagePolicyRequestBodySchema, + ]), }; export const UpdatePackagePolicyRequestSchema = { ...GetOnePackagePolicyRequestSchema, - body: UpdatePackagePolicyRequestBodySchema, + body: schema.oneOf([ + UpdatePackagePolicyRequestBodySchema, + SimplifiedCreatePackagePolicyRequestBodySchema, + ]), }; export const DeletePackagePoliciesRequestSchema = { diff --git a/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.test.ts b/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.test.ts index f878653430954..ed9f46f96b44c 100644 --- a/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.test.ts +++ b/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.test.ts @@ -33,12 +33,45 @@ describe('collapse_fn', () => { { val: 8, split: 'B' }, ], }, - { metric: ['val'], fn: 'sum' } + { metric: ['val'], fn: ['sum'] } ); expect(result.rows).toEqual([{ val: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 }]); }); + it('can use a single function for multiple metrics', async () => { + const result = await runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'val2', name: 'val2', meta: { type: 'number' } }, + { id: 'val3', name: 'val3', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, val2: 1, val3: 1, split: 'A' }, + { val: 2, val2: 2, val3: 2, split: 'B' }, + { val: 3, val2: 3, val3: 3, split: 'B' }, + { val: 4, val2: 4, val3: 4, split: 'A' }, + { val: 5, val2: 5, val3: 5, split: 'A' }, + { val: 6, val2: 6, val3: 6, split: 'A' }, + { val: 7, val2: 7, val3: 7, split: 'B' }, + { val: 8, val2: 22, val3: 77, split: 'B' }, + ], + }, + { metric: ['val', 'val2', 'val3'], fn: ['sum'] } + ); + + expect(result.rows).toEqual([ + { + val: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8, + val2: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 22, + val3: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 77, + }, + ]); + }); + it('can use different functions for each different metric', async () => { const result = await runFn( { @@ -114,7 +147,7 @@ describe('collapse_fn', () => { }; it('splits by a column', async () => { - const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: 'sum' }); + const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: ['sum'] }); expect(result.rows).toEqual([ { val: 1 + 4 + 6, split: 'A' }, { val: 2 + 7 + 8, split: 'B' }, @@ -123,7 +156,7 @@ describe('collapse_fn', () => { }); it('applies avg', async () => { - const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: 'avg' }); + const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: ['avg'] }); expect(result.rows).toEqual([ { val: (1 + 4 + 6) / 3, split: 'A' }, { val: (2 + 7 + 8) / 3, split: 'B' }, @@ -132,7 +165,7 @@ describe('collapse_fn', () => { }); it('applies min', async () => { - const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: 'min' }); + const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: ['min'] }); expect(result.rows).toEqual([ { val: 1, split: 'A' }, { val: 2, split: 'B' }, @@ -141,7 +174,7 @@ describe('collapse_fn', () => { }); it('applies max', async () => { - const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: 'max' }); + const result = await runFn(twoSplitTable, { metric: ['val'], by: ['split'], fn: ['max'] }); expect(result.rows).toEqual([ { val: 6, split: 'A' }, { val: 8, split: 'B' }, diff --git a/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.ts b/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.ts index 5ca2248ed1ef7..ee3192705332d 100644 --- a/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.ts +++ b/x-pack/plugins/lens/common/expressions/collapse/collapse_fn.ts @@ -17,11 +17,8 @@ function getValueAsNumberArray(value: unknown) { } export const collapseFn: CollapseExpressionFunction['fn'] = (input, { by, metric, fn }) => { - const collapseFunctionsByMetricIndex = Array.isArray(fn) - ? fn - : metric - ? new Array(metric.length).fill(fn) - : []; + const collapseFunctionsByMetricIndex = + fn.length > 1 ? fn : metric ? new Array(metric.length).fill(fn[0]) : []; if (metric && metric.length !== collapseFunctionsByMetricIndex.length) { throw Error(`lens_collapse - Called with ${metric.length} metrics and ${fn.length} collapse functions. diff --git a/x-pack/plugins/lens/common/expressions/collapse/index.ts b/x-pack/plugins/lens/common/expressions/collapse/index.ts index 5ea792e39cb0d..bd8df507c95e8 100644 --- a/x-pack/plugins/lens/common/expressions/collapse/index.ts +++ b/x-pack/plugins/lens/common/expressions/collapse/index.ts @@ -13,7 +13,7 @@ type CollapseFunction = 'sum' | 'avg' | 'min' | 'max'; export interface CollapseArgs { by?: string[]; metric?: string[]; - fn: CollapseFunction | CollapseFunction[]; + fn: CollapseFunction[]; } /** diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index bff30e66a9bc8..b84353e116f70 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -46,7 +46,8 @@ "kibanaUtils", "kibanaReact", "embeddable", - "fieldFormats" + "fieldFormats", + "charts" ], "owner": { "name": "Vis Editors", diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 31b777c904c8c..d314c1198fa9f 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -31,7 +31,7 @@ import { SaveModalContainer, runSaveLensVisualization } from './save_modal_conta import { LensInspector } from '../lens_inspector_service'; import { getEditPath } from '../../common'; import { isLensEqual } from './lens_document_equality'; -import { IndexPatternServiceAPI, createIndexPatternService } from '../indexpattern_service/service'; +import { IndexPatternServiceAPI, createIndexPatternService } from '../data_views_service/service'; import { replaceIndexpattern } from '../state_management/lens_slice'; export type SaveProps = Omit & { @@ -62,6 +62,9 @@ export function App({ const { data, + dataViews, + uiActions, + uiSettings, chrome, inspector: lensInspector, application, @@ -367,10 +370,10 @@ export function App({ const indexPatternService = useMemo( () => createIndexPatternService({ - dataViews: lensAppServices.dataViews, - uiSettings: lensAppServices.uiSettings, - uiActions: lensAppServices.uiActions, - core: { http, notifications }, + dataViews, + uiActions, + core: { http, notifications, uiSettings }, + data, updateIndexPatterns: (newIndexPatternsState, options) => { dispatch(updateIndexPatterns(newIndexPatternsState)); if (options?.applyImmediately) { @@ -384,7 +387,7 @@ export function App({ } }, }), - [dispatch, http, notifications, lensAppServices] + [dataViews, uiActions, http, notifications, uiSettings, data, dispatch] ); return ( @@ -400,6 +403,7 @@ export function App({ setHeaderActionMenu={setHeaderActionMenu} indicateNoData={indicateNoData} datasourceMap={datasourceMap} + visualizationMap={visualizationMap} title={persistedDoc?.title} lensInspector={lensInspector} currentDoc={currentDoc} 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 a32cc5c4a532b..6023445d61677 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 @@ -210,6 +210,7 @@ export const LensTopNavMenu = ({ onAppLeave, redirectToOrigin, datasourceMap, + visualizationMap, title, goBackToOriginatingApp, contextOriginatingApp, @@ -305,6 +306,10 @@ export const LensTopNavMenu = ({ {} ), datasourceStates, + visualizationState: visualization.state, + activeVisualization: visualization.activeId + ? visualizationMap[visualization.activeId] + : undefined, }) ); // Add ad-hoc data views from the Lens state even if they are not used @@ -337,6 +342,8 @@ export const LensTopNavMenu = ({ activeDatasourceId, rejectedIndexPatterns, datasourceMap, + visualizationMap, + visualization, indexPatterns, dataViewsService, dataViews, diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 52fe1aa98c500..335400b4cf793 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -52,7 +52,7 @@ import type { import type { LensAttributeService } from '../lens_attribute_service'; import type { LensEmbeddableInput } from '../embeddable/embeddable'; import type { LensInspector } from '../lens_inspector_service'; -import { IndexPatternServiceAPI } from '../indexpattern_service/service'; +import { IndexPatternServiceAPI } from '../data_views_service/service'; import { Document } from '../persistence/saved_object_store'; export interface RedirectToOriginProps { @@ -106,6 +106,7 @@ export interface LensTopNavMenuProps { setIsSaveModalVisible: React.Dispatch>; runSave: RunSave; datasourceMap: DatasourceMap; + visualizationMap: VisualizationMap; title?: string; lensInspector: LensInspector; goBackToOriginatingApp?: () => void; diff --git a/x-pack/plugins/lens/public/indexpattern_service/loader.test.ts b/x-pack/plugins/lens/public/data_views_service/loader.test.ts similarity index 83% rename from x-pack/plugins/lens/public/indexpattern_service/loader.test.ts rename to x-pack/plugins/lens/public/data_views_service/loader.test.ts index f0b8dab384de5..ffa106699a3ff 100644 --- a/x-pack/plugins/lens/public/indexpattern_service/loader.test.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.test.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { HttpHandler } from '@kbn/core/public'; -import { last } from 'lodash'; -import { DataViewsContract } from '@kbn/data-views-plugin/public'; -import { createHttpFetchError } from '@kbn/core-http-browser-mocks'; +import { DataViewsContract, DataViewSpec, FieldSpec } from '@kbn/data-views-plugin/public'; import { IndexPattern, IndexPatternField } from '../types'; import { ensureIndexPattern, @@ -18,6 +15,12 @@ import { } from './loader'; import { sampleIndexPatterns, mockDataViewsService } from './mocks'; import { documentField } from '../indexpattern_datasource/document_field'; +import { coreMock } from '@kbn/core/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { createHttpFetchError } from '@kbn/core-http-browser-mocks'; describe('loader', () => { describe('loadIndexPatternRefs', () => { @@ -262,6 +265,10 @@ describe('loader', () => { }); describe('syncExistingFields', () => { + const core = coreMock.createStart(); + const dataViews = dataViewPluginMocks.createStartContract(); + const data = dataPluginMock.createStartContract(); + const dslQuery = { bool: { must: [], @@ -273,27 +280,51 @@ describe('loader', () => { function getIndexPatternList() { return [ - { id: '1', title: '1', fields: [], hasRestrictions: false }, - { id: '2', title: '1', fields: [], hasRestrictions: false }, - { id: '3', title: '1', fields: [], hasRestrictions: false }, + { + id: '1', + title: '1', + fields: [{ name: 'ip1_field_1' }, { name: 'ip1_field_2' }], + hasRestrictions: false, + }, + { + id: '2', + title: '2', + fields: [{ name: 'ip2_field_1' }, { name: 'ip2_field_2' }], + hasRestrictions: false, + }, + { + id: '3', + title: '3', + fields: [{ name: 'ip3_field_1' }, { name: 'ip3_field_2' }], + hasRestrictions: false, + }, ] as unknown as IndexPattern[]; } + beforeEach(() => { + core.uiSettings.get.mockImplementation((key: string) => { + if (key === UI_SETTINGS.META_FIELDS) { + return []; + } + }); + dataViews.get.mockImplementation((id: string) => + Promise.resolve( + getIndexPatternList().find( + (indexPattern) => indexPattern.id === id + ) as unknown as DataView + ) + ); + }); + it('should call once for each index pattern', async () => { const updateIndexPatterns = jest.fn(); - const fetchJson = jest.fn((path: string) => { - const indexPatternTitle = last(path.split('/')); - return { - indexPatternTitle, - existingFieldNames: ['field_1', 'field_2'].map( - (fieldName) => `ip${indexPatternTitle}_${fieldName}` - ), - }; - }) as unknown as HttpHandler; + dataViews.getFieldsForIndexPattern.mockImplementation( + (dataView: DataViewSpec | DataView) => + Promise.resolve(dataView.fields) as Promise + ); await syncExistingFields({ dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, - fetchJson, indexPatternList: getIndexPatternList(), updateIndexPatterns, dslQuery, @@ -301,9 +332,13 @@ describe('loader', () => { currentIndexPatternTitle: 'abc', isFirstExistenceFetch: false, existingFields: {}, + core, + data, + dataViews, }); - expect(fetchJson).toHaveBeenCalledTimes(3); + expect(dataViews.get).toHaveBeenCalledTimes(3); + expect(dataViews.getFieldsForIndexPattern).toHaveBeenCalledTimes(3); expect(updateIndexPatterns).toHaveBeenCalledTimes(1); const [newState, options] = updateIndexPatterns.mock.calls[0]; @@ -322,20 +357,16 @@ describe('loader', () => { it('should call onNoData callback if current index pattern returns no fields', async () => { const updateIndexPatterns = jest.fn(); const onNoData = jest.fn(); - const fetchJson = jest.fn((path: string) => { - const indexPatternTitle = last(path.split('/')); - return { - indexPatternTitle, - existingFieldNames: - indexPatternTitle === '1' - ? ['field_1', 'field_2'].map((fieldName) => `${indexPatternTitle}_${fieldName}`) - : [], - }; - }) as unknown as HttpHandler; + dataViews.getFieldsForIndexPattern.mockImplementation( + async (dataView: DataViewSpec | DataView) => { + return (dataView.title === '1' + ? [{ name: `${dataView.title}_field_1` }, { name: `${dataView.title}_field_2` }] + : []) as unknown as Promise; + } + ); const args = { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, - fetchJson, indexPatternList: getIndexPatternList(), updateIndexPatterns, dslQuery, @@ -343,6 +374,9 @@ describe('loader', () => { currentIndexPatternTitle: 'abc', isFirstExistenceFetch: false, existingFields: {}, + core, + data, + dataViews, }; await syncExistingFields(args); @@ -355,15 +389,14 @@ describe('loader', () => { it('should set all fields to available and existence error flag if the request fails', async () => { const updateIndexPatterns = jest.fn(); - const fetchJson = jest.fn((path: string) => { - return new Promise((resolve, reject) => { + dataViews.getFieldsForIndexPattern.mockImplementation(() => { + return new Promise((_, reject) => { reject(new Error()); }); - }) as unknown as HttpHandler; + }); const args = { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, - fetchJson, indexPatternList: [ { id: '1', @@ -378,6 +411,9 @@ describe('loader', () => { currentIndexPatternTitle: 'abc', isFirstExistenceFetch: false, existingFields: {}, + core, + data, + dataViews, }; await syncExistingFields(args); @@ -395,8 +431,8 @@ describe('loader', () => { it('should set all fields to available and existence error flag if the request times out', async () => { const updateIndexPatterns = jest.fn(); - const fetchJson = jest.fn((path: string) => { - return new Promise((resolve, reject) => { + dataViews.getFieldsForIndexPattern.mockImplementation(() => { + return new Promise((_, reject) => { const error = createHttpFetchError( 'timeout', 'error', @@ -405,11 +441,10 @@ describe('loader', () => { ); reject(error); }); - }) as unknown as HttpHandler; + }); const args = { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, - fetchJson, indexPatternList: [ { id: '1', @@ -424,6 +459,9 @@ describe('loader', () => { currentIndexPatternTitle: 'abc', isFirstExistenceFetch: false, existingFields: {}, + core, + data, + dataViews, }; await syncExistingFields(args); diff --git a/x-pack/plugins/lens/public/indexpattern_service/loader.ts b/x-pack/plugins/lens/public/data_views_service/loader.ts similarity index 92% rename from x-pack/plugins/lens/public/indexpattern_service/loader.ts rename to x-pack/plugins/lens/public/data_views_service/loader.ts index 836daea7b6d30..fe42aaa701c94 100644 --- a/x-pack/plugins/lens/public/indexpattern_service/loader.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.ts @@ -8,10 +8,12 @@ import { isNestedField } from '@kbn/data-views-plugin/common'; import type { DataViewsContract, DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; import { keyBy } from 'lodash'; -import { HttpSetup } from '@kbn/core/public'; +import { CoreStart } from '@kbn/core/public'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { loadFieldExisting } from '@kbn/unified-field-list-plugin/public'; import { IndexPattern, IndexPatternField, IndexPatternMap, IndexPatternRef } from '../types'; import { documentField } from '../indexpattern_datasource/document_field'; -import { BASE_API_URL, DateRange, ExistingFields } from '../../common'; +import { DateRange } from '../../common'; import { DataViewsState } from '../state_management'; type ErrorHandler = (err: Error) => void; @@ -231,41 +233,40 @@ export async function ensureIndexPattern({ async function refreshExistingFields({ dateRange, - fetchJson, indexPatternList, dslQuery, + core, + data, + dataViews, }: { dateRange: DateRange; indexPatternList: IndexPattern[]; - fetchJson: HttpSetup['post']; dslQuery: object; + core: Pick; + data: DataPublicPluginStart; + dataViews: DataViewsContract; }) { try { const emptinessInfo = await Promise.all( - indexPatternList.map((pattern) => { + indexPatternList.map(async (pattern) => { if (pattern.hasRestrictions) { return { indexPatternTitle: pattern.title, existingFieldNames: pattern.fields.map((field) => field.name), }; } - const body: Record = { + + const dataView = await dataViews.get(pattern.id); + return await loadFieldExisting({ dslQuery, fromDate: dateRange.fromDate, toDate: dateRange.toDate, - }; - - if (pattern.timeFieldName) { - body.timeFieldName = pattern.timeFieldName; - } - - if (pattern.spec) { - body.spec = pattern.spec; - } - - return fetchJson(`${BASE_API_URL}/existing_fields/${pattern.id}`, { - body: JSON.stringify(body), - }) as Promise; + timeFieldName: pattern.timeFieldName, + data, + uiSettingsClient: core.uiSettings, + dataViewsService: dataViews, + dataView, + }); }) ); return { result: emptinessInfo, status: 200 }; @@ -289,7 +290,6 @@ export async function syncExistingFields({ dateRange: DateRange; indexPatternList: IndexPattern[]; existingFields: Record>; - fetchJson: HttpSetup['post']; updateIndexPatterns: ( newFieldState: FieldsPropsFromDataViewsState, options: { applyImmediately: boolean } @@ -298,6 +298,9 @@ export async function syncExistingFields({ currentIndexPatternTitle: string; dslQuery: object; onNoData?: () => void; + core: Pick; + data: DataPublicPluginStart; + dataViews: DataViewsContract; }) { const { indexPatternList } = requestOptions; const newExistingFields = { ...existingFields }; diff --git a/x-pack/plugins/lens/public/indexpattern_service/mocks.ts b/x-pack/plugins/lens/public/data_views_service/mocks.ts similarity index 82% rename from x-pack/plugins/lens/public/indexpattern_service/mocks.ts rename to x-pack/plugins/lens/public/data_views_service/mocks.ts index 048333786fdd6..55bf80ae60a46 100644 --- a/x-pack/plugins/lens/public/indexpattern_service/mocks.ts +++ b/x-pack/plugins/lens/public/data_views_service/mocks.ts @@ -11,22 +11,38 @@ import { createMockedIndexPattern, createMockedRestrictedIndexPattern, } from '../indexpattern_datasource/mocks'; -import { IndexPattern } from '../types'; +import { DataViewsState } from '../state_management'; +import { ExistingFieldsMap, IndexPattern } from '../types'; import { getFieldByNameFactory } from './loader'; -export function loadInitialDataViews() { - const indexPattern = createMockedIndexPattern(); - const restricted = createMockedRestrictedIndexPattern(); +/** + * Create a DataViewState from partial parameters, and infer the rest from the passed one. + * Passing no parameter will return an empty state. + */ +export const createMockDataViewsState = ({ + indexPatterns, + indexPatternRefs, + isFirstExistenceFetch, + existingFields, +}: Partial = {}): DataViewsState => { + const refs = + indexPatternRefs ?? + Object.values(indexPatterns ?? {}).map(({ id, title, name }) => ({ id, title, name })); + const allFields = + existingFields ?? + refs.reduce((acc, { id, title }) => { + if (indexPatterns && id in indexPatterns) { + acc[title] = Object.fromEntries(indexPatterns[id].fields.map((f) => [f.displayName, true])); + } + return acc; + }, {} as ExistingFieldsMap); return { - indexPatternRefs: [], - existingFields: {}, - indexPatterns: { - [indexPattern.id]: indexPattern, - [restricted.id]: restricted, - }, - isFirstExistenceFetch: false, + indexPatterns: indexPatterns ?? {}, + indexPatternRefs: refs, + isFirstExistenceFetch: Boolean(isFirstExistenceFetch), + existingFields: allFields, }; -} +}; export const createMockStorage = (lastData?: Record) => { return { diff --git a/x-pack/plugins/lens/public/indexpattern_service/service.ts b/x-pack/plugins/lens/public/data_views_service/service.ts similarity index 93% rename from x-pack/plugins/lens/public/indexpattern_service/service.ts rename to x-pack/plugins/lens/public/data_views_service/service.ts index 91e070d52d48a..a6c76468bb2ca 100644 --- a/x-pack/plugins/lens/public/indexpattern_service/service.ts +++ b/x-pack/plugins/lens/public/data_views_service/service.ts @@ -6,8 +6,9 @@ */ import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; -import type { CoreStart, IUiSettingsClient } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { ActionExecutionContext, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { UPDATE_FILTER_REFERENCES_ACTION, @@ -25,9 +26,9 @@ import type { DataViewsState } from '../state_management'; import { generateId } from '../id_generator'; export interface IndexPatternServiceProps { - core: Pick; + core: Pick; + data: DataPublicPluginStart; dataViews: DataViewsContract; - uiSettings: IUiSettingsClient; uiActions: UiActionsStart; updateIndexPatterns: ( newState: Partial, @@ -100,7 +101,7 @@ export interface IndexPatternServiceAPI { export function createIndexPatternService({ core, dataViews, - uiSettings, + data, updateIndexPatterns, replaceIndexPattern, uiActions, @@ -145,11 +146,13 @@ export function createIndexPatternService({ refreshExistingFields: (args) => syncExistingFields({ updateIndexPatterns, - fetchJson: core.http.post, ...args, + data, + dataViews, + core, }), loadIndexPatternRefs: async ({ isFullEditor }) => isFullEditor ? loadIndexPatternRefs(dataViews) : [], - getDefaultIndex: () => uiSettings.get('defaultIndex'), + getDefaultIndex: () => core.uiSettings.get('defaultIndex'), }; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx index 8ce2a4c0cc975..5f3fd2d4a73b5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx @@ -196,9 +196,9 @@ export interface OnVisDropProps { group?: VisualizationDimensionGroupConfig; } -export function onDropForVisualization( +export function onDropForVisualization( props: OnVisDropProps, - activeVisualization: Visualization + activeVisualization: Visualization ) { const { prevState, target, frame, dropType, source, group } = props; const { layerId, columnId, groupId } = target; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index f3cfa9a97667c..cd74119bbe315 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -29,7 +29,8 @@ function fromExcludedClickTarget(event: Event) { ) { if ( node.classList!.contains(DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS) || - node.classList!.contains('euiBody-hasPortalContent') + node.classList!.contains('euiBody-hasPortalContent') || + node.getAttribute('data-euiportal') === 'true' ) { return true; } 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 544835d2e3c21..65008196c4b8f 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 @@ -18,7 +18,7 @@ import { EuiIconTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IndexPatternServiceAPI } from '../../../indexpattern_service/service'; +import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { NativeRenderer } from '../../../native_renderer'; import { StateSetter, @@ -524,8 +524,13 @@ export function LayerPanel( columnId, label: columnLabelMap?.[columnId] ?? '', hideTooltip, - invalid: group.invalid, - invalidMessage: group.invalidMessage, + ...(activeVisualization?.validateColumn?.( + visualizationState, + { dataViews }, + layerId, + columnId, + group + ) || { invalid: false }), })} )} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index 3e1458d42c438..c4a2d77c30bab 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -6,7 +6,7 @@ */ import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { IndexPatternServiceAPI } from '../../../indexpattern_service/service'; +import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { Visualization, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx index 63e4f88d6d234..9370892a2d7fe 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { DataPanelWrapper } from './data_panel_wrapper'; -import { Datasource, DatasourceDataPanelProps } from '../../types'; +import { Datasource, DatasourceDataPanelProps, VisualizationMap } from '../../types'; import { DragDropIdentifier } from '../../drag_drop'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { createMockFramePublicAPI, mockStoreDeps, mountWithProvider } from '../../mocks'; @@ -26,12 +26,14 @@ describe('Data Panel Wrapper', () => { const datasourceMap = { activeDatasource: { renderDataPanel, + getUsedDataViews: jest.fn(), } as unknown as Datasource, }; const mountResult = await mountWithProvider( {}} core={{} as DatasourceDataPanelProps['core']} dropOntoWorkspace={(field: DragDropIdentifier) => {}} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx index 553a61ba2c1c5..0e2b7f88ca6f2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx @@ -16,7 +16,13 @@ import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { Easteregg } from './easteregg'; import { NativeRenderer } from '../../native_renderer'; import { DragContext, DragDropIdentifier } from '../../drag_drop'; -import { StateSetter, DatasourceDataPanelProps, DatasourceMap, FramePublicAPI } from '../../types'; +import { + StateSetter, + DatasourceDataPanelProps, + DatasourceMap, + FramePublicAPI, + VisualizationMap, +} from '../../types'; import { switchDatasource, useLensDispatch, @@ -27,14 +33,16 @@ import { selectExecutionContext, selectActiveDatasourceId, selectDatasourceStates, + selectVisualizationState, } from '../../state_management'; import { initializeSources } from './state_helpers'; -import type { IndexPatternServiceAPI } from '../../indexpattern_service/service'; +import type { IndexPatternServiceAPI } from '../../data_views_service/service'; import { changeIndexPattern } from '../../state_management/lens_slice'; import { getInitialDataViewsObject } from '../../utils'; interface DataPanelWrapperProps { datasourceMap: DatasourceMap; + visualizationMap: VisualizationMap; showNoDataPopover: () => void; core: DatasourceDataPanelProps['core']; dropOntoWorkspace: (field: DragDropIdentifier) => void; @@ -48,6 +56,7 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { const externalContext = useLensSelector(selectExecutionContext); const activeDatasourceId = useLensSelector(selectActiveDatasourceId); const datasourceStates = useLensSelector(selectDatasourceStates); + const visualizationState = useLensSelector(selectVisualizationState); const datasourceIsLoading = activeDatasourceId ? datasourceStates[activeDatasourceId].isLoading @@ -74,6 +83,8 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { initializeSources( { datasourceMap: props.datasourceMap, + visualizationMap: props.visualizationMap, + visualizationState, datasourceStates, dataViews: props.plugins.dataViews, references: undefined, @@ -84,29 +95,38 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { { isFullEditor: true, } - ).then(({ states, indexPatterns, indexPatternRefs }) => { - const newDatasourceStates = Object.entries(states).reduce( - (state, [datasourceId, datasourceState]) => ({ - ...state, - [datasourceId]: { - ...datasourceState, - isLoading: false, - }, - }), - {} - ); - dispatchLens( - setState({ - datasourceStates: newDatasourceStates, - dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), - }) - ); - }); + ).then( + ({ + datasourceStates: newDatasourceStates, + visualizationState: newVizState, + indexPatterns, + indexPatternRefs, + }) => { + dispatchLens( + setState({ + visualization: { ...visualizationState, state: newVizState }, + datasourceStates: Object.entries(newDatasourceStates).reduce( + (state, [datasourceId, datasourceState]) => ({ + ...state, + [datasourceId]: { + ...datasourceState, + isLoading: false, + }, + }), + {} + ), + dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), + }) + ); + } + ); } }, [ datasourceStates, + visualizationState, activeDatasourceId, props.datasourceMap, + props.visualizationMap, dispatchLens, props.plugins.dataViews, props.core.uiSettings, @@ -145,6 +165,19 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { onChangeIndexPattern, indexPatternService: props.indexPatternService, frame: props.frame, + // Visualization can handle dataViews, so need to pass to the data panel the full list of used dataViews + usedIndexPatterns: [ + ...((activeDatasourceId && + props.datasourceMap[activeDatasourceId]?.getUsedDataViews( + datasourceStates[activeDatasourceId].state + )) || + []), + ...((visualizationState.activeId && + props.visualizationMap[visualizationState.activeId]?.getUsedDataViews?.( + visualizationState.state + )) || + []), + ], }; const [showDatasourceSwitcher, setDatasourceSwitcher] = useState(false); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 090161ee11565..4100e25da7667 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -30,7 +30,7 @@ import { } from '../../state_management'; import type { LensInspector } from '../../lens_inspector_service'; import { ErrorBoundary, showMemoizedErrorNotification } from '../../lens_ui_errors'; -import { IndexPatternServiceAPI } from '../../indexpattern_service/service'; +import { IndexPatternServiceAPI } from '../../data_views_service/service'; export interface EditorFrameProps { datasourceMap: DatasourceMap; @@ -120,6 +120,7 @@ export function EditorFrame(props: EditorFrameProps) { core={props.core} plugins={props.plugins} datasourceMap={datasourceMap} + visualizationMap={visualizationMap} showNoDataPopover={props.showNoDataPopover} dropOntoWorkspace={dropOntoWorkspace} hasSuggestionForField={hasSuggestionForField} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index a5cc6c06644ff..aab10d783ab52 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -37,9 +37,9 @@ import { getMissingVisualizationTypeError, getUnknownVisualizationTypeError, } from '../error_helper'; -import type { DatasourceStates, DataViewsState } from '../../state_management'; +import type { DatasourceStates, DataViewsState, VisualizationState } from '../../state_management'; import { readFromStorage } from '../../settings_storage'; -import { loadIndexPatternRefs, loadIndexPatterns } from '../../indexpattern_service/loader'; +import { loadIndexPatternRefs, loadIndexPatterns } from '../../data_views_service/loader'; function getIndexPatterns( references?: SavedObjectReference[], @@ -158,6 +158,8 @@ export async function initializeSources( { dataViews, datasourceMap, + visualizationMap, + visualizationState, datasourceStates, storage, defaultIndexPatternId, @@ -167,6 +169,8 @@ export async function initializeSources( }: { dataViews: DataViewsContract; datasourceMap: DatasourceMap; + visualizationMap: VisualizationMap; + visualizationState: VisualizationState; datasourceStates: DatasourceStates; defaultIndexPatternId: string; storage: IStorageWrapper; @@ -192,7 +196,7 @@ export async function initializeSources( return { indexPatterns, indexPatternRefs, - states: initializeDatasources({ + datasourceStates: initializeDatasources({ datasourceMap, datasourceStates, initialContext, @@ -200,9 +204,34 @@ export async function initializeSources( indexPatterns, references, }), + visualizationState: initializeVisualization({ + visualizationMap, + visualizationState, + references, + }), }; } +export function initializeVisualization({ + visualizationMap, + visualizationState, + references, +}: { + visualizationState: VisualizationState; + visualizationMap: VisualizationMap; + references?: SavedObjectReference[]; +}) { + if (visualizationState?.activeId) { + return ( + visualizationMap[visualizationState.activeId]?.fromPersistableState?.( + visualizationState.state, + references + ) ?? visualizationState.state + ); + } + return visualizationState.state; +} + export function initializeDatasources({ datasourceMap, datasourceStates, @@ -271,7 +300,7 @@ export async function persistedStateToExpression( ): Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }> { const { state: { - visualization: visualizationState, + visualization: persistedVisualizationState, datasourceStates: persistedDatasourceStates, adHocDataViews, internalReferences, @@ -294,6 +323,14 @@ export async function persistedStateToExpression( }; } const visualization = visualizations[visualizationType!]; + const visualizationState = initializeVisualization({ + visualizationMap: visualizations, + visualizationState: { + state: persistedVisualizationState, + activeId: visualizationType, + }, + references: [...references, ...(internalReferences || [])], + }); const datasourceStatesFromSO = Object.fromEntries( Object.entries(persistedDatasourceStates).map(([id, state]) => [ id, @@ -404,15 +441,15 @@ export const validateDatasourceAndVisualization = ( currentDatasourceState: unknown | null, currentVisualization: Visualization | null, currentVisualizationState: unknown | undefined, - { datasourceLayers, dataViews }: Pick + frame: Pick ): ErrorMessage[] | undefined => { try { const datasourceValidationErrors = currentDatasourceState - ? currentDataSource?.getErrorMessages(currentDatasourceState, dataViews.indexPatterns) + ? currentDataSource?.getErrorMessages(currentDatasourceState, frame.dataViews.indexPatterns) : undefined; const visualizationValidationErrors = currentVisualizationState - ? currentVisualization?.getErrorMessages(currentVisualizationState, datasourceLayers) + ? currentVisualization?.getErrorMessages(currentVisualizationState, frame) : undefined; if (datasourceValidationErrors?.length || visualizationValidationErrors?.length) { 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 ebe2de3f341d1..87b34e066f5e5 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 @@ -243,6 +243,7 @@ export function SuggestionPanel({ visualizationMap[visualizationId], suggestionVisualizationState, { + ...frame, dataViews: frame.dataViews, datasourceLayers: getDatasourceLayers( suggestionDatasourceId diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss index ca1d4b2e2caff..34829f8323131 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss @@ -8,6 +8,7 @@ @include euiYScroll; max-height: $euiSize * 20; width: $euiSize * 16; + overflow-wrap: break-word; } .lnsWorkspaceWarningList__item { 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 cc2397b820a91..257d6d78cff99 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 @@ -562,7 +562,10 @@ describe('workspace_panel', () => { expect(mounted.lensStore.dispatch).toHaveBeenCalledWith({ type: 'lens/onActiveDataChange', - payload: tablesData, + payload: { + activeData: tablesData, + requestWarnings: [], + }, }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index e2ad7cf809ee7..5291811671715 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -217,22 +217,34 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ if (renderDeps.current) { const [defaultLayerId] = Object.keys(renderDeps.current.datasourceLayers); + const requestWarnings: string[] = []; + const datasource = Object.values(renderDeps.current?.datasourceMap)[0]; + const datasourceState = Object.values(renderDeps.current?.datasourceStates)[0].state; + if (adapters?.requests) { + plugins.data.search.showWarnings(adapters.requests, (warning) => { + const warningMessage = datasource.getSearchWarningMessages?.(datasourceState, warning); + + requestWarnings.push(...(warningMessage || [])); + if (warningMessage && warningMessage.length) return true; + }); + } if (adapters && adapters.tables) { dispatchLens( - onActiveDataChange( - Object.entries(adapters.tables?.tables).reduce>( + onActiveDataChange({ + activeData: Object.entries(adapters.tables?.tables).reduce>( (acc, [key, value], index, tables) => ({ ...acc, [tables.length === 1 ? defaultLayerId : key]: value, }), {} - ) - ) + ), + requestWarnings, + }) ); } } }, - [dispatchLens] + [dispatchLens, plugins.data.search] ); const shouldApplyExpression = autoApplyEnabled || !initialRenderComplete.current || triggerApply; @@ -606,6 +618,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ datasourceMap={datasourceMap} visualizationMap={visualizationMap} isFullscreen={isFullscreen} + lensInspector={lensInspector} > {renderWorkspace()} @@ -656,6 +669,7 @@ export const VisualizationWrapper = ({ to: context.dateRange.toDate, }, filters: context.filters, + disableShardWarnings: true, }), [context] ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx index fcdbedff74a8d..844f3e6cb845e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx @@ -18,6 +18,7 @@ import { selectTriggerApplyChanges, } from '../../../state_management'; import { enableAutoApply, setChangesApplied } from '../../../state_management/lens_slice'; +import { LensInspector } from '../../../lens_inspector_service'; describe('workspace_panel_wrapper', () => { let mockVisualization: jest.Mocked; @@ -39,6 +40,7 @@ describe('workspace_panel_wrapper', () => { datasourceMap={{}} datasourceStates={{}} isFullscreen={false} + lensInspector={{} as unknown as LensInspector} > @@ -60,6 +62,7 @@ describe('workspace_panel_wrapper', () => { datasourceMap={{}} datasourceStates={{}} isFullscreen={false} + lensInspector={{} as unknown as LensInspector} /> ); @@ -114,6 +117,7 @@ describe('workspace_panel_wrapper', () => { datasourceMap={{}} datasourceStates={{}} isFullscreen={false} + lensInspector={{} as unknown as LensInspector} >
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index a10d4c2f4333a..cdc293dc78bc5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -31,8 +31,10 @@ import { selectChangesApplied, applyChanges, selectAutoApplyEnabled, + selectStagedRequestWarnings, } from '../../../state_management'; import { WorkspaceTitle } from './title'; +import { LensInspector } from '../../../lens_inspector_service'; export const AUTO_APPLY_DISABLED_STORAGE_KEY = 'autoApplyDisabled'; @@ -45,6 +47,7 @@ export interface WorkspacePanelWrapperProps { datasourceMap: DatasourceMap; datasourceStates: DatasourceStates; isFullscreen: boolean; + lensInspector: LensInspector; } export function WorkspacePanelWrapper({ @@ -56,11 +59,13 @@ export function WorkspacePanelWrapper({ datasourceMap, datasourceStates, isFullscreen, + lensInspector, }: WorkspacePanelWrapperProps) { const dispatchLens = useLensDispatch(); const changesApplied = useLensSelector(selectChangesApplied); const autoApplyEnabled = useLensSelector(selectAutoApplyEnabled); + const requestWarnings = useLensSelector(selectStagedRequestWarnings); const activeVisualization = visualizationId ? visualizationMap[visualizationId] : null; const setVisualizationState = useCallback( @@ -99,12 +104,18 @@ export function WorkspacePanelWrapper({ const datasource = datasourceMap[datasourceId]; if (!datasourceState.isLoading && datasource.getWarningMessages) { warningMessages.push( - ...(datasource.getWarningMessages(datasourceState.state, framePublicAPI, (updater) => - setDatasourceState(updater, datasourceId) + ...(datasource.getWarningMessages( + datasourceState.state, + framePublicAPI, + lensInspector.adapters, + (updater) => setDatasourceState(updater, datasourceId) ) || []) ); } }); + if (requestWarnings) { + warningMessages.push(...requestWarnings); + } return ( <> {!(isFullscreen && (autoApplyEnabled || warningMessages?.length)) && ( diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 863bc82485b81..9eb79190d44e7 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -1415,4 +1415,88 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(2); expect(expressionRenderer.mock.calls[1][0]!.expression).toBe(`edited`); }); + + it('should override noPadding in the display options if noPadding is set in the embeddable input', async () => { + expressionRenderer = jest.fn((_) => null); + + const visDocument: Document = { + state: { + visualization: {}, + datasourceStates: {}, + query: { query: '', language: 'lucene' }, + filters: [], + }, + references: [], + title: 'My title', + visualizationType: 'testVis', + }; + + const createEmbeddable = (noPadding?: boolean) => { + return new Embeddable( + { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService: attributeServiceMockFromSavedVis(visDocument), + data: dataMock, + expressionRenderer, + basePath, + dataViews: {} as DataViewsContract, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + discover: {}, + navLinks: {}, + }, + inspector: inspectorPluginMock.createStartContract(), + getTrigger, + theme: themeServiceMock.createStartContract(), + visualizationMap: { + [visDocument.visualizationType as string]: { + getDisplayOptions: () => ({ + noPadding: false, + }), + } as unknown as Visualization, + }, + datasourceMap: {}, + injectFilterReferences: jest.fn(mockInjectFilterReferences), + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + errors: undefined, + }), + uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, + }, + { + timeRange: { + from: 'now-15m', + to: 'now', + }, + noPadding, + } as LensEmbeddableInput + ); + }; + + let embeddable = createEmbeddable(); + embeddable.render(mountpoint); + + // wait one tick to give embeddable time to initialize + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(expressionRenderer).toHaveBeenCalledTimes(1); + expect(expressionRenderer.mock.calls[0][0]!.padding).toBe('s'); + + embeddable = createEmbeddable(true); + embeddable.render(mountpoint); + + // wait one tick to give embeddable time to initialize + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(expressionRenderer).toHaveBeenCalledTimes(2); + expect(expressionRenderer.mock.calls[1][0]!.padding).toBe(undefined); + }); }); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index be6706aded4a0..fb7d7646871c7 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -7,6 +7,7 @@ import { isEqual, uniqBy } from 'lodash'; import React from 'react'; +import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; import type { DataViewBase, EsQueryConfig, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -44,6 +45,7 @@ import { SelfStyledEmbeddable, FilterableEmbeddable, } from '@kbn/embeddable-plugin/public'; +import { euiThemeVars } from '@kbn/ui-theme'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; import type { @@ -54,7 +56,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 { BrushTriggerEvent, ClickTriggerEvent, Warnings } from '@kbn/charts-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { getExecutionContextEvents, trackUiCounterEvents } from '../lens_ui_telemetry'; import { Document } from '../persistence'; @@ -79,7 +81,7 @@ import { getLensInspectorService, LensInspector } from '../lens_inspector_servic import { SharingSavedObjectProps, VisualizationDisplayOptions } from '../types'; import { getActiveDatasourceIdFromDoc, getIndexPatternsObjects, inferTimeField } from '../utils'; import { getLayerMetaInfo, combineQueryAndFilters } from '../app_plugin/show_underlying_data'; -import { convertDataViewIntoLensIndexPattern } from '../indexpattern_service/loader'; +import { convertDataViewIntoLensIndexPattern } from '../data_views_service/loader'; export type LensSavedObjectAttributes = Omit; @@ -101,6 +103,7 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { renderMode?: RenderMode; style?: React.CSSProperties; className?: string; + noPadding?: boolean; onBrushEnd?: (data: BrushTriggerEvent['data']) => void; onLoad?: (isLoading: boolean, adapters?: Partial) => void; onFilter?: (data: ClickTriggerEvent['data']) => void; @@ -233,6 +236,7 @@ export class Embeddable private savedVis: Document | undefined; private expression: string | undefined | null; private domNode: HTMLElement | Element | undefined; + private warningDomNode: HTMLElement | Element | undefined; private subscription: Subscription; private isInitialized = false; private errors: ErrorMessage[] | undefined; @@ -497,6 +501,26 @@ export class Embeddable return isDirty; } + private handleWarnings(adapters?: Partial) { + const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis); + if (!activeDatasourceId || !adapters?.requests) return; + const activeDatasource = this.deps.datasourceMap[activeDatasourceId]; + const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId]; + const warnings: React.ReactNode[] = []; + this.deps.data.search.showWarnings(adapters.requests, (warning) => { + const warningMessage = activeDatasource.getSearchWarningMessages?.( + docDatasourceState, + warning + ); + + warnings.push(...(warningMessage || [])); + if (warningMessage && warningMessage.length) return true; + }); + if (warnings && this.warningDomNode) { + render(, this.warningDomNode); + } + } + private updateActiveData: ExpressionWrapperProps['onData$'] = (data, adapters) => { this.activeDataInfo.activeData = adapters?.tables?.tables; if (this.input.onLoad) { @@ -510,6 +534,8 @@ export class Embeddable loading: false, error: type === 'error' ? error : undefined, }); + + this.handleWarnings(adapters); }; private onRender: ExpressionWrapperProps['onRender$'] = () => { @@ -625,6 +651,19 @@ export class Embeddable }} noPadding={this.visDisplayOptions?.noPadding} /> +
{ + if (el) { + this.warningDomNode = el; + } + }} + /> , domNode ); @@ -665,6 +704,7 @@ export class Embeddable this.savedVis.state.filters, this.savedVis.references ), + disableShardWarnings: true, }; if (this.externalSearchContext.query) { @@ -977,6 +1017,17 @@ export class Embeddable ) { return; } - return this.deps.visualizationMap[this.savedVis.visualizationType].getDisplayOptions!(); + + let displayOptions = + this.deps.visualizationMap[this.savedVis.visualizationType].getDisplayOptions!(); + + if (this.input.noPadding !== undefined) { + displayOptions = { + ...displayOptions, + noPadding: this.input.noPadding, + }; + } + + return displayOptions; } } 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 7a3986ec3f5ba..f3dea6eb9efe9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx @@ -9,7 +9,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { createMockedDragDropContext } from './mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { + dataViewPluginMocks, + Start as DataViewPublicStart, +} from '@kbn/data-views-plugin/public/mocks'; import { InnerIndexPatternDataPanel, IndexPatternDataPanel, Props } from './datapanel'; import { FieldList } from './field_list'; import { FieldItem } from './field_item'; @@ -31,7 +34,9 @@ import { createIndexPatternServiceMock } from '../mocks/data_views_service_mock' import { createMockFramePublicAPI } from '../mocks'; import { DataViewsState } from '../state_management'; import { ExistingFieldsMap, FramePublicAPI, IndexPattern } from '../types'; -import { IndexPatternServiceProps } from '../indexpattern_service/service'; +import { IndexPatternServiceProps } from '../data_views_service/service'; +import { FieldSpec, DataView } from '@kbn/data-views-plugin/public'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; const fieldsOne = [ { @@ -275,13 +280,33 @@ const dslQuery = { bool: { must: [], filter: [], should: [], must_not: [] } }; ReactDOM.createPortal = jest.fn((element) => element); describe('IndexPattern Data Panel', () => { + const indexPatterns = { + a: { + id: 'a', + title: 'aaa', + timeFieldName: 'atime', + fields: [{ name: 'aaa_field_1' }, { name: 'aaa_field_2' }], + getFieldByName: getFieldByNameFactory([]), + hasRestrictions: false, + }, + b: { + id: 'b', + title: 'bbb', + timeFieldName: 'btime', + fields: [{ name: 'bbb_field_1' }, { name: 'bbb_field_2' }], + getFieldByName: getFieldByNameFactory([]), + hasRestrictions: false, + }, + }; let defaultProps: Parameters[0] & { showNoDataPopover: () => void; }; let core: ReturnType; + let dataViews: DataViewPublicStart; beforeEach(() => { core = coreMock.createStart(); + dataViews = dataViewPluginMocks.createStartContract(); defaultProps = { data: dataPluginMock.createStartContract(), dataViews: dataViewPluginMocks.createStartContract(), @@ -302,7 +327,7 @@ describe('IndexPattern Data Panel', () => { dropOntoWorkspace: jest.fn(), hasSuggestionForField: jest.fn(() => false), uiActions: uiActionsPluginMock.createStartContract(), - indexPatternService: createIndexPatternServiceMock(), + indexPatternService: createIndexPatternServiceMock({ core, dataViews }), frame: getFrameAPIMock(), }; }); @@ -328,19 +353,29 @@ describe('IndexPattern Data Panel', () => { describe('loading existence data', () => { function testProps(updateIndexPatterns: IndexPatternServiceProps['updateIndexPatterns']) { - core.http.post.mockImplementation(async (path) => { - const parts = (path as unknown as string).split('/'); - const indexPatternTitle = parts[parts.length - 1]; - return { - indexPatternTitle: `${indexPatternTitle}_testtitle`, - existingFieldNames: ['field_1', 'field_2'].map( - (fieldName) => `${indexPatternTitle}_${fieldName}` - ), - }; + core.uiSettings.get.mockImplementation((key: string) => { + if (key === UI_SETTINGS.META_FIELDS) { + return []; + } + }); + dataViews.getFieldsForIndexPattern.mockImplementation((dataView) => { + return Promise.resolve([ + { name: `${dataView.title}_field_1` }, + { name: `${dataView.title}_field_2` }, + ]) as Promise; + }); + dataViews.get.mockImplementation(async (id: string) => { + return [indexPatterns.a, indexPatterns.b].find( + (indexPattern) => indexPattern.id === id + ) as unknown as DataView; }); return { ...defaultProps, - indexPatternService: createIndexPatternServiceMock({ updateIndexPatterns, core }), + indexPatternService: createIndexPatternServiceMock({ + updateIndexPatterns, + core, + dataViews, + }), setState: jest.fn(), dragDropContext: { ...createMockedDragDropContext(), @@ -352,24 +387,7 @@ describe('IndexPattern Data Panel', () => { indexPatternRefs: [], existingFields: {}, isFirstExistenceFetch: false, - indexPatterns: { - a: { - id: 'a', - title: 'aaa', - timeFieldName: 'atime', - fields: [], - getFieldByName: getFieldByNameFactory([]), - hasRestrictions: false, - }, - b: { - id: 'b', - title: 'bbb', - timeFieldName: 'btime', - fields: [], - getFieldByName: getFieldByNameFactory([]), - hasRestrictions: false, - }, - }, + indexPatterns, }, } as unknown as FramePublicAPI, state: { @@ -390,7 +408,7 @@ describe('IndexPattern Data Panel', () => { stateChanges?: Partial, propChanges?: Partial ) { - const inst = mountWithIntl(); + const inst = mountWithIntl(); await act(async () => { inst.update(); @@ -400,10 +418,10 @@ describe('IndexPattern Data Panel', () => { await act(async () => { inst.setProps({ ...props, - ...((propChanges as object) || {}), + ...(propChanges || {}), state: { ...props.state, - ...((stateChanges as object) || {}), + ...(stateChanges || {}), }, }); inst.update(); @@ -418,9 +436,9 @@ describe('IndexPattern Data Panel', () => { expect(updateIndexPatterns).toHaveBeenCalledWith( { existingFields: { - a_testtitle: { - a_field_1: true, - a_field_2: true, + aaa: { + aaa_field_1: true, + aaa_field_2: true, }, }, isFirstExistenceFetch: false, @@ -438,13 +456,13 @@ describe('IndexPattern Data Panel', () => { expect(updateIndexPatterns).toHaveBeenCalledWith( { existingFields: { - a_testtitle: { - a_field_1: true, - a_field_2: true, + aaa: { + aaa_field_1: true, + aaa_field_2: true, }, - b_testtitle: { - b_field_1: true, - b_field_2: true, + bbb: { + bbb_field_1: true, + bbb_field_2: true, }, }, isFirstExistenceFetch: false, @@ -473,32 +491,41 @@ describe('IndexPattern Data Panel', () => { }); expect(updateIndexPatterns).toHaveBeenCalledTimes(2); - expect(core.http.post).toHaveBeenCalledTimes(2); - - expect(core.http.post).toHaveBeenCalledWith('/api/lens/existing_fields/a', { - body: JSON.stringify({ - dslQuery, - fromDate: '2019-01-01', - toDate: '2020-01-01', - timeFieldName: 'atime', - }), + expect(dataViews.getFieldsForIndexPattern).toHaveBeenCalledTimes(2); + expect(dataViews.get).toHaveBeenCalledTimes(2); + + const firstCall = dataViews.getFieldsForIndexPattern.mock.calls[0]; + expect(firstCall[0]).toEqual(indexPatterns.a); + expect(firstCall[1]?.filter?.bool?.filter).toContainEqual(dslQuery); + expect(firstCall[1]?.filter?.bool?.filter).toContainEqual({ + range: { + atime: { + format: 'strict_date_optional_time', + gte: '2019-01-01', + lte: '2020-01-01', + }, + }, }); - expect(core.http.post).toHaveBeenCalledWith('/api/lens/existing_fields/a', { - body: JSON.stringify({ - dslQuery, - fromDate: '2019-01-01', - toDate: '2020-01-02', - timeFieldName: 'atime', - }), + const secondCall = dataViews.getFieldsForIndexPattern.mock.calls[1]; + expect(secondCall[0]).toEqual(indexPatterns.a); + expect(secondCall[1]?.filter?.bool?.filter).toContainEqual(dslQuery); + expect(secondCall[1]?.filter?.bool?.filter).toContainEqual({ + range: { + atime: { + format: 'strict_date_optional_time', + gte: '2019-01-01', + lte: '2020-01-02', + }, + }, }); expect(updateIndexPatterns).toHaveBeenCalledWith( { existingFields: { - a_testtitle: { - a_field_1: true, - a_field_2: true, + aaa: { + aaa_field_1: true, + aaa_field_2: true, }, }, isFirstExistenceFetch: false, @@ -521,34 +548,42 @@ describe('IndexPattern Data Panel', () => { expect(updateIndexPatterns).toHaveBeenCalledTimes(2); - expect(core.http.post).toHaveBeenCalledWith('/api/lens/existing_fields/a', { - body: JSON.stringify({ - dslQuery, - fromDate: '2019-01-01', - toDate: '2020-01-01', - timeFieldName: 'atime', - }), + const secondCall = dataViews.getFieldsForIndexPattern.mock.calls[1]; + expect(secondCall[0]).toEqual(indexPatterns.a); + expect(secondCall[1]?.filter?.bool?.filter).toContainEqual(dslQuery); + expect(secondCall[1]?.filter?.bool?.filter).toContainEqual({ + range: { + atime: { + format: 'strict_date_optional_time', + gte: '2019-01-01', + lte: '2020-01-01', + }, + }, }); - expect(core.http.post).toHaveBeenCalledWith('/api/lens/existing_fields/b', { - body: JSON.stringify({ - dslQuery, - fromDate: '2019-01-01', - toDate: '2020-01-01', - timeFieldName: 'btime', - }), + const thirdCall = dataViews.getFieldsForIndexPattern.mock.calls[2]; + expect(thirdCall[0]).toEqual(indexPatterns.b); + expect(thirdCall[1]?.filter?.bool?.filter).toContainEqual(dslQuery); + expect(thirdCall[1]?.filter?.bool?.filter).toContainEqual({ + range: { + btime: { + format: 'strict_date_optional_time', + gte: '2019-01-01', + lte: '2020-01-01', + }, + }, }); expect(updateIndexPatterns).toHaveBeenCalledWith( { existingFields: { - a_testtitle: { - a_field_1: true, - a_field_2: true, + aaa: { + aaa_field_1: true, + aaa_field_2: true, }, - b_testtitle: { - b_field_1: true, - b_field_2: true, + bbb: { + bbb_field_1: true, + bbb_field_2: true, }, }, isFirstExistenceFetch: false, @@ -573,20 +608,15 @@ describe('IndexPattern Data Panel', () => { let overlapCount = 0; const props = testProps(updateIndexPatterns); - core.http.post.mockImplementation((path) => { + dataViews.getFieldsForIndexPattern.mockImplementation((dataView) => { if (queryCount) { ++overlapCount; } ++queryCount; - - const parts = (path as unknown as string).split('/'); - const indexPatternTitle = parts[parts.length - 1]; - const result = Promise.resolve({ - indexPatternTitle, - existingFieldNames: ['field_1', 'field_2'].map( - (fieldName) => `${indexPatternTitle}_${fieldName}` - ), - }); + const result = Promise.resolve([ + { name: `${dataView.title}_field_1` }, + { name: `${dataView.title}_field_2` }, + ]) as Promise; result.then(() => --queryCount); @@ -613,7 +643,7 @@ describe('IndexPattern Data Panel', () => { inst.update(); }); - expect(core.http.post).toHaveBeenCalledTimes(2); + expect(dataViews.getFieldsForIndexPattern).toHaveBeenCalledTimes(2); expect(overlapCount).toEqual(0); }); @@ -628,13 +658,14 @@ describe('IndexPattern Data Panel', () => { }; await testExistenceLoading(props, undefined, undefined); - expect((props.core.http.post as jest.Mock).mock.calls[0][1].body).toContain( - JSON.stringify({ + const firstCall = dataViews.getFieldsForIndexPattern.mock.calls[0]; + expect(firstCall[1]?.filter?.bool?.filter).toContainEqual({ + bool: { must_not: { match_all: {}, }, - }) - ); + }, + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index 1d46cef70bc9d..01053dbb49a2b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -16,9 +16,9 @@ import { EuiPopover, EuiCallOut, EuiFormControlLayout, - EuiIcon, EuiFilterButton, EuiScreenReaderOnly, + EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { EsQueryConfig, Query, Filter } from '@kbn/es-query'; @@ -47,7 +47,7 @@ import { LensFieldIcon } from '../shared_components/field_picker/lens_field_icon import { getFieldType } from './pure_utils'; import { FieldGroups, FieldList } from './field_list'; import { fieldContainsData, fieldExists } from '../shared_components'; -import { IndexPatternServiceAPI } from '../indexpattern_service/service'; +import { IndexPatternServiceAPI } from '../data_views_service/service'; export type Props = Omit< DatasourceDataPanelProps, @@ -143,15 +143,16 @@ export function IndexPatternDataPanel({ indexPatternService, frame, onIndexPatternRefresh, + usedIndexPatterns, }: Props) { const { indexPatterns, indexPatternRefs, existingFields, isFirstExistenceFetch } = frame.dataViews; const { currentIndexPatternId } = state; const indexPatternList = uniq( - Object.values(state.layers) - .map((l) => l.indexPatternId) - .concat(currentIndexPatternId) + ( + usedIndexPatterns ?? Object.values(state.layers).map(({ indexPatternId }) => indexPatternId) + ).concat(currentIndexPatternId) ) .filter((id) => !!indexPatterns[id]) .sort() @@ -283,7 +284,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ onIndexPatternRefresh, }: Omit< DatasourceDataPanelProps, - 'state' | 'setState' | 'showNoDataPopover' | 'core' | 'onChangeIndexPattern' + 'state' | 'setState' | 'showNoDataPopover' | 'core' | 'onChangeIndexPattern' | 'usedIndexPatterns' > & { data: DataPublicPluginStart; dataViews: DataViewsPublicPluginStart; 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 56acffaed1ffb..3f7c4f1e9b006 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 @@ -55,8 +55,8 @@ jest.mock('./reference_editor', () => ({ ReferenceEditor: () => null, })); jest.mock('../loader'); -jest.mock('../query_input', () => ({ - QueryInput: () => null, +jest.mock('@kbn/unified-search-plugin/public', () => ({ + QueryStringInput: () => null, })); jest.mock('../operations'); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx index 5117e935c59d6..1c68844079fa6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx @@ -19,10 +19,8 @@ import { } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; import { GenericIndexPatternColumn, operationDefinitionMap } from '../operations'; -import { validateQuery } from '../operations/definitions/filters'; -import { QueryInput } from '../query_input'; import type { IndexPatternLayer } from '../types'; -import { useDebouncedValue } from '../../shared_components'; +import { QueryInput, useDebouncedValue, validateQuery } from '../../shared_components'; import type { IndexPattern } from '../../types'; const filterByLabel = i18n.translate('xpack.lens.indexPattern.filterBy.label', { 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 e91aa1b286bec..66c554d7bbe9a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -2543,7 +2543,12 @@ describe('IndexPattern Data Source', () => { ); it('should return mismatched time shifts', () => { - const warnings = indexPatternDatasource.getWarningMessages!(state, framePublicAPI, () => {}); + const warnings = indexPatternDatasource.getWarningMessages!( + state, + framePublicAPI, + {}, + () => {} + ); expect(extractTranslationIdsFromWarnings(warnings)).toMatchInlineSnapshot(` Array [ @@ -2556,7 +2561,12 @@ describe('IndexPattern Data Source', () => { it('should show different types of warning messages', () => { framePublicAPI.activeData!.first.columns[1].meta.sourceParams!.hasPrecisionError = true; - const warnings = indexPatternDatasource.getWarningMessages!(state, framePublicAPI, () => {}); + const warnings = indexPatternDatasource.getWarningMessages!( + state, + framePublicAPI, + {}, + () => {} + ); expect(extractTranslationIdsFromWarnings(warnings)).toMatchInlineSnapshot(` Array [ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index c806eb525da37..da317d5ea04be 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -61,7 +61,12 @@ import { getDatasourceSuggestionsForVisualizeCharts, } from './indexpattern_suggestions'; -import { getFiltersInLayer, getVisualDefaultsForLayer, isColumnInvalid } from './utils'; +import { + getFiltersInLayer, + getTSDBRollupWarningMessages, + getVisualDefaultsForLayer, + isColumnInvalid, +} from './utils'; import { normalizeOperationDataType, isDraggedField } from './pure_utils'; import { LayerPanel } from './layerpanel'; import { @@ -105,6 +110,9 @@ export function columnToOperation( ? 'version' : undefined, hasTimeShift: Boolean(timeShift), + interval: isColumnOfType('date_histogram', column) + ? column.params.interval + : undefined, }; } @@ -653,7 +661,7 @@ export function getIndexPatternDatasource({ }); return messages.length ? messages : undefined; }, - getWarningMessages: (state, frame, setState) => { + getWarningMessages: (state, frame, adapters, setState) => { return [ ...(getStateTimeShiftWarningMessages(data.datatableUtilities, state, frame) || []), ...getPrecisionErrorWarningMessages( @@ -665,6 +673,9 @@ export function getIndexPatternDatasource({ ), ]; }, + getSearchWarningMessages: (state, warning) => { + return [...getTSDBRollupWarningMessages(state, warning)]; + }, getDeprecationMessages: () => { const deprecatedMessages: React.ReactNode[] = []; const useFieldExistenceSamplingKey = 'lens:useFieldExistenceSampling'; @@ -743,6 +754,9 @@ export function getIndexPatternDatasource({ getUsedDataView: (state: IndexPatternPrivateState, layerId: string) => { return state.layers[layerId].indexPatternId; }, + getUsedDataViews: (state) => { + return Object.values(state.layers).map(({ indexPatternId }) => indexPatternId); + }, }; return indexPatternDatasource; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index e90ed2c781d5a..3950f9a02dadf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -2214,6 +2214,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'interval', isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -2225,6 +2226,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ratio', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], @@ -2289,6 +2291,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ordinal', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, { @@ -2300,6 +2303,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'interval', isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -2311,6 +2315,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ratio', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], @@ -2396,6 +2401,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ordinal', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, { @@ -2407,6 +2413,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'interval', isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -2418,6 +2425,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ratio', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], @@ -2526,6 +2534,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'ordinal', isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, { @@ -2537,6 +2546,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'interval', isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -2548,6 +2558,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], @@ -3126,6 +3137,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: 'interval', isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -3137,6 +3149,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, { @@ -3148,6 +3161,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], @@ -3213,6 +3227,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: 'auto', }, }, { @@ -3224,6 +3239,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, { @@ -3235,6 +3251,7 @@ describe('IndexPattern Data Source suggestions', () => { scale: undefined, isStaticValue: false, hasTimeShift: false, + interval: undefined, }, }, ], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 80f6eaecd3a8a..3d641ca004a3b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -14,7 +14,7 @@ import { } from './loader'; import { IndexPatternPersistedState, IndexPatternPrivateState } from './types'; import { DateHistogramIndexPatternColumn, TermsIndexPatternColumn } from './operations'; -import { sampleIndexPatterns } from '../indexpattern_service/mocks'; +import { sampleIndexPatterns } from '../data_views_service/mocks'; const createMockStorage = (lastData?: Record) => { return { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx index 0fc67aca36747..475761c94ec54 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx @@ -12,8 +12,8 @@ import { EuiPopover, EuiLink } from '@elastic/eui'; import { createMockedIndexPattern } from '../../../mocks'; import { FilterPopover } from './filter_popover'; import { LabelInput } from '../shared_components'; -import { QueryInput } from '../../../query_input'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; +import { QueryInput } from '../../../../shared_components'; jest.mock('.', () => ({ isQueryValid: () => true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx index 95021dd22900f..17314bbc991c0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx @@ -10,10 +10,12 @@ import './filter_popover.scss'; import React from 'react'; import { EuiPopover, EuiSpacer } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; +// Need to keep it separate to make it work Jest mocks in dimension_panel tests +// import { QueryInput } from '../../../../shared_components/query_input'; +import { isQueryValid, QueryInput } from '../../../../shared_components'; import { IndexPattern } from '../../../../types'; -import { FilterValue, defaultLabel, isQueryValid } from '.'; +import { FilterValue, defaultLabel } from '.'; import { LabelInput } from '../shared_components'; -import { QueryInput } from '../../../query_input'; export const FilterPopover = ({ filter, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index cee188b6f483a..8aff5ac3f850a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -7,7 +7,6 @@ import './filters.scss'; import React, { useState } from 'react'; -import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; import { omit } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiLink, htmlIdGenerator } from '@elastic/eui'; @@ -15,12 +14,17 @@ import type { Query } from '@kbn/es-query'; import type { AggFunctionsMapping } from '@kbn/data-plugin/public'; import { queryFilterToAst } from '@kbn/data-plugin/common'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; +import { + DragDropBuckets, + DraggableBucketContainer, + isQueryValid, + NewBucketButton, +} from '../../../../shared_components'; import { IndexPattern } from '../../../../types'; import { updateColumnParam } from '../../layer_helpers'; import type { OperationDefinition } from '..'; import type { BaseIndexPatternColumn } from '../column_types'; import { FilterPopover } from './filter_popover'; -import { DragDropBuckets, DraggableBucketContainer, NewBucketButton } from '../shared_components'; const generateId = htmlIdGenerator(); const OPERATION_NAME = 'filters'; @@ -54,29 +58,6 @@ const defaultFilter: Filter = { label: '', }; -export const validateQuery = (input: Query | undefined, indexPattern: IndexPattern) => { - let isValid = true; - let error: string | undefined; - - try { - if (input) { - if (input.language === 'kuery') { - toElasticsearchQuery(fromKueryExpression(input.query), indexPattern); - } else { - luceneStringToDsl(input.query); - } - } - } catch (e) { - isValid = false; - error = e.message; - } - - return { isValid, error }; -}; - -export const isQueryValid = (input: Query, indexPattern: IndexPattern) => - validateQuery(input, indexPattern).isValid; - export interface FiltersIndexPatternColumn extends BaseIndexPatternColumn { operationType: typeof OPERATION_NAME; params: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.test.ts index 3286696308d51..c854595757a0c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.test.ts @@ -10,13 +10,13 @@ import { createFormulaPublicApi, FormulaPublicApi } from './formula_public_api'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DateHistogramIndexPatternColumn, PersistedIndexPatternLayer } from '../../../types'; -import { convertDataViewIntoLensIndexPattern } from '../../../../indexpattern_service/loader'; +import { convertDataViewIntoLensIndexPattern } from '../../../../data_views_service/loader'; jest.mock('./parse', () => ({ insertOrReplaceFormulaColumn: jest.fn().mockReturnValue({}), })); -jest.mock('../../../../indexpattern_service/loader', () => ({ +jest.mock('../../../../data_views_service/loader', () => ({ convertDataViewIntoLensIndexPattern: jest.fn((v) => v), })); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.ts index f5080de84d67d..4085ec931d3a6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula_public_api.ts @@ -6,7 +6,7 @@ */ import type { DataView } from '@kbn/data-views-plugin/public'; -import { convertDataViewIntoLensIndexPattern } from '../../../../indexpattern_service/loader'; +import { convertDataViewIntoLensIndexPattern } from '../../../../data_views_service/loader'; import type { IndexPattern } from '../../../../types'; import type { PersistedIndexPatternLayer } from '../../../types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 60d5a76a3085e..9afd9311f026c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -23,15 +23,15 @@ import { keys, } from '@elastic/eui'; import { IFieldFormat } from '@kbn/field-formats-plugin/common'; -import { useDebounceWithOptions } from '../../../../shared_components'; -import { RangeTypeLens, isValidRange } from './ranges'; -import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; import { - NewBucketButton, DragDropBuckets, DraggableBucketContainer, - LabelInput, -} from '../shared_components'; + NewBucketButton, + useDebounceWithOptions, +} from '../../../../shared_components'; +import { RangeTypeLens, isValidRange } from './ranges'; +import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; +import { LabelInput } from '../shared_components'; import { isValidNumber } from '../helpers'; const generateId = htmlIdGenerator(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 43603d94154d4..bee30a2e0400f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -26,7 +26,7 @@ import { SLICES, } from './constants'; import { RangePopover } from './advanced_editor'; -import { DragDropBuckets } from '../shared_components'; +import { DragDropBuckets } from '../../../../shared_components'; import { getFieldByNameFactory } from '../../../pure_helpers'; import { IndexPattern } from '../../../../types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx index 1dbd20d8767d0..e9e7dba840291 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx @@ -4,6 +4,5 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export * from './buckets'; export * from './label_input'; export * from './form_row'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx index 5ae10751e64c4..f140fdb9807ac 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx @@ -18,12 +18,16 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ExistingFieldsMap, IndexPattern } from '../../../../types'; -import { TooltipWrapper, useDebouncedValue } from '../../../../shared_components'; +import { + DragDropBuckets, + NewBucketButton, + TooltipWrapper, + useDebouncedValue, +} from '../../../../shared_components'; import { FieldSelect } from '../../../dimension_panel/field_select'; import type { TermsIndexPatternColumn } from './types'; import type { OperationSupportMatrix } from '../../../dimension_panel'; import { supportedTypes } from './constants'; -import { DragDropBuckets, NewBucketButton } from '../shared_components'; const generateId = htmlIdGenerator(); export const MAX_MULTI_FIELDS_SIZE = 3; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx index 0f485d71a7f35..ce2f5edad12c5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx @@ -14,10 +14,15 @@ import { TimeRange } from '@kbn/es-query'; import { EuiLink, EuiTextColor, EuiButton, EuiSpacer } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { groupBy, escape } from 'lodash'; +import { groupBy, escape, uniq } from 'lodash'; import type { Query } from '@kbn/data-plugin/common'; +import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types'; import type { FramePublicAPI, IndexPattern, StateSetter } from '../types'; -import type { IndexPatternLayer, IndexPatternPrivateState } from './types'; +import type { + IndexPatternLayer, + IndexPatternPersistedState, + IndexPatternPrivateState, +} from './types'; import type { ReferenceBasedIndexPatternColumn } from './operations/definitions/column_types'; import { @@ -33,12 +38,13 @@ import { } from './operations'; import { getInvalidFieldMessage, isColumnOfType } from './operations/definitions/helpers'; -import { FiltersIndexPatternColumn, isQueryValid } from './operations/definitions/filters'; +import { FiltersIndexPatternColumn } from './operations/definitions/filters'; import { hasField } from './pure_utils'; import { mergeLayer } from './state_helpers'; import { supportsRarityRanking } from './operations/definitions/terms'; import { DEFAULT_MAX_DOC_COUNT } from './operations/definitions/terms/constants'; import { getOriginalId } from '../../common/expressions'; +import { isQueryValid } from '../shared_components'; export function isColumnInvalid( layer: IndexPatternLayer, @@ -159,6 +165,46 @@ const accuracyModeEnabledWarning = (columnName: string, docLink: string) => ( /> ); +export function getTSDBRollupWarningMessages( + state: IndexPatternPersistedState, + warning: SearchResponseWarning +) { + if (state) { + const hasTSDBRollupWarnings = + warning.type === 'shard_failure' && + warning.reason.type === 'unsupported_aggregation_on_downsampled_index'; + if (!hasTSDBRollupWarnings) { + return []; + } + return Object.values(state.layers).flatMap((layer) => + uniq( + Object.values(layer.columns) + .filter((col) => + [ + 'median', + 'percentile', + 'percentile_rank', + 'last_value', + 'unique_count', + 'standard_deviation', + ].includes(col.operationType) + ) + .map((col) => col.label) + ).map((label) => + i18n.translate('xpack.lens.indexPattern.tsdbRollupWarning', { + defaultMessage: + '"{label}" does not work for all indices in the selected data view because it\'s using a function which is not supported on rolled up data. Please edit the visualization to use another function or change the time range.', + values: { + label, + }, + }) + ) + ); + } + + return []; +} + export function getPrecisionErrorWarningMessages( datatableUtilities: DatatableUtilitiesService, state: IndexPatternPrivateState, diff --git a/x-pack/plugins/lens/public/mocks/data_plugin_mock.ts b/x-pack/plugins/lens/public/mocks/data_plugin_mock.ts index 8260c71b04981..161f14359d588 100644 --- a/x-pack/plugins/lens/public/mocks/data_plugin_mock.ts +++ b/x-pack/plugins/lens/public/mocks/data_plugin_mock.ts @@ -59,6 +59,7 @@ export function mockDataPlugin( getSessionId: jest.fn(() => currentSessionId), getSession$: jest.fn(() => sessionIdSubject.asObservable()), }, + showWarnings: jest.fn(), }; } diff --git a/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts b/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts index e2a07c717ee07..2846e46a27d6c 100644 --- a/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts +++ b/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts @@ -5,30 +5,30 @@ * 2.0. */ -import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { coreMock } from '@kbn/core/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { createIndexPatternService, IndexPatternServiceProps, IndexPatternServiceAPI, -} from '../indexpattern_service/service'; +} from '../data_views_service/service'; export function createIndexPatternServiceMock({ core = coreMock.createStart(), - uiSettings = uiSettingsServiceMock.createStartContract(), dataViews = dataViewPluginMocks.createStartContract(), uiActions = uiActionsPluginMock.createStartContract(), + data = dataPluginMock.createStartContract(), updateIndexPatterns = jest.fn(), replaceIndexPattern = jest.fn(), }: Partial = {}): IndexPatternServiceAPI { return createIndexPatternService({ core, - uiSettings, + dataViews, + data, updateIndexPatterns, replaceIndexPattern, - dataViews, uiActions, }); } diff --git a/x-pack/plugins/lens/public/mocks/datasource_mock.ts b/x-pack/plugins/lens/public/mocks/datasource_mock.ts index 8111270cc5d12..eec1d062131e7 100644 --- a/x-pack/plugins/lens/public/mocks/datasource_mock.ts +++ b/x-pack/plugins/lens/public/mocks/datasource_mock.ts @@ -61,6 +61,7 @@ export function createMockDatasource(id: string): DatasourceMock { isValidColumn: jest.fn(), isEqual: jest.fn(), getUsedDataView: jest.fn(), + getUsedDataViews: jest.fn(), onRefreshIndexPattern: jest.fn(), }; } diff --git a/x-pack/plugins/lens/public/mocks/index.ts b/x-pack/plugins/lens/public/mocks/index.ts index 0d90e719ebade..4cfdfbad661af 100644 --- a/x-pack/plugins/lens/public/mocks/index.ts +++ b/x-pack/plugins/lens/public/mocks/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { createMockDataViewsState } from '../data_views_service/mocks'; import { FramePublicAPI, FrameDatasourceAPI } from '../types'; export { mockDataPlugin } from './data_plugin_mock'; export { @@ -29,28 +30,36 @@ export { lensPluginMock } from './lens_plugin_mock'; export type FrameMock = jest.Mocked; -export const createMockFramePublicAPI = (): FrameMock => ({ - datasourceLayers: {}, - dateRange: { fromDate: '2022-03-17T08:25:00.000Z', toDate: '2022-04-17T08:25:00.000Z' }, - dataViews: { - indexPatterns: {}, - indexPatternRefs: [], - isFirstExistenceFetch: true, - existingFields: {}, +export const createMockFramePublicAPI = ({ + datasourceLayers, + dateRange, + dataViews, + activeData, +}: Partial = {}): FrameMock => ({ + datasourceLayers: datasourceLayers ?? {}, + dateRange: dateRange ?? { + fromDate: '2022-03-17T08:25:00.000Z', + toDate: '2022-04-17T08:25:00.000Z', }, + dataViews: createMockDataViewsState(dataViews), + activeData, }); export type FrameDatasourceMock = jest.Mocked; -export const createMockFrameDatasourceAPI = (): FrameDatasourceMock => ({ - datasourceLayers: {}, - dateRange: { fromDate: '2022-03-17T08:25:00.000Z', toDate: '2022-04-17T08:25:00.000Z' }, - query: { query: '', language: 'lucene' }, - filters: [], - dataViews: { - indexPatterns: {}, - indexPatternRefs: [], - isFirstExistenceFetch: true, - existingFields: {}, +export const createMockFrameDatasourceAPI = ({ + datasourceLayers, + dateRange, + dataViews, + query, + filters, +}: Partial = {}): FrameDatasourceMock => ({ + datasourceLayers: datasourceLayers ?? {}, + dateRange: dateRange ?? { + fromDate: '2022-03-17T08:25:00.000Z', + toDate: '2022-04-17T08:25:00.000Z', }, + query: query ?? { query: '', language: 'lucene' }, + filters: filters ?? [], + dataViews: createMockDataViewsState(dataViews), }); diff --git a/x-pack/plugins/lens/public/mocks/store_mocks.tsx b/x-pack/plugins/lens/public/mocks/store_mocks.tsx index e63a79693cadb..8320f429e9d5a 100644 --- a/x-pack/plugins/lens/public/mocks/store_mocks.tsx +++ b/x-pack/plugins/lens/public/mocks/store_mocks.tsx @@ -6,9 +6,7 @@ */ import React from 'react'; -// eslint-disable-next-line import/no-extraneous-dependencies import { ReactWrapper } from 'enzyme'; -// eslint-disable-next-line import/no-extraneous-dependencies import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; import { Provider } from 'react-redux'; import { act } from 'react-dom/test-utils'; diff --git a/x-pack/plugins/lens/public/mocks/visualization_mock.ts b/x-pack/plugins/lens/public/mocks/visualization_mock.ts index f002af43f88c8..23700094dc7c6 100644 --- a/x-pack/plugins/lens/public/mocks/visualization_mock.ts +++ b/x-pack/plugins/lens/public/mocks/visualization_mock.ts @@ -11,7 +11,7 @@ import { Visualization, VisualizationMap } from '../types'; export function createMockVisualization(id = 'testVis'): jest.Mocked { return { id, - clearLayer: jest.fn((state, _layerId) => state), + clearLayer: jest.fn((state, _layerId, _indexPatternId) => state), removeLayer: jest.fn(), getLayerIds: jest.fn((_state) => ['layer1']), getSupportedLayers: jest.fn(() => [{ type: layerTypes.DATA, label: 'Data Layer' }]), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx similarity index 97% rename from x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx rename to x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx index 101eca3ab1785..aba0cbfc40a6e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { EuiIcon } from '@elastic/eui'; -import { DragDropBuckets, DraggableBucketContainer } from '.'; +import { DragDropBuckets, DraggableBucketContainer } from './buckets'; jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx similarity index 100% rename from x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx rename to x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts index c22f071ceede3..924f678c1f96b 100644 --- a/x-pack/plugins/lens/public/shared_components/index.ts +++ b/x-pack/plugins/lens/public/shared_components/index.ts @@ -12,6 +12,12 @@ export { PalettePicker } from './palette_picker'; export { FieldPicker, LensFieldIcon, TruncatedLabel } from './field_picker'; export type { FieldOption, FieldOptionValue } from './field_picker'; export { ChangeIndexPattern, fieldExists, fieldContainsData } from './dataview_picker'; +export { QueryInput, isQueryValid, validateQuery } from './query_input'; +export { + NewBucketButton, + DraggableBucketContainer, + DragDropBuckets, +} from './drag_drop_bucket/buckets'; export { RangeInputField } from './range_input_field'; export { BucketAxisBoundsControl, diff --git a/x-pack/plugins/lens/public/shared_components/query_input/helpers.ts b/x-pack/plugins/lens/public/shared_components/query_input/helpers.ts new file mode 100644 index 0000000000000..1b598a10f6f50 --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/query_input/helpers.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Query } from '@kbn/es-query'; +import { toElasticsearchQuery, fromKueryExpression, luceneStringToDsl } from '@kbn/es-query'; +import { IndexPattern } from '../../types'; + +export const validateQuery = (input: Query | undefined, indexPattern: IndexPattern) => { + let isValid = true; + let error: string | undefined; + + try { + if (input) { + if (input.language === 'kuery') { + toElasticsearchQuery(fromKueryExpression(input.query), indexPattern); + } else { + luceneStringToDsl(input.query); + } + } + } catch (e) { + isValid = false; + error = e.message; + } + + return { isValid, error }; +}; + +export const isQueryValid = (input: Query, indexPattern: IndexPattern) => + validateQuery(input, indexPattern).isValid; diff --git a/x-pack/plugins/lens/public/shared_components/query_input/index.ts b/x-pack/plugins/lens/public/shared_components/query_input/index.ts new file mode 100644 index 0000000000000..b2934de916822 --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/query_input/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 { QueryInput } from './query_input'; +export { validateQuery, isQueryValid } from './helpers'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx b/x-pack/plugins/lens/public/shared_components/query_input/query_input.tsx similarity index 97% rename from x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx rename to x-pack/plugins/lens/public/shared_components/query_input/query_input.tsx index 583f6b28996c9..ecbc102eb751c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx +++ b/x-pack/plugins/lens/public/shared_components/query_input/query_input.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; -import { useDebouncedValue } from '../shared_components'; +import { useDebouncedValue } from '../debounced_value'; export const QueryInput = ({ value, diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index 0a000f75ee006..dc2425917bad8 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -97,7 +97,13 @@ export function loadInitial( }, autoApplyDisabled: boolean ) { - const { lensServices, datasourceMap, embeddableEditorIncomingState, initialContext } = storeDeps; + const { + lensServices, + datasourceMap, + embeddableEditorIncomingState, + initialContext, + visualizationMap, + } = storeDeps; const { resolvedDateRange, searchSessionId, isLinkedToOriginatingApp, ...emptyState } = getPreloadedState(storeDeps); const { attributeService, notifications, data, dashboardFeatureFlag } = lensServices; @@ -117,6 +123,8 @@ export function loadInitial( return initializeSources( { datasourceMap, + visualizationMap, + visualizationState: lens.visualization, datasourceStates: lens.datasourceStates, initialContext, adHocDataViews: lens.persistedDoc?.state.adHocDataViews, @@ -126,14 +134,14 @@ export function loadInitial( isFullEditor: true, } ) - .then(({ states, indexPatterns, indexPatternRefs }) => { + .then(({ datasourceStates, indexPatterns, indexPatternRefs }) => { store.dispatch( initEmpty({ newState: { ...emptyState, dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), searchSessionId: data.search.session.getSessionId() || data.search.session.start(), - datasourceStates: Object.entries(states).reduce( + datasourceStates: Object.entries(datasourceStates).reduce( (state, [datasourceId, datasourceState]) => ({ ...state, [datasourceId]: { @@ -187,9 +195,16 @@ export function loadInitial( const filters = data.query.filterManager.inject(doc.state.filters, doc.references); // Don't overwrite any pinned filters data.query.filterManager.setAppFilters(filters); + + const docVisualizationState = { + activeId: doc.visualizationType, + state: doc.state.visualization, + }; return initializeSources( { datasourceMap, + visualizationMap, + visualizationState: docVisualizationState, datasourceStates: docDatasourceStates, references: [...doc.references, ...(doc.state.internalReferences || [])], initialContext, @@ -200,7 +215,7 @@ export function loadInitial( }, { isFullEditor: true } ) - .then(({ states, indexPatterns, indexPatternRefs }) => { + .then(({ datasourceStates, visualizationState, indexPatterns, indexPatternRefs }) => { const currentSessionId = data.search.session.getSessionId(); store.dispatch( setState({ @@ -219,10 +234,10 @@ export function loadInitial( activeDatasourceId: getInitialDatasourceId(datasourceMap, doc), visualization: { activeId: doc.visualizationType, - state: doc.state.visualization, + state: visualizationState, }, dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), - datasourceStates: Object.entries(states).reduce( + datasourceStates: Object.entries(datasourceStates).reduce( (state, [datasourceId, datasourceState]) => ({ ...state, [datasourceId]: { diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts index d38c0aed16676..fba5c9c9abd54 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts @@ -227,6 +227,7 @@ describe('lensSlice', () => { removeLayer: (layerIds: unknown, layerId: string) => (layerIds as string[]).filter((id: string) => id !== layerId), insertLayer: (layerIds: unknown, layerId: string) => [...(layerIds as string[]), layerId], + getCurrentIndexPatternId: jest.fn(() => 'indexPattern1'), }; }; const datasourceStates = { diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index aaf34f62305c5..e7eaf6b9c3811 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -88,7 +88,10 @@ export const getPreloadedState = ({ }; export const setState = createAction>('lens/setState'); -export const onActiveDataChange = createAction('lens/onActiveDataChange'); +export const onActiveDataChange = createAction<{ + activeData: TableInspectorAdapter; + requestWarnings?: string[]; +}>('lens/onActiveDataChange'); export const setSaveable = createAction('lens/setSaveable'); export const enableAutoApply = createAction('lens/enableAutoApply'); export const disableAutoApply = createAction('lens/disableAutoApply'); @@ -222,10 +225,16 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { ...payload, }; }, - [onActiveDataChange.type]: (state, { payload }: PayloadAction) => { + [onActiveDataChange.type]: ( + state, + { + payload: { activeData, requestWarnings }, + }: PayloadAction<{ activeData: TableInspectorAdapter; requestWarnings?: string[] }> + ) => { return { ...state, - activeData: payload, + activeData, + requestWarnings, }; }, [setSaveable.type]: (state, { payload }: PayloadAction) => { @@ -279,6 +288,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { } ) => { const activeVisualization = visualizationMap[visualizationId]; + const activeDataSource = datasourceMap[state.activeDatasourceId!]; const isOnlyLayer = getRemoveOperation( activeVisualization, @@ -300,9 +310,13 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { } ); state.stagedPreview = undefined; + // reuse the activeDatasource current dataView id for the moment + const currentDataViewsId = activeDataSource.getCurrentIndexPatternId( + state.datasourceStates[state.activeDatasourceId!].state + ); state.visualization.state = isOnlyLayer || !activeVisualization.removeLayer - ? activeVisualization.clearLayer(state.visualization.state, layerId) + ? activeVisualization.clearLayer(state.visualization.state, layerId, currentDataViewsId) : activeVisualization.removeLayer(state.visualization.state, layerId); }, [changeIndexPattern.type]: ( @@ -343,15 +357,15 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { for (const visualizationId of visualizationIds) { const activeVisualization = visualizationId && - state.visualization.activeId !== visualizationId && + state.visualization.activeId === visualizationId && visualizationMap[visualizationId]; if (activeVisualization && layerId && activeVisualization?.onIndexPatternChange) { newState.visualization = { ...state.visualization, state: activeVisualization.onIndexPatternChange( state.visualization.state, - layerId, - indexPatternId + indexPatternId, + layerId ), }; } @@ -797,15 +811,20 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { } const activeVisualization = visualizationMap[state.visualization.activeId]; + const activeDatasource = datasourceMap[state.activeDatasourceId]; + // reuse the active datasource dataView id for the new layer + const currentDataViewsId = activeDatasource.getCurrentIndexPatternId( + state.datasourceStates[state.activeDatasourceId!].state + ); const visualizationState = activeVisualization.appendLayer!( state.visualization.state, layerId, - layerType + layerType, + currentDataViewsId ); const framePublicAPI = selectFramePublicAPI({ lens: current(state) }, datasourceMap); - const activeDatasource = datasourceMap[state.activeDatasourceId]; const { noDatasource } = activeVisualization .getSupportedLayers(visualizationState, framePublicAPI) diff --git a/x-pack/plugins/lens/public/state_management/selectors.ts b/x-pack/plugins/lens/public/state_management/selectors.ts index 447eccd1705fd..54e3b420bbe36 100644 --- a/x-pack/plugins/lens/public/state_management/selectors.ts +++ b/x-pack/plugins/lens/public/state_management/selectors.ts @@ -28,10 +28,13 @@ export const selectVisualization = (state: LensState) => state.lens.visualizatio export const selectStagedPreview = (state: LensState) => state.lens.stagedPreview; export const selectStagedActiveData = (state: LensState) => state.lens.stagedPreview?.activeData || state.lens.activeData; +export const selectStagedRequestWarnings = (state: LensState) => + state.lens.stagedPreview?.requestWarnings || state.lens.requestWarnings; export const selectAutoApplyEnabled = (state: LensState) => !state.lens.autoApplyDisabled; export const selectChangesApplied = (state: LensState) => !state.lens.autoApplyDisabled || Boolean(state.lens.changesApplied); export const selectDatasourceStates = (state: LensState) => state.lens.datasourceStates; +export const selectVisualizationState = (state: LensState) => state.lens.visualization; export const selectActiveDatasourceId = (state: LensState) => state.lens.activeDatasourceId; export const selectActiveData = (state: LensState) => state.lens.activeData; export const selectDataViews = (state: LensState) => state.lens.dataViews; @@ -61,6 +64,7 @@ export const selectExecutionContextSearch = createSelector(selectExecutionContex to: res.dateRange.toDate, }, filters: res.filters, + disableShardWarnings: true, })); const selectInjectedDependencies = (_state: LensState, dependencies: unknown) => dependencies; @@ -94,7 +98,9 @@ export const selectSavedObjectFormat = createSelector( { datasourceMap, visualizationMap, extractFilterReferences } ) => { const activeVisualization = - visualization.state && visualization.activeId && visualizationMap[visualization.activeId]; + visualization.state && visualization.activeId + ? visualizationMap[visualization.activeId] + : null; const activeDatasource = datasourceStates && activeDatasourceId && !datasourceStates[activeDatasourceId].isLoading ? datasourceMap[activeDatasourceId] @@ -129,6 +135,20 @@ export const selectSavedObjectFormat = createSelector( }); }); + let persistibleVisualizationState = visualization.state; + if (activeVisualization.getPersistableState) { + const { state: persistableState, savedObjectReferences } = + activeVisualization.getPersistableState(visualization.state); + persistibleVisualizationState = persistableState; + savedObjectReferences.forEach((r) => { + if (r.type === 'index-pattern' && adHocDataViews[r.id]) { + internalReferences.push(r); + } else { + references.push(r); + } + }); + } + const persistableAdHocDataViews = Object.fromEntries( Object.entries(adHocDataViews).map(([id, dataView]) => { const { references: dataViewReferences, state } = @@ -159,7 +179,7 @@ export const selectSavedObjectFormat = createSelector( type: 'lens', references, state: { - visualization: visualization.state, + visualization: persistibleVisualizationState, query, filters: [...persistableFilters, ...adHocFilters], datasourceStates: persistibleDatasourceStates, diff --git a/x-pack/plugins/lens/public/state_management/types.ts b/x-pack/plugins/lens/public/state_management/types.ts index 7b461338261fc..ceae38967c77d 100644 --- a/x-pack/plugins/lens/public/state_management/types.ts +++ b/x-pack/plugins/lens/public/state_management/types.ts @@ -41,6 +41,7 @@ export interface PreviewState { visualization: VisualizationState; datasourceStates: DatasourceStates; activeData?: TableInspectorAdapter; + requestWarnings?: string[]; } export interface EditorFrameState extends PreviewState { activeDatasourceId: string | null; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 506b3fe459db2..17950f8c5e50e 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -18,6 +18,7 @@ import type { Datatable, } from '@kbn/expressions-plugin/public'; import type { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { Adapters } from '@kbn/inspector-plugin/public'; import type { Query } from '@kbn/es-query'; import type { UiActionsStart, @@ -28,6 +29,7 @@ import type { ClickTriggerEvent, BrushTriggerEvent } from '@kbn/charts-plugin/pu import type { IndexPatternAggRestrictions } from '@kbn/data-plugin/public'; import type { FieldSpec, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { FieldFormatParams } from '@kbn/field-formats-plugin/common'; +import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types'; import type { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import type { DateRange, LayerType, SortingHint } from '../common'; import type { @@ -46,7 +48,7 @@ import { import type { LensInspector } from './lens_inspector_service'; import type { FormatSelectorOptions } from './indexpattern_datasource/dimension_panel/format_selector'; import type { DataViewsState } from './state_management/types'; -import type { IndexPatternServiceAPI } from './indexpattern_service/service'; +import type { IndexPatternServiceAPI } from './data_views_service/service'; import type { Document } from './persistence/saved_object_store'; export interface IndexPatternRef { @@ -116,8 +118,8 @@ export interface EditorFrameSetup { registerDatasource: ( datasource: Datasource | (() => Promise>) ) => void; - registerVisualization: ( - visualization: Visualization | (() => Promise>) + registerVisualization: ( + visualization: Visualization | (() => Promise>) ) => void; } @@ -414,11 +416,16 @@ export interface Datasource { getWarningMessages?: ( state: T, frame: FramePublicAPI, + adapters: Adapters, setState: StateSetter ) => React.ReactNode[] | undefined; getDeprecationMessages?: (state: T) => React.ReactNode[] | undefined; + /** + * The embeddable calls this function to display warnings about visualization on the dashboard + */ + getSearchWarningMessages?: (state: P, warning: SearchResponseWarning) => string[] | undefined; /** * Checks if the visualization created is time based, for example date histogram */ @@ -449,6 +456,10 @@ export interface Datasource { * Get the used DataView value from state */ getUsedDataView: (state: T, layerId: string) => string; + /** + * Get all the used DataViews from state + */ + getUsedDataViews: (state: T) => string[]; } export interface DatasourceFixAction { @@ -510,6 +521,7 @@ export interface DatasourceDataPanelProps { uiActions: UiActionsStart; indexPatternService: IndexPatternServiceAPI; frame: FramePublicAPI; + usedIndexPatterns?: string[]; } interface SharedDimensionProps { @@ -631,6 +643,7 @@ export interface Operation extends OperationMetadata { } export interface OperationMetadata { + interval?: string; // The output of this operation will have this data type dataType: DataType; // A bucketed operation is grouped by duplicate values, otherwise each row is @@ -665,12 +678,10 @@ export interface VisualizationConfigProps { export type VisualizationLayerWidgetProps = VisualizationConfigProps & { setState: (newState: T) => void; - onChangeIndexPattern: (indexPatternId: string, layerId: string) => void; + onChangeIndexPattern: (indexPatternId: string) => void; }; -export type VisualizationLayerHeaderContentProps = VisualizationLayerWidgetProps & { - defaultIndexPatternId: string; -}; +export type VisualizationLayerHeaderContentProps = VisualizationLayerWidgetProps; export interface VisualizationToolbarProps { setState: (newState: T) => void; @@ -885,18 +896,23 @@ export interface VisualizationDisplayOptions { noPadding?: boolean; } -export interface Visualization { +export interface Visualization { /** Plugin ID, such as "lnsXY" */ id: string; /** * Initialize is allowed to modify the state stored in memory. The initialize function * is called with a previous state in two cases: - * - Loadingn from a saved visualization + * - Loading from a saved visualization * - When using suggestions, the suggested state is passed in */ initialize: (addNewLayer: () => string, state?: T, mainPalette?: PaletteOutput) => T; + /** + * Retrieve the used DataViews in the visualization + */ + getUsedDataViews?: (state?: T) => string[]; + getMainPalette?: (state: T) => undefined | PaletteOutput; /** @@ -919,15 +935,18 @@ export interface Visualization { switchVisualizationType?: (visualizationTypeId: string, state: T) => T; /** Description is displayed as the clickable text in the chart switcher */ getDescription: (state: T) => { icon?: IconType; label: string }; - + /** Visualizations can have references as well */ + getPersistableState?: (state: T) => { state: P; savedObjectReferences: SavedObjectReference[] }; + /** Hydrate from persistable state and references to final state */ + fromPersistableState?: (state: P, references?: SavedObjectReference[]) => T; /** Frame needs to know which layers the visualization is currently using */ getLayerIds: (state: T) => string[]; /** Reset button on each layer triggers this */ - clearLayer: (state: T, layerId: string) => T; + clearLayer: (state: T, layerId: string, indexPatternId: string) => T; /** Optional, if the visualization supports multiple layers */ removeLayer?: (state: T, layerId: string) => T; /** Track added layers in internal state */ - appendLayer?: (state: T, layerId: string, type: LayerType, indexPatternId?: string) => T; + appendLayer?: (state: T, layerId: string, type: LayerType, indexPatternId: string) => T; /** Retrieve a list of supported layer types with initialization data */ getSupportedLayers: ( @@ -1081,7 +1100,7 @@ export interface Visualization { */ getErrorMessages: ( state: T, - datasourceLayers?: DatasourceLayers + frame?: Pick ) => | Array<{ shortMessage: string; @@ -1089,6 +1108,14 @@ export interface Visualization { }> | undefined; + validateColumn?: ( + state: T, + frame: Pick, + layerId: string, + columnId: string, + group?: VisualizationDimensionGroupConfig + ) => { invalid: boolean; invalidMessage?: string }; + /** * The frame calls this function to display warnings about visualization */ diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 717d0e9f996b9..385a533dc7c75 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -22,7 +22,7 @@ import type { IndexPatternRef, } from './types'; import type { DatasourceStates, VisualizationState } from './state_management'; -import { IndexPatternServiceAPI } from './indexpattern_service/service'; +import { IndexPatternServiceAPI } from './data_views_service/service'; export function getVisualizeGeoFieldMessage(fieldType: string) { return i18n.translate('xpack.lens.visualizeGeoFieldMessage', { @@ -113,9 +113,13 @@ export async function refreshIndexPatternsList({ export function getIndexPatternsIds({ activeDatasources, datasourceStates, + visualizationState, + activeVisualization, }: { activeDatasources: Record; datasourceStates: DatasourceStates; + visualizationState: unknown; + activeVisualization?: Visualization; }): string[] { let currentIndexPatternId: string | undefined; const references: SavedObjectReference[] = []; @@ -125,6 +129,11 @@ export function getIndexPatternsIds({ currentIndexPatternId = indexPatternId; references.push(...savedObjectReferences); }); + + if (activeVisualization?.getPersistableState) { + const { savedObjectReferences } = activeVisualization.getPersistableState(visualizationState); + references.push(...savedObjectReferences); + } const referencesIds = references .filter(({ type }) => type === 'index-pattern') .map(({ id }) => id); diff --git a/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx index 494be445670ae..85d1bc3f1a575 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx @@ -70,7 +70,7 @@ describe('Datatable Visualization', () => { layerType: layerTypes.DATA, columns: [{ columnId: 'a' }, { columnId: 'b' }, { columnId: 'c' }], }; - expect(datatableVisualization.clearLayer(state, 'baz')).toMatchObject({ + expect(datatableVisualization.clearLayer(state, 'baz', 'indexPattern1')).toMatchObject({ layerId: 'baz', layerType: layerTypes.DATA, columns: [], diff --git a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx index d106643a205e3..8cc30c65ed1a9 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx @@ -223,10 +223,10 @@ export const getDatatableVisualization = ({ { groupId: 'columns', groupLabel: i18n.translate('xpack.lens.datatable.breakdownColumns', { - defaultMessage: 'Columns', + defaultMessage: 'Split metrics by', }), dimensionEditorGroupLabel: i18n.translate('xpack.lens.datatable.breakdownColumn', { - defaultMessage: 'Column', + defaultMessage: 'Split metrics by', }), groupTooltip: i18n.translate('xpack.lens.datatable.breakdownColumns.description', { defaultMessage: diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts index fb8493fc4d0f5..c8a1b18e392c5 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts @@ -70,7 +70,7 @@ describe('metric_visualization', () => { describe('#clearLayer', () => { it('returns a clean layer', () => { (generateId as jest.Mock).mockReturnValueOnce('test-id1'); - expect(metricVisualization.clearLayer(exampleState(), 'l1')).toEqual({ + expect(metricVisualization.clearLayer(exampleState(), 'l1', 'indexPattern1')).toEqual({ accessor: undefined, layerId: 'l1', layerType: layerTypes.DATA, diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts index 18e664ce4ea36..8171bd379237e 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts @@ -515,7 +515,7 @@ describe('metric visualization', () => { }); it('clears a layer', () => { - expect(visualization.clearLayer(fullState, 'some-id')).toMatchInlineSnapshot(` + expect(visualization.clearLayer(fullState, 'some-id', 'indexPattern1')).toMatchInlineSnapshot(` Object { "layerId": "first", "layerType": "data", diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap index c69a3f0567a3c..e040232581962 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap @@ -8,6 +8,7 @@ Object { "addTimeMarker": Array [ false, ], + "annotations": Array [], "emphasizeFitting": Array [ true, ], diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts index 8560e3bbaec31..d953814621aad 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts @@ -5,24 +5,21 @@ * 2.0. */ +import { createMockFramePublicAPI } from '../../../mocks'; import { FramePublicAPI } from '../../../types'; import { getStaticDate } from './helpers'; -const frame: FramePublicAPI = { - datasourceLayers: {}, - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, - dataViews: { - indexPatterns: {}, - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: true, - }, -}; - describe('annotations helpers', () => { describe('getStaticDate', () => { it('should return the middle of the date range on when nothing is configured', () => { - expect(getStaticDate([], frame)).toBe('2022-03-12T00:00:00.000Z'); + expect( + getStaticDate( + [], + createMockFramePublicAPI({ + dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + }) + ) + ).toBe('2022-03-12T00:00:00.000Z'); }); it('should return the middle of the date range value on when there is no active data', () => { expect( @@ -36,7 +33,9 @@ describe('annotations helpers', () => { xAccessor: 'a', }, ], - frame + createMockFramePublicAPI({ + dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + }) ) ).toBe('2022-03-12T00:00:00.000Z'); }); @@ -64,7 +63,7 @@ describe('annotations helpers', () => { }, ], }, - }; + } as FramePublicAPI['activeData']; expect( getStaticDate( [ @@ -76,10 +75,10 @@ describe('annotations helpers', () => { xAccessor: 'a', }, ], - { - ...frame, - activeData: activeData as FramePublicAPI['activeData'], - } + createMockFramePublicAPI({ + dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + activeData, + }) ) ).toBe('2022-02-27T23:00:00.000Z'); }); @@ -107,7 +106,7 @@ describe('annotations helpers', () => { }, ], }, - }; + } as FramePublicAPI['activeData']; expect( getStaticDate( [ @@ -119,10 +118,10 @@ describe('annotations helpers', () => { xAccessor: 'a', }, ], - { - ...frame, - activeData: activeData as FramePublicAPI['activeData'], - } + createMockFramePublicAPI({ + dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + activeData, + }) ) ).toBe('2022-03-12T00:00:00.000Z'); }); @@ -162,7 +161,7 @@ describe('annotations helpers', () => { }, ], }, - }; + } as FramePublicAPI['activeData']; expect( getStaticDate( [ @@ -174,10 +173,10 @@ describe('annotations helpers', () => { xAccessor: 'a', }, ], - { - ...frame, - activeData: activeData as FramePublicAPI['activeData'], - } + createMockFramePublicAPI({ + dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + activeData, + }) ) ).toBe('2022-03-26T05:00:00.000Z'); }); @@ -242,7 +241,7 @@ describe('annotations helpers', () => { }, ], }, - }; + } as FramePublicAPI['activeData']; expect( getStaticDate( [ @@ -261,11 +260,11 @@ describe('annotations helpers', () => { xAccessor: 'd', }, ], - { - ...frame, + + createMockFramePublicAPI({ + activeData, dateRange: { fromDate: '2020-02-01T00:00:00.000Z', toDate: '2022-09-20T00:00:00.000Z' }, - activeData: activeData as FramePublicAPI['activeData'], - } + }) ) ).toBe('2020-08-24T12:06:40.000Z'); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index a84e7a3c3ed19..b45e79cc018e8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -120,6 +120,7 @@ export const getAnnotationsSupportedLayer = ( const getDefaultAnnotationConfig = (id: string, timestamp: string): EventAnnotationConfig => ({ label: defaultAnnotationLabel, + type: 'manual', key: { type: 'point_in_time', timestamp, diff --git a/x-pack/plugins/lens/public/visualizations/xy/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts index 37d4f46a5bcc7..9dcd1dab63593 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -32,8 +32,10 @@ export class XyVisualization { const { getXyVisualization } = await import('../../async_services'); const [coreStart, { charts, data, fieldFormats, eventAnnotation }] = await core.getStartServices(); - const palettes = await charts.palettes.getPalettes(); - const eventAnnotationService = await eventAnnotation.getService(); + const [palettes, eventAnnotationService] = await Promise.all([ + charts.palettes.getPalettes(), + eventAnnotation.getService(), + ]); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return getXyVisualization({ core: coreStart, diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index d1fcfdc1c7997..37c08fc7e8668 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -5,8 +5,17 @@ * 2.0. */ +import { DistributiveOmit } from '@elastic/eui'; import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import type { FramePublicAPI, DatasourcePublicAPI } from '../../types'; +import type { SavedObjectReference } from '@kbn/core/public'; +import { isQueryAnnotationConfig } from '@kbn/event-annotation-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { validateQuery } from '../../shared_components'; +import type { + FramePublicAPI, + DatasourcePublicAPI, + VisualizationDimensionGroupConfig, +} from '../../types'; import { visualizationTypes, XYLayerConfig, @@ -14,6 +23,9 @@ import { XYReferenceLineLayerConfig, SeriesType, YConfig, + XYState, + XYPersistedState, + State, } from './types'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; @@ -102,3 +114,129 @@ export function hasHistogramSeries( ); }); } + +function getLayerReferenceName(layerId: string) { + return `xy-visualization-layer-${layerId}`; +} + +export function extractReferences(state: XYState) { + const savedObjectReferences: SavedObjectReference[] = []; + const persistableLayers: Array> = []; + state.layers.forEach((layer) => { + if (isAnnotationsLayer(layer)) { + const { indexPatternId, ...persistableLayer } = layer; + savedObjectReferences.push({ + type: 'index-pattern', + id: indexPatternId, + name: getLayerReferenceName(layer.layerId), + }); + persistableLayers.push(persistableLayer); + } else { + persistableLayers.push(layer); + } + }); + return { savedObjectReferences, state: { ...state, layers: persistableLayers } }; +} + +export function injectReferences( + state: XYPersistedState, + references?: SavedObjectReference[] +): XYState { + if (!references || !references.length) { + return state as XYState; + } + const fallbackIndexPatternId = references.find(({ type }) => type === 'index-pattern')!.id; + return { + ...state, + layers: state.layers.map((layer) => { + if (!isAnnotationsLayer(layer)) { + return layer as XYLayerConfig; + } + return { + ...layer, + indexPatternId: + references.find(({ name }) => name === getLayerReferenceName(layer.layerId))?.id || + fallbackIndexPatternId, + }; + }), + }; +} + +export function validateColumn( + state: State, + frame: Pick, + layerId: string, + columnId: string, + group?: VisualizationDimensionGroupConfig +): { invalid: boolean; invalidMessages?: string[] } { + if (group?.invalid) { + return { + invalid: true, + invalidMessages: group.invalidMessage ? [group.invalidMessage] : undefined, + }; + } + const validColumn = { invalid: false }; + const layer = state.layers.find((l) => l.layerId === layerId); + if (!layer || !isAnnotationsLayer(layer)) { + return validColumn; + } + const annotation = layer.annotations.find(({ id }) => id === columnId); + if (!annotation || !isQueryAnnotationConfig(annotation)) { + return validColumn; + } + const { dataViews } = frame || {}; + const layerDataView = dataViews.indexPatterns[layer.indexPatternId]; + + const invalidMessages: string[] = []; + + if (annotation.timeField && !Boolean(layerDataView.getFieldByName(annotation.timeField))) { + invalidMessages.push( + i18n.translate('xpack.lens.xyChart.annotationError.timeFieldNotFound', { + defaultMessage: 'Time field {timeField} not found in data view {dataView}', + values: { timeField: annotation.timeField, dataView: layerDataView.title }, + }) + ); + } + + const { isValid, error } = validateQuery(annotation?.filter, layerDataView); + if (!isValid && error) { + invalidMessages.push(error); + } + if (annotation.textField && !Boolean(layerDataView.getFieldByName(annotation.textField))) { + invalidMessages.push( + i18n.translate('xpack.lens.xyChart.annotationError.textFieldNotFound', { + defaultMessage: 'Text field {textField} not found in data view {dataView}', + values: { textField: annotation.textField, dataView: layerDataView.title }, + }) + ); + } + if (annotation.extraFields?.length) { + const missingTooltipFields = []; + for (const field of annotation.extraFields) { + if (!Boolean(layerDataView.getFieldByName(field))) { + missingTooltipFields.push(field); + } + } + if (missingTooltipFields.length) { + invalidMessages.push( + i18n.translate('xpack.lens.xyChart.annotationError.tooltipFieldNotFound', { + defaultMessage: + 'Tooltip {missingFields, plural, one {field} other {fields}} {missingTooltipFields} not found in data view {dataView}', + values: { + missingTooltipFields: missingTooltipFields.join(', '), + missingFields: missingTooltipFields.length, + dataView: layerDataView.title, + }, + }) + ); + } + } + + if (!invalidMessages.length) { + return validColumn; + } + return { + invalid: true, + invalidMessages, + }; +} diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index e5faf9c2682ad..124e7e8e1ddd3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -539,6 +539,38 @@ describe('#toExpression', () => { expect(getYConfigColorForLayer(expression, 1)).toEqual([defaultReferenceLineColor]); }); + it('should ignore annotation layers with no event configured', () => { + const expression = xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + valueLabels: 'show', + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], + yConfig: [{ forAccessor: 'a' }], + }, + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + annotations: [], + indexPatternId: 'my-indexPattern', + }, + ], + }, + { ...frame.datasourceLayers, referenceLine: mockDatasource.publicAPIMock }, + undefined, + datasourceExpressionsByLayers + ) as Ast; + + expect(expression.chain[0].arguments.layers).toHaveLength(1); + }); + it('should correctly set the current time marker visibility settings', () => { const state: XYState = { legend: { position: Position.Bottom, isVisible: true }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 387542e5465f8..bfe722a21f979 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -8,9 +8,14 @@ import { Ast, AstFunction } from '@kbn/interpreter'; import { Position, ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; -import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { + EventAnnotationServiceType, + isManualPointAnnotationConfig, + isRangeAnnotationConfig, +} from '@kbn/event-annotation-plugin/public'; import { LegendSize } from '@kbn/visualizations-plugin/public'; import { XYCurveType } from '@kbn/expression-xy-plugin/common'; +import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { State, YConfig, @@ -241,6 +246,11 @@ export const buildExpression = ( }); } + const isValidAnnotation = (a: EventAnnotationConfig) => + isManualPointAnnotationConfig(a) || + isRangeAnnotationConfig(a) || + (a.filter && a.filter?.query !== ''); + return { type: 'expression', chain: [ @@ -343,10 +353,39 @@ export const buildExpression = ( datasourceExpressionsByLayers[layer.layerId] ) ), - ...validAnnotationsLayers.map((layer) => - annotationLayerToExpression(layer, eventAnnotationService) - ), ], + annotations: + validAnnotationsLayers.length && + validAnnotationsLayers.flatMap((l) => l.annotations.filter(isValidAnnotation)).length + ? [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'event_annotations_result', + arguments: { + layers: validAnnotationsLayers.map((layer) => + annotationLayerToExpression(layer, eventAnnotationService) + ), + datatable: eventAnnotationService.toFetchExpression({ + interval: + (validDataLayers[0]?.xAccessor && + metadata[validDataLayers[0]?.layerId]?.[ + validDataLayers[0]?.xAccessor + ]?.interval) || + 'auto', + groups: validAnnotationsLayers.map((layer) => ({ + indexPatternId: layer.indexPatternId, + annotations: layer.annotations.filter(isValidAnnotation), + })), + }), + }, + }, + ], + }, + ] + : [], }, }, ], @@ -416,9 +455,7 @@ const annotationLayerToExpression = ( arguments: { simpleView: [Boolean(layer.simpleView)], layerId: [layer.layerId], - annotations: layer.annotations - ? layer.annotations.map((ann): Ast => eventAnnotationService.toExpression(ann)) - : [], + annotations: eventAnnotationService.toExpression(layer.annotations || []), }, }, ], diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 27c5d485065c8..203718e4b9f8c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -35,6 +35,7 @@ import { IconChartBarHorizontal, } from '@kbn/chart-icons'; +import { DistributiveOmit } from '@elastic/eui'; import type { VisualizationType, Suggestion } from '../../types'; import type { ValueLabelConfig } from '../../../common/types'; @@ -115,6 +116,8 @@ export interface XYAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; + hide?: boolean; + indexPatternId: string; simpleView?: boolean; } @@ -160,6 +163,12 @@ export interface XYState { export type State = XYState; +export type XYPersistedState = Omit & { + layers: Array>; +}; + +export type PersistedState = XYPersistedState; + const groupLabelForBar = i18n.translate('xpack.lens.xyVisualization.barGroupLabel', { defaultMessage: 'Bar', }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index ad26ea92762bf..596f20d9fe2e7 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -35,9 +35,13 @@ import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/ import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { DataViewsState } from '../../state_management'; +import { createMockedIndexPattern } from '../../indexpattern_datasource/mocks'; +import { createMockDataViewsState } from '../../data_views_service/mocks'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', + type: 'manual', label: 'Event 1', key: { type: 'point_in_time', @@ -47,6 +51,7 @@ const exampleAnnotation: EventAnnotationConfig = { }; const exampleAnnotation2: EventAnnotationConfig = { icon: 'circle', + type: 'manual', id: 'an2', key: { timestamp: '2022-04-18T11:01:59.135Z', @@ -237,7 +242,12 @@ describe('xy_visualization', () => { describe('#appendLayer', () => { it('adds a layer', () => { - const layers = xyVisualization.appendLayer!(exampleState(), 'foo', layerTypes.DATA).layers; + const layers = xyVisualization.appendLayer!( + exampleState(), + 'foo', + layerTypes.DATA, + 'indexPattern1' + ).layers; expect(layers.length).toEqual(exampleState().layers.length + 1); expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); }); @@ -245,7 +255,7 @@ describe('xy_visualization', () => { describe('#clearLayer', () => { it('clears the specified layer', () => { - const layer = xyVisualization.clearLayer(exampleState(), 'first').layers[0]; + const layer = xyVisualization.clearLayer(exampleState(), 'first', 'indexPattern1').layers[0]; expect(layer).toMatchObject({ accessors: [], layerId: 'first', @@ -460,6 +470,7 @@ describe('xy_visualization', () => { { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ], @@ -471,10 +482,12 @@ describe('xy_visualization', () => { ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [ exampleAnnotation, { icon: 'triangle', + type: 'manual', id: 'newCol', key: { timestamp: '2022-04-15T00:00:00.000Z', @@ -495,6 +508,7 @@ describe('xy_visualization', () => { { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], }, ], @@ -517,6 +531,7 @@ describe('xy_visualization', () => { ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], }); }); @@ -530,6 +545,7 @@ describe('xy_visualization', () => { { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, exampleAnnotation2], }, ], @@ -553,6 +569,7 @@ describe('xy_visualization', () => { ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, exampleAnnotation], }); }); @@ -566,12 +583,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, { layerId: 'second', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], }, ], @@ -596,11 +615,13 @@ describe('xy_visualization', () => { { layerId: 'first', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, { layerId: 'second', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [{ ...exampleAnnotation, id: 'an2' }], }, ]); @@ -614,12 +635,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, { layerId: 'second', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], }, ], @@ -644,11 +667,13 @@ describe('xy_visualization', () => { { layerId: 'first', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], }, { layerId: 'second', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ]); @@ -662,12 +687,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, { layerId: 'second', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], }, ], @@ -692,11 +719,13 @@ describe('xy_visualization', () => { { layerId: 'first', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [], }, { layerId: 'second', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ]); @@ -710,12 +739,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, { layerId: 'second', - layerType: 'annotations', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [], }, ], @@ -740,11 +771,13 @@ describe('xy_visualization', () => { { layerId: 'first', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [], }, { layerId: 'second', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ]); @@ -1106,6 +1139,7 @@ describe('xy_visualization', () => { { layerId: 'ann', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, { ...exampleAnnotation, id: 'an2' }], }, ], @@ -1124,6 +1158,7 @@ describe('xy_visualization', () => { { layerId: 'ann', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ]); @@ -1843,6 +1878,7 @@ describe('xy_visualization', () => { { layerId: 'annotations', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], }, ], @@ -2278,7 +2314,7 @@ describe('xy_visualization', () => { }, ], }, - frame.datasourceLayers + { datasourceLayers: frame.datasourceLayers, dataViews: {} as DataViewsState } ) ).toEqual([ { @@ -2334,7 +2370,7 @@ describe('xy_visualization', () => { }, ], }, - datasourceLayers + { datasourceLayers, dataViews: {} as DataViewsState } ) ).toEqual([ { @@ -2390,7 +2426,7 @@ describe('xy_visualization', () => { }, ], }, - datasourceLayers + { datasourceLayers, dataViews: {} as DataViewsState } ) ).toEqual([ { @@ -2399,6 +2435,98 @@ describe('xy_visualization', () => { }, ]); }); + + describe('Annotation layers', () => { + function createStateWithAnnotationProps(annotation: Partial) { + return { + layers: [ + { + layerId: 'layerId', + layerType: 'annotations', + indexPatternId: 'first', + annotations: [ + { + label: 'Event', + id: '1', + type: 'query', + timeField: 'start_date', + ...annotation, + }, + ], + }, + ], + } as XYState; + } + + function getFrameMock() { + return createMockFramePublicAPI({ + datasourceLayers: { first: mockDatasource.publicAPIMock }, + dataViews: createMockDataViewsState({ + indexPatterns: { first: createMockedIndexPattern() }, + }), + }); + } + it('should return error if current annotation contains non-existent field as timeField', () => { + const xyState = createStateWithAnnotationProps({ + timeField: 'non-existent', + }); + const errors = xyVisualization.getErrorMessages(xyState, getFrameMock()); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual( + expect.objectContaining({ + shortMessage: 'Time field non-existent not found in data view my-fake-index-pattern', + }) + ); + }); + it('should return error if current annotation contains non existent field as textField', () => { + const xyState = createStateWithAnnotationProps({ + textField: 'non-existent', + }); + const errors = xyVisualization.getErrorMessages(xyState, getFrameMock()); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual( + expect.objectContaining({ + shortMessage: 'Text field non-existent not found in data view my-fake-index-pattern', + }) + ); + }); + it('should contain error if current annotation contains at least one non-existent field as tooltip field', () => { + const xyState = createStateWithAnnotationProps({ + extraFields: ['bytes', 'memory', 'non-existent'], + }); + const errors = xyVisualization.getErrorMessages(xyState, getFrameMock()); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual( + expect.objectContaining({ + shortMessage: 'Tooltip field non-existent not found in data view my-fake-index-pattern', + }) + ); + }); + it('should contain error if current annotation contains invalid query', () => { + const xyState = createStateWithAnnotationProps({ + filter: { type: 'kibana_query', query: 'invalid: "', language: 'kuery' }, + }); + const errors = xyVisualization.getErrorMessages(xyState, getFrameMock()); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual( + expect.objectContaining({ + shortMessage: expect.stringContaining( + 'Expected "(", "{", value, whitespace but """ found.' + ), + }) + ); + }); + it('should contain multiple errors if current annotation contains multiple non-existent fields', () => { + const xyState = createStateWithAnnotationProps({ + timeField: 'non-existent', + textField: 'non-existent', + extraFields: ['bytes', 'memory', 'non-existent'], + filter: { type: 'kibana_query', query: 'invalid: "', language: 'kuery' }, + }); + const errors = xyVisualization.getErrorMessages(xyState, getFrameMock()); + expect(errors).toHaveLength(4); + }); + }); }); describe('#getWarningMessages', () => { @@ -2555,4 +2683,80 @@ describe('xy_visualization', () => { }); }); }); + + describe('#fromPersistableState', () => { + it('should inject references on annotation layers', () => { + const baseState = exampleState(); + expect( + xyVisualization.fromPersistableState!( + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + }, + ], + }, + [ + { + type: 'index-pattern', + name: `xy-visualization-layer-annotation`, + id: 'indexPattern1', + }, + ] + ) + ).toEqual({ + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + }, + ], + }); + }); + + it('should fallback to the first dataView reference in case there are missing annotation references', () => { + const baseState = exampleState(); + expect( + xyVisualization.fromPersistableState!( + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + }, + ], + }, + [ + { + type: 'index-pattern', + name: 'something-else', + id: 'indexPattern1', + }, + ] + ) + ).toEqual({ + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + }, + ], + }); + }); + }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 24710c3261cdb..4e9a2e55e19ad 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -14,7 +14,7 @@ import type { PaletteRegistry } from '@kbn/coloring'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { CoreStart, ThemeServiceStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { FillStyle } from '@kbn/expression-xy-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -22,7 +22,7 @@ import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; -import { LayerHeader } from './xy_config_panel/layer_header'; +import { LayerHeader, LayerHeaderContent } from './xy_config_panel/layer_header'; import type { Visualization, AccessorConfig, FramePublicAPI } from '../../types'; import { State, @@ -33,9 +33,15 @@ import { YConfig, YAxisMode, SeriesType, + PersistedState, } from './types'; import { layerTypes } from '../../../common'; -import { isHorizontalChart } from './state_helpers'; +import { + extractReferences, + injectReferences, + isHorizontalChart, + validateColumn, +} from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; import { getAccessorColorConfigs, getColorAssignments } from './color_assignment'; import { getColumnToLabelMap } from './state_helpers'; @@ -55,6 +61,7 @@ import { import { checkXAccessorCompatibility, defaultSeriesType, + getAnnotationsLayers, getAxisName, getDataLayers, getDescription, @@ -79,6 +86,7 @@ import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; +const XY_ID = 'lnsXY'; export const getXyVisualization = ({ core, storage, @@ -97,8 +105,8 @@ export const getXyVisualization = ({ fieldFormats: FieldFormatsStart; useLegacyTimeAxis: boolean; kibanaTheme: ThemeServiceStart; -}): Visualization => ({ - id: 'lnsXY', +}): Visualization => ({ + id: XY_ID, visualizationTypes, getVisualizationTypeId(state) { const type = getVisualizationType(state); @@ -121,7 +129,7 @@ export const getXyVisualization = ({ }; }, - appendLayer(state, layerId, layerType) { + appendLayer(state, layerId, layerType, indexPatternId) { const firstUsedSeriesType = getDataLayers(state.layers)?.[0]?.seriesType; return { ...state, @@ -131,12 +139,13 @@ export const getXyVisualization = ({ seriesType: firstUsedSeriesType || state.preferredSeriesType, layerId, layerType, + indexPatternId, }), ], }; }, - clearLayer(state, layerId) { + clearLayer(state, layerId, indexPatternId) { return { ...state, layers: state.layers.map((l) => @@ -145,11 +154,20 @@ export const getXyVisualization = ({ : newLayerState({ seriesType: state.preferredSeriesType, layerId, + indexPatternId, }) ), }; }, + getPersistableState(state) { + return extractReferences(state); + }, + + fromPersistableState(state, references) { + return injectReferences(state, references); + }, + getDescription, switchVisualizationType(seriesType: string, state: State) { @@ -204,7 +222,7 @@ export const getXyVisualization = ({ return state; } const newLayers = [...state.layers]; - newLayers[layerIndex] = { ...layer }; + newLayers[layerIndex] = { ...layer, indexPatternId }; return { ...state, layers: newLayers, @@ -468,8 +486,10 @@ export const getXyVisualization = ({ return prevState; } if (isAnnotationsLayer(foundLayer)) { - const newLayer = { ...foundLayer }; - newLayer.annotations = newLayer.annotations.filter(({ id }) => id !== columnId); + const newLayer = { + ...foundLayer, + annotations: foundLayer.annotations.filter(({ id }) => id !== columnId), + }; const newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); return { @@ -520,6 +540,24 @@ export const getXyVisualization = ({ }; }, + renderLayerPanel(domElement, props) { + const { onChangeIndexPattern, ...otherProps } = props; + render( + + + { + // TODO: should it trigger an action as in the datasource? + onChangeIndexPattern(indexPatternId); + }} + /> + + , + domElement + ); + }, + renderLayerHeader(domElement, props) { render( @@ -560,7 +598,22 @@ export const getXyVisualization = ({ render( - {dimensionEditor} + + + {dimensionEditor} + + , domElement ); @@ -585,7 +638,53 @@ export const getXyVisualization = ({ eventAnnotationService ), - getErrorMessages(state, datasourceLayers) { + validateColumn(state, frame, layerId, columnId, group) { + const { invalid, invalidMessages } = validateColumn(state, frame, layerId, columnId, group); + if (!invalid) { + return { invalid }; + } + return { invalid, invalidMessage: invalidMessages![0] }; + }, + + getErrorMessages(state, frame) { + const { datasourceLayers, dataViews } = frame || {}; + const errors: Array<{ + shortMessage: string; + longMessage: React.ReactNode; + }> = []; + + const annotationLayers = getAnnotationsLayers(state.layers); + + if (dataViews) { + annotationLayers.forEach((layer) => { + layer.annotations.forEach((annotation) => { + const validatedColumn = validateColumn( + state, + { dataViews }, + layer.layerId, + annotation.id + ); + if (validatedColumn?.invalid && validatedColumn.invalidMessages?.length) { + errors.push( + ...validatedColumn.invalidMessages.map((invalidMessage) => ({ + shortMessage: invalidMessage, + longMessage: ( + + ), + })) + ); + } + }); + }); + } + // Data error handling below here const hasNoAccessors = ({ accessors }: XYDataLayerConfig) => accessors == null || accessors.length === 0; @@ -594,11 +693,6 @@ export const getXyVisualization = ({ const hasNoSplitAccessor = ({ splitAccessor, seriesType }: XYDataLayerConfig) => seriesType.includes('percentage') && splitAccessor == null; - const errors: Array<{ - shortMessage: string; - longMessage: React.ReactNode; - }> = []; - // check if the layers in the state are compatible with this type of chart if (state && state.layers.length > 1) { // Order is important here: Y Axis is fundamental to exist to make it valid @@ -696,6 +790,11 @@ export const getXyVisualization = ({ getUniqueLabels(state) { return getUniqueLabels(state.layers); }, + getUsedDataViews(state) { + return ( + state?.layers.filter(isAnnotationsLayer).map(({ indexPatternId }) => indexPatternId) ?? [] + ); + }, renderDimensionTrigger({ columnId, label, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 46bc1ba8dc551..0e32e4006187c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -275,10 +275,17 @@ const newLayerFn = { layerType: layerTypes.REFERENCELINE, accessors: [], }), - [layerTypes.ANNOTATIONS]: ({ layerId }: { layerId: string }): XYAnnotationLayerConfig => ({ + [layerTypes.ANNOTATIONS]: ({ + layerId, + indexPatternId, + }: { + layerId: string; + indexPatternId: string; + }): XYAnnotationLayerConfig => ({ layerId, layerType: layerTypes.ANNOTATIONS, annotations: [], + indexPatternId, }), }; @@ -286,12 +293,14 @@ export function newLayerState({ layerId, layerType = layerTypes.DATA, seriesType, + indexPatternId, }: { layerId: string; layerType?: LayerType; seriesType: SeriesType; + indexPatternId: string; }) { - return newLayerFn[layerType]({ layerId, seriesType }); + return newLayerFn[layerType]({ layerId, seriesType, indexPatternId }); } export function getLayersByType(state: State, byType?: string) { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 5d5e4360b28f9..778a1a13e200e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -8,121 +8,41 @@ import './index.scss'; import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiDatePicker, - EuiFormRow, - EuiSwitch, - EuiSwitchEvent, - EuiButtonGroup, - EuiFormLabel, - EuiFormControlLayout, - EuiText, - transparentize, -} from '@elastic/eui'; +import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup, EuiSpacer } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import moment from 'moment'; -import { - EventAnnotationConfig, - PointInTimeEventAnnotationConfig, - RangeEventAnnotationConfig, -} from '@kbn/event-annotation-plugin/common/types'; -import { pick } from 'lodash'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import { search } from '@kbn/data-plugin/public'; import { defaultAnnotationColor, defaultAnnotationRangeColor, + isQueryAnnotationConfig, isRangeAnnotationConfig, - isManualPointAnnotationConfig, } from '@kbn/event-annotation-plugin/public'; -import Color from 'color'; -import { getDataLayers } from '../../visualization_helpers'; +import { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { + FieldOption, + FieldOptionValue, + FieldPicker, +} from '../../../../shared_components/field_picker'; import { FormatFactory } from '../../../../../common'; -import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; import { DimensionEditorSection, + fieldExists, NameInput, useDebouncedValue, } from '../../../../shared_components'; import { isHorizontalChart } from '../../state_helpers'; -import { defaultAnnotationLabel, defaultRangeAnnotationLabel } from '../../annotations/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'; -import type { FramePublicAPI, VisualizationDimensionEditorProps } from '../../../../types'; -import { State, XYState, XYAnnotationLayerConfig, XYDataLayerConfig } from '../../types'; - -export const toRangeAnnotationColor = (color = defaultAnnotationColor) => { - return new Color(transparentize(color, 0.1)).hexa(); -}; - -export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => { - return new Color(transparentize(color, 1)).hex(); -}; - -export const getEndTimestamp = ( - datatableUtilities: DatatableUtilitiesService, - 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 = datatableUtilities.getDateHistogramMeta(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 (isRangeAnnotationConfig(annotation)) { - const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ - 'label', - 'key', - 'id', - 'isHidden', - 'color', - 'outside', - ]); - return rangeAnnotation; - } else if (isManualPointAnnotationConfig(annotation)) { - const lineAnnotation: PointInTimeEventAnnotationConfig = pick(annotation, [ - 'id', - 'label', - 'key', - 'isHidden', - 'lineStyle', - 'lineWidth', - 'color', - 'icon', - 'textVisibility', - ]); - return lineAnnotation; - } - return annotation; // todo: sanitize for the query annotations here -}; +import type { VisualizationDimensionEditorProps } from '../../../../types'; +import type { State, XYState, XYAnnotationLayerConfig } from '../../types'; +import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; +import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; +import { TooltipSection } from './tooltip_annotation_panel'; +import { sanitizeProperties } from './helpers'; export const AnnotationsPanel = ( props: VisualizationDimensionEditorProps & { @@ -146,8 +66,8 @@ export const AnnotationsPanel = ( const currentAnnotation = localLayer.annotations?.find((c) => c.id === accessor); + const isQueryBased = isQueryAnnotationConfig(currentAnnotation); const isRange = isRangeAnnotationConfig(currentAnnotation); - const isManualPoint = isManualPointAnnotationConfig(currentAnnotation); const setAnnotations = useCallback( (annotation) => { @@ -178,99 +98,69 @@ export const AnnotationsPanel = ( defaultMessage: 'Placement', })} > - {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(), - }, - }); - } - } - }} - /> - - ) : isManualPoint ? ( - + { - if (date) { - setAnnotations({ - key: { - ...(currentAnnotation?.key || { type: 'point_in_time' }), - timestamp: date.toISOString(), - }, - }); - } + data-test-subj="lns-xyAnnotation-placementType" + name="placementType" + buttonSize="compressed" + options={[ + { + id: `lens_xyChart_annotation_staticDate`, + label: i18n.translate('xpack.lens.xyChart.annotation.staticDate', { + defaultMessage: 'Static Date', + }), + 'data-test-subj': 'lnsXY_annotation_staticDate', + }, + { + id: `lens_xyChart_annotation_query`, + label: i18n.translate('xpack.lens.xyChart.annotation.query', { + defaultMessage: 'Query', + }), + 'data-test-subj': 'lnsXY_annotation_query', + }, + ]} + idSelected={`lens_xyChart_annotation_${ + currentAnnotation?.type === 'query' ? 'query' : 'staticDate' + }`} + onChange={(id) => { + setAnnotations({ + type: id === `lens_xyChart_annotation_query` ? 'query' : 'manual', + // when switching to query, reset the key value + key: + !isQueryBased && id === `lens_xyChart_annotation_query` + ? { type: 'point_in_time' } + : currentAnnotation?.key, + }); }} + isFullWidth /> - ) : null} - - + + {isQueryBased ? ( + + ) : ( + + )} {!isRange && ( - - )} - {!isRange && ( - - )} - {!isRange && ( - - )} + <> + + + {(textDecorationSelected) => { + if (textDecorationSelected !== 'field') { + return null; + } + const currentIndexPattern = + frame.dataViews.indexPatterns[localLayer.indexPatternId]; + const options = currentIndexPattern.fields + .filter(({ displayName, type }) => displayName && type !== 'document') + .map( + (field) => + ({ + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: fieldExists( + frame.dataViews.existingFields[currentIndexPattern.title], + field.name + ), + compatible: true, + 'data-test-subj': `lnsXY-annotation-fieldOption-${field.name}`, + } as FieldOption) + ); + const selectedField = (currentAnnotation as QueryPointEventAnnotationConfig) + .textField; + const fieldIsValid = selectedField + ? Boolean(currentIndexPattern.getFieldByName(selectedField)) + : true; + return ( + <> + + + + ); + }} + + + + )} {isRange && ( setAnnotations({ isHidden: ev.target.checked })} /> - - ); -}; - -const ConfigPanelApplyAsRangeSwitch = ({ - annotation, - datatableUtilities, - onChange, - frame, - state, -}: { - annotation?: EventAnnotationConfig; - datatableUtilities: DatatableUtilitiesService; - onChange: (annotations: Partial | undefined) => void; - frame: FramePublicAPI; - state: XYState; -}) => { - const isRange = isRangeAnnotationConfig(annotation); - const isManualPoint = isManualPointAnnotationConfig(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 (isManualPoint) { - const fromTimestamp = moment(annotation?.key?.timestamp); - const dataLayers = getDataLayers(state.layers); - const newRangeAnnotation: RangeEventAnnotationConfig = { - key: { - type: 'range', - timestamp: annotation.key.timestamp, - endTimestamp: getEndTimestamp( - datatableUtilities, - 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} - } + {isQueryBased && currentAnnotation && ( + - - - ) : ( - + label={i18n.translate('xpack.lens.xyChart.annotation.tooltip', { + defaultMessage: 'Show additional fields', + })} + > + + + )} - + ); }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts new file mode 100644 index 0000000000000..2ca0f9530bbe1 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { search } from '@kbn/data-plugin/public'; +import { transparentize } from '@elastic/eui'; +import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import type { + EventAnnotationConfig, + RangeEventAnnotationConfig, + PointInTimeEventAnnotationConfig, + QueryPointEventAnnotationConfig, +} from '@kbn/event-annotation-plugin/common'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isQueryAnnotationConfig, + isRangeAnnotationConfig, +} from '@kbn/event-annotation-plugin/public'; +import Color from 'color'; +import { pick } from 'lodash'; +import moment from 'moment'; +import type { FramePublicAPI } from '../../../../types'; +import type { XYDataLayerConfig } from '../../types'; + +export const toRangeAnnotationColor = (color = defaultAnnotationColor) => { + return new Color(transparentize(color, 0.1)).hexa(); +}; + +export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => { + return new Color(transparentize(color, 1)).hex(); +}; + +export const getEndTimestamp = ( + datatableUtilities: DatatableUtilitiesService, + 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 = datatableUtilities.getDateHistogramMeta(xColumn)?.interval; + if (!dateInterval) return fallbackValue; + const intervalDuration = search.aggs.parseInterval(dateInterval); + if (!intervalDuration) return fallbackValue; + return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); +}; + +export const sanitizeProperties = (annotation: EventAnnotationConfig) => { + if (isRangeAnnotationConfig(annotation)) { + const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ + 'type', + 'label', + 'key', + 'id', + 'isHidden', + 'color', + 'outside', + ]); + return rangeAnnotation; + } + if (isQueryAnnotationConfig(annotation)) { + const lineAnnotation: QueryPointEventAnnotationConfig = pick(annotation, [ + 'type', + 'id', + 'label', + 'key', + 'timeField', + 'isHidden', + 'lineStyle', + 'lineWidth', + 'color', + 'icon', + 'textVisibility', + 'textField', + 'filter', + 'extraFields', + ]); + return lineAnnotation; + } + const lineAnnotation: PointInTimeEventAnnotationConfig = pick(annotation, [ + 'type', + 'id', + 'label', + 'key', + 'isHidden', + 'lineStyle', + 'lineWidth', + 'color', + 'icon', + 'textVisibility', + ]); + return lineAnnotation; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx index 330fbf1f55da6..5514e3062213e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx @@ -16,6 +16,9 @@ import { State } from '../../types'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import moment from 'moment'; +import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { createMockDataViewsState } from '../../../../data_views_service/mocks'; +import { createMockedIndexPattern } from '../../../../indexpattern_datasource/mocks'; jest.mock('lodash', () => { const original = jest.requireActual('lodash'); @@ -26,8 +29,9 @@ jest.mock('lodash', () => { }; }); -const customLineStaticAnnotation = { +const customLineStaticAnnotation: EventAnnotationConfig = { id: 'ann1', + type: 'manual', key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, label: 'Event', icon: 'triangle' as const, @@ -49,6 +53,7 @@ describe('AnnotationsPanel', () => { { layerType: layerTypes.ANNOTATIONS, layerId: 'annotation', + indexPatternId: 'indexPattern1', annotations: [customLineStaticAnnotation], }, ], @@ -56,9 +61,9 @@ describe('AnnotationsPanel', () => { } beforeEach(() => { - frame = createMockFramePublicAPI(); - frame.datasourceLayers = {}; + frame = createMockFramePublicAPI({ datasourceLayers: {} }); }); + describe('Dimension Editor', () => { test('shows correct options for line annotations', () => { const state = testState(); @@ -109,6 +114,7 @@ describe('AnnotationsPanel', () => { color: 'red', icon: 'triangle', id: 'ann1', + type: 'manual', isHidden: undefined, key: { endTimestamp: '2022-03-21T10:49:00.000Z', @@ -122,6 +128,7 @@ describe('AnnotationsPanel', () => { ], layerId: 'annotation', layerType: 'annotations', + indexPatternId: 'indexPattern1', }; const component = mount( { panelRef={React.createRef()} /> ); + component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); expect(setState).toBeCalledWith({ @@ -188,6 +196,7 @@ describe('AnnotationsPanel', () => { id: 'ann1', isHidden: undefined, label: 'Event range', + type: 'manual', key: { endTimestamp: '2022-03-21T10:49:00.000Z', timestamp: '2022-03-18T08:25:00.000Z', @@ -195,6 +204,7 @@ describe('AnnotationsPanel', () => { }, }, ], + indexPatternId: 'indexPattern1', layerId: 'annotation', layerType: 'annotations', }, @@ -215,13 +225,85 @@ describe('AnnotationsPanel', () => { type: 'point_in_time', }, label: 'Event', + type: 'manual', }, ], + indexPatternId: 'indexPattern1', layerId: 'annotation', layerType: 'annotations', }, ], }); }); + + test('shows correct options for query based', () => { + const state = testState(); + const indexPattern = createMockedIndexPattern(); + state.layers[0] = { + annotations: [ + { + color: 'red', + icon: 'triangle', + id: 'ann1', + type: 'query', + isHidden: undefined, + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + label: 'Query based event', + lineStyle: 'dashed', + lineWidth: 3, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + ], + layerId: 'annotation', + layerType: 'annotations', + indexPatternId: indexPattern.id, + }; + const frameMock = createMockFramePublicAPI({ + datasourceLayers: {}, + dataViews: createMockDataViewsState({ + indexPatterns: { [indexPattern.id]: indexPattern }, + }), + }); + + const component = mount( + + ); + + expect( + component.find('[data-test-subj="annotation-query-based-field-picker"]').exists() + ).toBeTruthy(); + expect( + component.find('[data-test-subj="annotation-query-based-query-input"]').exists() + ).toBeTruthy(); + + // The provided indexPattern has 2 date fields + expect( + component + .find('[data-test-subj="annotation-query-based-field-picker"]') + .at(0) + .prop('options') + ).toHaveLength(2); + // When in query mode a new "field" option is added to the previous 2 ones + expect( + component.find('[data-test-subj="lns-lineMarker-text-visibility"]').at(0).prop('options') + ).toHaveLength(3); + expect( + component.find('[data-test-subj="lnsXY-annotation-tooltip-add_field"]').exists() + ).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.tsx new file mode 100644 index 0000000000000..33b04b17b1a2f --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.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 type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import { isRangeAnnotationConfig } from '@kbn/event-annotation-plugin/public'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import React from 'react'; +import type { FramePublicAPI } from '../../../../types'; +import type { XYState } from '../../types'; +import { + ConfigPanelRangeDatePicker, + ConfigPanelApplyAsRangeSwitch, +} from './range_annotation_panel'; +import type { ManualEventAnnotationType } from './types'; + +export const ConfigPanelManualAnnotation = ({ + annotation, + frame, + state, + onChange, + datatableUtilities, +}: { + annotation?: ManualEventAnnotationType | undefined; + onChange: (annotations: Partial | undefined) => void; + datatableUtilities: DatatableUtilitiesService; + frame: FramePublicAPI; + state: XYState; +}) => { + const isRange = isRangeAnnotationConfig(annotation); + return ( + <> + {isRange ? ( + <> + { + if (date) { + const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); + if (currentEndTime < date.valueOf()) { + const currentStartTime = moment(annotation?.key.timestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + endTimestamp: moment(date.valueOf() + dif).toISOString(), + }, + }); + } else { + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + }, + }); + } + } + }} + label={i18n.translate('xpack.lens.xyChart.annotationDate', { + defaultMessage: 'Annotation date', + })} + /> + { + if (date) { + const currentStartTime = moment(annotation?.key.timestamp).valueOf(); + if (currentStartTime > date.valueOf()) { + const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + timestamp: moment(date.valueOf() - dif).toISOString(), + }, + }); + } else { + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + }, + }); + } + } + }} + /> + + ) : ( + { + if (date) { + onChange({ + key: { + ...(annotation?.key || { type: 'point_in_time' }), + timestamp: date.toISOString(), + }, + }); + } + }} + /> + )} + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx new file mode 100644 index 0000000000000..c9cd0bbec7d35 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFormRow } from '@elastic/eui'; +import type { Query } from '@kbn/data-plugin/common'; +import type { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { + fieldExists, + FieldOption, + FieldOptionValue, + FieldPicker, + QueryInput, + validateQuery, +} from '../../../../shared_components'; +import type { FramePublicAPI } from '../../../../types'; +import type { XYState, XYAnnotationLayerConfig } from '../../types'; + +export const defaultQuery: Query = { + query: '', + language: 'kuery', +}; + +export const ConfigPanelQueryAnnotation = ({ + annotation, + frame, + state, + onChange, + layer, +}: { + annotation?: QueryPointEventAnnotationConfig; + onChange: (annotations: Partial | undefined) => void; + frame: FramePublicAPI; + state: XYState; + layer: XYAnnotationLayerConfig; +}) => { + const inputQuery = annotation?.filter ?? defaultQuery; + const currentIndexPattern = frame.dataViews.indexPatterns[layer.indexPatternId]; + const currentExistingFields = frame.dataViews.existingFields[currentIndexPattern.title]; + // list only supported field by operation, remove the rest + const options = currentIndexPattern.fields + .filter((field) => field.type === 'date' && field.displayName) + .map((field) => { + return { + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: fieldExists(currentExistingFields, field.name), + compatible: true, + 'data-test-subj': `lns-fieldOption-${field.name}`, + } as FieldOption; + }); + const { isValid: isQueryInputValid, error: queryInputError } = validateQuery( + annotation?.filter, + currentIndexPattern + ); + + const selectedField = + annotation?.timeField || currentIndexPattern.timeFieldName || options[0]?.value.field; + const fieldIsValid = selectedField + ? Boolean(currentIndexPattern.getFieldByName(selectedField)) + : true; + return ( + <> + + {}} + data-test-subj="annotation-query-based-query-input" + placeholder={ + inputQuery.language === 'kuery' + ? i18n.translate('xpack.lens.annotations.query.queryPlaceholderKql', { + defaultMessage: '{example}', + values: { example: 'method : "GET"' }, + }) + : i18n.translate('xpack.lens.annotations.query.queryPlaceholderLucene', { + defaultMessage: '{example}', + values: { example: 'method:GET' }, + }) + } + /> + + + + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx new file mode 100644 index 0000000000000..edecd94e3c8e8 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx @@ -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 type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import type { + PointInTimeEventAnnotationConfig, + RangeEventAnnotationConfig, +} from '@kbn/event-annotation-plugin/common'; +import { isRangeAnnotationConfig } from '@kbn/event-annotation-plugin/public'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { + EuiFormRow, + EuiSwitch, + EuiText, + EuiFormControlLayout, + EuiFormLabel, + EuiDatePicker, +} from '@elastic/eui'; +import moment from 'moment'; +import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; +import type { FramePublicAPI } from '../../../../types'; +import { defaultRangeAnnotationLabel, defaultAnnotationLabel } from '../../annotations/helpers'; +import type { XYState } from '../../types'; +import { getDataLayers } from '../../visualization_helpers'; +import { toLineAnnotationColor, getEndTimestamp, toRangeAnnotationColor } from './helpers'; +import type { ManualEventAnnotationType } from './types'; + +export const ConfigPanelApplyAsRangeSwitch = ({ + annotation, + datatableUtilities, + onChange, + frame, + state, +}: { + annotation?: ManualEventAnnotationType; + datatableUtilities: DatatableUtilitiesService; + onChange: (annotations: Partial | undefined) => void; + frame: FramePublicAPI; + state: XYState; +}) => { + const isRange = isRangeAnnotationConfig(annotation); + return ( + + + {i18n.translate('xpack.lens.xyChart.applyAsRange', { + defaultMessage: 'Apply as range', + })} + + } + checked={isRange} + onChange={() => { + if (isRange) { + const newPointAnnotation: PointInTimeEventAnnotationConfig = { + type: 'manual', + 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 = { + type: 'manual', + key: { + type: 'range', + timestamp: annotation.key.timestamp, + endTimestamp: getEndTimestamp( + datatableUtilities, + fromTimestamp.toISOString(), + frame, + dataLayers + ), + }, + id: annotation.id, + label: + annotation.label === defaultAnnotationLabel + ? defaultRangeAnnotationLabel + : annotation.label, + color: toRangeAnnotationColor(annotation.color), + isHidden: annotation.isHidden, + }; + onChange(newRangeAnnotation); + } + }} + compressed + /> + + ); +}; + +export 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} + } + > + + + ) : ( + + )} + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx new file mode 100644 index 0000000000000..c8ea7a0ed2ece --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx @@ -0,0 +1,233 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + htmlIdGenerator, + EuiButton, + EuiButtonIcon, + EuiDraggable, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useMemo } from 'react'; +import { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import type { ExistingFieldsMap, IndexPattern } from '../../../../types'; +import { + fieldExists, + FieldOption, + FieldOptionValue, + FieldPicker, + useDebouncedValue, + NewBucketButton, + DragDropBuckets, +} from '../../../../shared_components'; + +const generateId = htmlIdGenerator(); +const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); + +export interface FieldInputsProps { + currentConfig: QueryPointEventAnnotationConfig; + setConfig: (config: QueryPointEventAnnotationConfig) => void; + indexPattern: IndexPattern; + existingFields: ExistingFieldsMap; + invalidFields?: string[]; +} + +interface WrappedValue { + id: string; + value: string | undefined; + isNew?: boolean; +} + +type SafeWrappedValue = Omit & { value: string }; + +function removeNewEmptyField(v: WrappedValue): v is SafeWrappedValue { + return v.value != null; +} + +export function TooltipSection({ + currentConfig, + setConfig, + indexPattern, + existingFields, + invalidFields, +}: FieldInputsProps) { + const onChangeWrapped = useCallback( + (values: WrappedValue[]) => { + setConfig({ + ...currentConfig, + extraFields: values.filter(removeNewEmptyField).map(({ value }) => value), + }); + }, + [setConfig, currentConfig] + ); + const { wrappedValues, rawValuesLookup } = useMemo(() => { + const rawValues = currentConfig.extraFields ?? []; + return { + wrappedValues: rawValues.map((value) => ({ id: generateId(), value })), + rawValuesLookup: new Set(rawValues), + }; + }, [currentConfig]); + + const { inputValue: localValues, handleInputChange } = useDebouncedValue({ + onChange: onChangeWrapped, + value: wrappedValues, + }); + + const onFieldSelectChange = useCallback( + (choice, index = 0) => { + const fields = [...localValues]; + + if (indexPattern.getFieldByName(choice.field)) { + fields[index] = { id: generateId(), value: choice.field }; + + // update the layer state + handleInputChange(fields); + } + }, + [localValues, indexPattern, handleInputChange] + ); + + const newBucketButton = ( + { + handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); + }} + label={i18n.translate('xpack.lens.xyChart.annotation.tooltip.addField', { + defaultMessage: 'Add field', + })} + /> + ); + + if (localValues.length === 0) { + return ( + <> + + {}}> + {i18n.translate('xpack.lens.xyChart.annotation.tooltip.noFields', { + defaultMessage: 'None selected', + })} + + + {newBucketButton} + + ); + } + const currentExistingField = existingFields[indexPattern.title]; + const disableActions = localValues.length === 2 && localValues.some(({ isNew }) => isNew); + const options = indexPattern.fields + .filter( + ({ displayName, type }) => + displayName && !rawValuesLookup.has(displayName) && supportedTypes.has(type) + ) + .map( + (field) => + ({ + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: fieldExists(currentExistingField, field.name), + compatible: true, + 'data-test-subj': `lnsXY-annotation-tooltip-fieldOption-${field.name}`, + } as FieldOption) + ) + .sort((a, b) => a.label.localeCompare(b.label)); + + return ( + <> + { + handleInputChange(updatedValues); + }} + onDragStart={() => {}} + droppableId="ANNOTATION_TOOLTIP_DROPPABLE_AREA" + items={localValues} + > + {localValues.map(({ id, value, isNew }, index) => { + const fieldIsValid = value ? Boolean(indexPattern.getFieldByName(value)) : true; + return ( + + {(provided) => ( + + {/* Empty for spacing */} + + + + + + + + { + handleInputChange(localValues.filter((_, i) => i !== index)); + }} + data-test-subj={`lnsXY-annotation-tooltip-removeField-${index}`} + isDisabled={disableActions && !isNew} + /> + + + )} + + ); + })} + + {newBucketButton} + + ); +} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts new file mode 100644 index 0000000000000..f446afb6be265 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + PointInTimeEventAnnotationConfig, + RangeEventAnnotationConfig, +} from '@kbn/event-annotation-plugin/common'; + +export type ManualEventAnnotationType = + | PointInTimeEventAnnotationConfig + | RangeEventAnnotationConfig; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index d55aa0aa12133..819dfe13c2ba2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -10,10 +10,14 @@ import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; -import type { VisualizationLayerWidgetProps, VisualizationType } from '../../../types'; -import { State, visualizationTypes, SeriesType } from '../types'; +import type { + VisualizationLayerHeaderContentProps, + VisualizationLayerWidgetProps, + VisualizationType, +} from '../../../types'; +import { State, visualizationTypes, SeriesType, XYAnnotationLayerConfig } from '../types'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; -import { StaticHeader } from '../../../shared_components'; +import { ChangeIndexPattern, StaticHeader } from '../../../shared_components'; import { updateLayer } from '.'; import { isAnnotationsLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; @@ -24,12 +28,21 @@ export function LayerHeader(props: VisualizationLayerWidgetProps) { } if (isReferenceLayer(layer)) { return ; - } else if (isAnnotationsLayer(layer)) { + } + if (isAnnotationsLayer(layer)) { return ; } return ; } +export function LayerHeaderContent(props: VisualizationLayerHeaderContentProps) { + const layer = props.state.layers.find((l) => l.layerId === props.layerId); + if (layer && isAnnotationsLayer(layer)) { + return ; + } + return null; +} + function ReferenceLayerHeader() { return ( ) { + const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingDataView', { + defaultMessage: 'Data view not found', + }); + const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[layerIndex] as XYAnnotationLayerConfig; + const currentIndexPattern = frame.dataViews.indexPatterns[layer.indexPatternId]; + + return ( + + ); +} + function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx index e11c69f766a32..b347a39c788ce 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { IconPosition } from '@kbn/expression-xy-plugin/common'; @@ -77,17 +77,67 @@ export interface MarkerDecorationConfig { icon?: T; iconPosition?: IconPosition; textVisibility?: boolean; + textField?: string; +} + +function getSelectedOption( + { textField, textVisibility }: MarkerDecorationConfig = {}, + isQueryBased?: boolean +) { + if (!textVisibility) { + return 'none'; + } + if (isQueryBased && textField) { + return 'field'; + } + return 'name'; } export function TextDecorationSetting({ currentConfig, setConfig, customIconSet, + isQueryBased, + children, }: { currentConfig?: MarkerDecorationConfig; setConfig: (config: MarkerDecorationConfig) => void; customIconSet?: IconSet; + isQueryBased?: boolean; + /** A children render function for custom sub fields on textDecoration change */ + children?: (textDecoration: 'none' | 'name' | 'field') => JSX.Element | null; }) { + // To model the temporary state for label based on field when user didn't pick up the field yet, + // use a local state + const [selectedVisibleOption, setVisibleOption] = useState<'none' | 'name' | 'field'>( + getSelectedOption(currentConfig, isQueryBased) + ); + const options = [ + { + id: `${idPrefix}none`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.none', { + defaultMessage: 'None', + }), + 'data-test-subj': 'lnsXY_textVisibility_none', + }, + { + id: `${idPrefix}name`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.name', { + defaultMessage: 'Name', + }), + 'data-test-subj': 'lnsXY_textVisibility_name', + }, + ]; + if (isQueryBased) { + options.push({ + id: `${idPrefix}field`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.field', { + defaultMessage: 'Field', + }), + 'data-test-subj': 'lnsXY_textVisibility_field', + }); + } + return ( ({ display="columnCompressed" fullWidth > - { - setConfig({ textVisibility: id === `${idPrefix}name` }); - }} - isFullWidth - /> +
+ { + const chosenOption = id.replace(idPrefix, '') as 'none' | 'name' | 'field'; + if (chosenOption === 'none') { + setConfig({ + textVisibility: false, + textField: undefined, + }); + } else if (chosenOption === 'field') { + setConfig({ + textVisibility: true, + }); + } else if (chosenOption === 'name') { + setConfig({ + textVisibility: true, + textField: undefined, + }); + } + + setVisibleOption(chosenOption); + }} + isFullWidth + /> + {children?.(selectedVisibleOption)} +
); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 676d1a0dafe29..a2a0463a41cc4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -549,9 +549,11 @@ describe('xy_suggestions', () => { const annotationLayer: XYAnnotationLayerConfig = { layerId: 'second', layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', annotations: [ { id: '1', + type: 'manual', key: { type: 'point_in_time', timestamp: '2020-20-22', diff --git a/x-pack/plugins/lens/readme.md b/x-pack/plugins/lens/readme.md index 47a1d82c36a15..41db47090cb47 100644 --- a/x-pack/plugins/lens/readme.md +++ b/x-pack/plugins/lens/readme.md @@ -148,6 +148,8 @@ Example: } ``` +**Important!** To prevent conflicts, it's important to not re-use ad-hoc data view ids for different specs. If you change the spec in some way, make sure to also change its id. This even applies across multiple embeddables, sessions, etc. Ideally, the id will be globally unique. You can use the `uuid` package to generate a new unique id every time when you are changing the spec in some way. However, make sure to also not change the id on every single render either, as this will have a substantial performance impact. + ## Refreshing a Lens embeddable The Lens embeddable is handling data fetching internally, this means as soon as the props change, it will trigger a new request if necessary. However, in some situations it's necessary to trigger a refresh even if the configuration of the chart doesn't change at all. Refreshing is managed using search sessions is Lens. To trigger a refresh without changing the actual configuration of a Lens embeddable, follow these steps: 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 8ae71538562ed..bbac982150000 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 @@ -27,6 +27,7 @@ import { commonUpdateVisLayerType, getLensCustomVisualizationMigrations, getLensFilterMigrations, + commonExplicitAnnotationType, getLensDataViewMigrations, commonMigrateMetricIds, commonMigratePartitionChartGroups, @@ -36,13 +37,14 @@ import { LensDocShape713, LensDocShape715, LensDocShape810, - LensDocShape840, + LensDocShape850, LensDocShapePre712, VisState716, VisState810, - VisState840, + VisState850, VisStatePre715, VisStatePre830, + XYVisState850, } from '../migrations/types'; import { extract, inject } from '../../common/embeddable_factory'; @@ -134,10 +136,16 @@ export const makeLensEmbeddableFactory = } as unknown as SerializableRecord; }, '8.5.0': (state) => { - const lensState = state as unknown as { attributes: LensDocShape840 }; + const lensState = state as unknown as { + attributes: LensDocShape850; + }; + let migratedLensState = commonMigrateMetricIds(lensState.attributes); + migratedLensState = commonExplicitAnnotationType( + migratedLensState as LensDocShape850 + ); migratedLensState = commonMigratePartitionChartGroups( - migratedLensState as LensDocShape840<{ + migratedLensState as LensDocShape850<{ shape: string; layers: Array<{ groups?: string[] }>; }> diff --git a/x-pack/plugins/lens/server/index.ts b/x-pack/plugins/lens/server/index.ts index d05a7b303a566..99f4f80f92672 100644 --- a/x-pack/plugins/lens/server/index.ts +++ b/x-pack/plugins/lens/server/index.ts @@ -8,12 +8,10 @@ // TODO: https://github.com/elastic/kibana/issues/110891 /* eslint-disable @kbn/eslint/no_export_all */ -import { PluginInitializerContext } from '@kbn/core/server'; import { LensServerPlugin } from './plugin'; export type { LensServerPluginSetup } from './plugin'; export * from './plugin'; export * from './migrations/types'; -export const plugin = (initializerContext: PluginInitializerContext) => - new LensServerPlugin(initializerContext); +export const plugin = () => new LensServerPlugin(); diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index b14fe7a3fa8fd..89dca4829c583 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -30,7 +30,8 @@ import { LensDocShape810, LensDocShape830, VisStatePre830, - LensDocShape840, + XYVisStatePre850, + VisState850, LensDocShape850, } from './types'; import { DOCUMENT_FIELD_NAME, layerTypes, LegacyMetricState, isPartitionShape } from '../../common'; @@ -422,16 +423,49 @@ export const commonFixValueLabelsInXY = ( }; }; +export const commonExplicitAnnotationType = ( + attributes: LensDocShape850 +): LensDocShape850 => { + // Skip the migration heavy part if not XY or it does not contain annotations + if ( + attributes.visualizationType !== 'lnsXY' || + attributes.state.visualization.layers.every((l) => l.layerType !== 'annotations') + ) { + return attributes as LensDocShape850; + } + const newAttributes = cloneDeep(attributes); + const { visualization } = newAttributes.state; + const { layers } = visualization; + return { + ...newAttributes, + state: { + ...newAttributes.state, + visualization: { + ...visualization, + layers: layers.map((l) => { + if (l.layerType !== 'annotations') { + return l; + } + return { + ...l, + annotations: l.annotations.map((a) => ({ ...a, type: 'manual' })), + }; + }), + }, + }, + }; +}; + export const commonMigrateMetricIds = ( - attributes: LensDocShape840 -): LensDocShape840 => { + attributes: LensDocShape850 +): LensDocShape850 => { const typeMappings = { lnsMetric: 'lnsLegacyMetric', lnsMetricNew: 'lnsMetric', } as Record; if (!attributes.visualizationType || !(attributes.visualizationType in typeMappings)) { - return attributes as LensDocShape840; + return attributes as LensDocShape850; } const newAttributes = cloneDeep(attributes); @@ -441,7 +475,7 @@ export const commonMigrateMetricIds = ( }; export const commonMigratePartitionChartGroups = ( - attributes: LensDocShape840<{ + attributes: LensDocShape850<{ shape: string; layers: Array<{ groups?: string[] }>; }> 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 3f60eddb73e8e..b07f801e53f11 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 @@ -22,6 +22,9 @@ import { VisState810, VisState820, VisState830, + LensDocShape850, + XYVisStatePre850, + VisState850, } from './types'; import { layerTypes, LegacyMetricState } from '../../common'; import { Filter } from '@kbn/es-query'; @@ -2291,6 +2294,44 @@ describe('Lens migrations', () => { }); }); + describe('8.5.0 Add Annotation event type and dataView references', () => { + const context = { log: { warn: () => {} } } as unknown as SavedObjectMigrationContext; + const example = { + type: 'lens', + id: 'mocked-saved-object-id', + attributes: { + savedObjectId: '1', + title: 'MyRenamedOps', + description: '', + visualizationType: 'lnsXY', + state: { + visualization: { + layers: [ + { layerType: 'data' }, + { + layerType: 'annotations', + annotations: [{ id: 'annotation-id' }], + }, + ], + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc>; + + it('migrates existing annotation events as manual type', () => { + const result = migrations['8.5.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + const visState = result.attributes.state.visualization as VisState850; + const [dataLayer, annotationLayer] = visState.layers; + expect(dataLayer).toEqual({ layerType: 'data' }); + expect(annotationLayer).toEqual({ + layerType: 'annotations', + annotations: [{ id: 'annotation-id', type: 'manual' }], + }); + }); + }); + describe('8.5.0 migrates metric IDs', () => { const context = { log: { warn: () => {} } } as unknown as SavedObjectMigrationContext; const example = { 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 0dd3474b139d8..e48f46ad885c5 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -34,7 +34,10 @@ import { XYVisualizationState830, VisState810, VisState820, + XYVisStatePre850, + LensDocShape850, LensDocShape840, + VisState850, } from './types'; import { commonRenameOperationsForFormula, @@ -52,6 +55,7 @@ import { commonFixValueLabelsInXY, commonLockOldMetricVisSettings, commonPreserveOldLegendSizeDefault, + commonExplicitAnnotationType, getLensDataViewMigrations, commonMigrateMetricIds, commonMigratePartitionChartGroups, @@ -516,7 +520,15 @@ const preserveOldLegendSizeDefault: SavedObjectMigrationFn ({ ...doc, attributes: commonPreserveOldLegendSizeDefault(doc.attributes) }); -const migrateMetricIds: SavedObjectMigrationFn = (doc) => ({ +const addEventAnnotationType: SavedObjectMigrationFn< + LensDocShape850, + LensDocShape850 +> = (doc) => { + const newDoc = cloneDeep(doc); + return { ...newDoc, attributes: commonExplicitAnnotationType(newDoc.attributes) }; +}; + +const migrateMetricIds: SavedObjectMigrationFn = (doc) => ({ ...doc, attributes: commonMigrateMetricIds(doc.attributes), }); @@ -553,7 +565,7 @@ const lensMigrations: SavedObjectMigrationMap = { enhanceTableRowHeight ), '8.3.0': flow(lockOldMetricVisSettings, preserveOldLegendSizeDefault, fixValueLabelsInXY), - '8.5.0': flow(migrateMetricIds, migratePartitionChartGroups), + '8.5.0': flow(migrateMetricIds, addEventAnnotationType, migratePartitionChartGroups), }; export const getAllMigrations = ( diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts index 71dae1619db59..a922223b6ccd7 100644 --- a/x-pack/plugins/lens/server/migrations/types.ts +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -270,6 +270,34 @@ export interface XYVisualizationState830 extends VisState820 { export type VisStatePre830 = XYVisualizationStatePre830; export type VisState830 = XYVisualizationState830; +export interface XYVisStatePre850 { + layers: Array< + | { + layerId: string; + layerType: Exclude; + } + | { + layerId: string; + layerType: Extract; + annotations: Array<{ id: string }>; + } + >; +} + +export interface XYVisState850 { + layers: Array< + | { + layerId: string; + layerType: Exclude; + } + | { + layerId: string; + layerType: Extract; + annotations: Array<{ id: string; type: 'manual' | 'query' }>; + } + >; +} +export type VisState850 = XYVisState850; export type VisState840 = VisState830; export type LensDocShape840 = LensDocShape830; diff --git a/x-pack/plugins/lens/server/plugin.tsx b/x-pack/plugins/lens/server/plugin.tsx index baaa663616d2f..03adef0d2b10a 100644 --- a/x-pack/plugins/lens/server/plugin.tsx +++ b/x-pack/plugins/lens/server/plugin.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from '@kbn/core/server'; +import { Plugin, CoreSetup, CoreStart } from '@kbn/core/server'; import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { PluginStart as DataPluginStart, @@ -21,8 +21,6 @@ import { } from '@kbn/task-manager-plugin/server'; import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; -import { setupRoutes } from './routes'; -import { getUiSettings } from './ui_settings'; import { setupSavedObjects } from './saved_objects'; import { setupExpressions } from './expressions'; import { makeLensEmbeddableFactory } from './embeddable/make_lens_embeddable_factory'; @@ -59,16 +57,14 @@ export interface LensServerPluginSetup { export class LensServerPlugin implements Plugin { private customVisualizationMigrations: CustomVisualizationMigrations = {}; - constructor(private initializerContext: PluginInitializerContext) {} + constructor() {} setup(core: CoreSetup, plugins: PluginSetupContract) { const getFilterMigrations = plugins.data.query.filterManager.getAllMigrations.bind( plugins.data.query.filterManager ); setupSavedObjects(core, getFilterMigrations, this.customVisualizationMigrations); - setupRoutes(core, this.initializerContext.logger.get()); setupExpressions(core, plugins.expressions); - core.uiSettings.register(getUiSettings()); const lensEmbeddableFactory = makeLensEmbeddableFactory( getFilterMigrations, diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts deleted file mode 100644 index 7fd15d8bdfaf7..0000000000000 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { errors } from '@elastic/elasticsearch'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { schema } from '@kbn/config-schema'; -import { RequestHandlerContext, ElasticsearchClient } from '@kbn/core/server'; -import { CoreSetup, Logger } from '@kbn/core/server'; -import { RuntimeField } from '@kbn/data-views-plugin/common'; -import { DataViewsService, DataView, FieldSpec, DataViewSpec } from '@kbn/data-views-plugin/common'; -import { UI_SETTINGS } from '@kbn/data-plugin/server'; -import { BASE_API_URL } from '../../common'; -import { FIELD_EXISTENCE_SETTING } from '../ui_settings'; -import { PluginStartContract } from '../plugin'; - -export function isBoomError(error: { isBoom?: boolean }): error is Boom.Boom { - return error.isBoom === true; -} - -/** - * The number of docs to sample to determine field empty status. - */ -const SAMPLE_SIZE = 500; - -export interface Field { - name: string; - isScript: boolean; - isMeta: boolean; - lang?: estypes.ScriptLanguage; - script?: string; - runtimeField?: RuntimeField; -} - -export async function existingFieldsRoute(setup: CoreSetup, logger: Logger) { - const router = setup.http.createRouter(); - - router.post( - { - path: `${BASE_API_URL}/existing_fields/{indexPatternId}`, - validate: { - params: schema.object({ - indexPatternId: schema.string(), - }), - body: schema.object({ - dslQuery: schema.object({}, { unknowns: 'allow' }), - fromDate: schema.maybe(schema.string()), - toDate: schema.maybe(schema.string()), - timeFieldName: schema.maybe(schema.string()), - spec: schema.object({}, { unknowns: 'allow' }), - }), - }, - }, - async (context, req, res) => { - const [{ savedObjects, elasticsearch, uiSettings }, { dataViews }] = - await setup.getStartServices(); - const savedObjectsClient = savedObjects.getScopedClient(req); - const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); - const [includeFrozen, useSampling]: boolean[] = await Promise.all([ - uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), - uiSettingsClient.get(FIELD_EXISTENCE_SETTING), - ]); - const esClient = elasticsearch.client.asScoped(req).asCurrentUser; - try { - return res.ok({ - body: await fetchFieldExistence({ - ...req.params, - ...req.body, - dataViewsService: await dataViews.dataViewsServiceFactory(savedObjectsClient, esClient), - context, - includeFrozen, - useSampling, - }), - }); - } catch (e) { - if (e instanceof errors.TimeoutError) { - logger.info(`Field existence check timed out on ${req.params.indexPatternId}`); - // 408 is Request Timeout - return res.customError({ statusCode: 408, body: e.message }); - } - logger.info( - `Field existence check failed on ${req.params.indexPatternId}: ${ - isBoomError(e) ? e.output.payload.message : e.message - }` - ); - if (e instanceof errors.ResponseError && e.statusCode === 404) { - return res.notFound({ body: e.message }); - } - if (isBoomError(e)) { - if (e.output.statusCode === 404) { - return res.notFound({ body: e.output.payload.message }); - } - throw new Error(e.output.payload.message); - } else { - throw e; - } - } - } - ); -} - -async function fetchFieldExistence({ - context, - indexPatternId, - dataViewsService, - dslQuery = { match_all: {} }, - fromDate, - toDate, - timeFieldName, - spec, - includeFrozen, - useSampling, -}: { - indexPatternId: string; - context: RequestHandlerContext; - dataViewsService: DataViewsService; - dslQuery: object; - fromDate?: string; - toDate?: string; - timeFieldName?: string; - spec?: DataViewSpec; - includeFrozen: boolean; - useSampling: boolean; -}) { - if (useSampling) { - return legacyFetchFieldExistenceSampling({ - context, - indexPatternId, - dataViewsService, - dslQuery, - fromDate, - toDate, - timeFieldName, - spec, - includeFrozen, - }); - } - - const uiSettingsClient = (await context.core).uiSettings.client; - const metaFields: string[] = await uiSettingsClient.get(UI_SETTINGS.META_FIELDS); - const dataView = - spec && Object.keys(spec).length !== 0 - ? await dataViewsService.create(spec) - : await dataViewsService.get(indexPatternId); - const allFields = buildFieldList(dataView, metaFields); - const existingFieldList = await dataViewsService.getFieldsForIndexPattern(dataView, { - // filled in by data views service - pattern: '', - filter: toQuery(timeFieldName, fromDate, toDate, dslQuery), - }); - return { - indexPatternTitle: dataView.title, - existingFieldNames: existingFields(existingFieldList, allFields), - }; -} - -async function legacyFetchFieldExistenceSampling({ - context, - indexPatternId, - dataViewsService, - dslQuery, - fromDate, - toDate, - timeFieldName, - spec, - includeFrozen, -}: { - indexPatternId: string; - context: RequestHandlerContext; - dataViewsService: DataViewsService; - dslQuery: object; - fromDate?: string; - toDate?: string; - timeFieldName?: string; - spec?: DataViewSpec; - includeFrozen: boolean; -}) { - const coreContext = await context.core; - const metaFields: string[] = await coreContext.uiSettings.client.get(UI_SETTINGS.META_FIELDS); - const indexPattern = - spec && Object.keys(spec).length !== 0 - ? await dataViewsService.create(spec) - : await dataViewsService.get(indexPatternId); - - const fields = buildFieldList(indexPattern, metaFields); - const runtimeMappings = indexPattern.getRuntimeMappings(); - - const docs = await fetchIndexPatternStats({ - fromDate, - toDate, - dslQuery, - client: coreContext.elasticsearch.client.asCurrentUser, - index: indexPattern.title, - timeFieldName: timeFieldName || indexPattern.timeFieldName, - fields, - runtimeMappings, - includeFrozen, - }); - - return { - indexPatternTitle: indexPattern.title, - existingFieldNames: legacyExistingFields(docs, fields), - }; -} - -/** - * Exported only for unit tests. - */ -export function buildFieldList(indexPattern: DataView, metaFields: string[]): Field[] { - return indexPattern.fields.map((field) => { - return { - name: field.name, - isScript: !!field.scripted, - lang: field.lang, - script: field.script, - // id is a special case - it doesn't show up in the meta field list, - // but as it's not part of source, it has to be handled separately. - isMeta: metaFields.includes(field.name) || field.name === '_id', - runtimeField: !field.isMapped ? field.runtimeField : undefined, - }; - }); -} - -async function fetchIndexPatternStats({ - client, - index, - dslQuery, - timeFieldName, - fromDate, - toDate, - fields, - runtimeMappings, - includeFrozen, -}: { - client: ElasticsearchClient; - index: string; - dslQuery: object; - timeFieldName?: string; - fromDate?: string; - toDate?: string; - fields: Field[]; - runtimeMappings: estypes.MappingRuntimeFields; - includeFrozen: boolean; -}) { - const query = toQuery(timeFieldName, fromDate, toDate, dslQuery); - - const scriptedFields = fields.filter((f) => f.isScript); - const result = await client.search( - { - index, - ...(includeFrozen ? { ignore_throttled: false } : {}), - body: { - size: SAMPLE_SIZE, - query, - // Sorted queries are usually able to skip entire shards that don't match - sort: timeFieldName && fromDate && toDate ? [{ [timeFieldName]: 'desc' }] : [], - fields: ['*'], - _source: false, - runtime_mappings: runtimeMappings, - script_fields: scriptedFields.reduce((acc, field) => { - acc[field.name] = { - script: { - lang: field.lang!, - source: field.script!, - }, - }; - return acc; - }, {} as Record), - // Small improvement because there is overhead in counting - track_total_hits: false, - // Per-shard timeout, must be lower than overall. Shards return partial results on timeout - timeout: '4500ms', - }, - }, - { - // Global request timeout. Will cancel the request if exceeded. Overrides the elasticsearch.requestTimeout - requestTimeout: '5000ms', - // Fails fast instead of retrying- default is to retry - maxRetries: 0, - } - ); - return result.hits.hits; -} - -function toQuery( - timeFieldName: string | undefined, - fromDate: string | undefined, - toDate: string | undefined, - dslQuery: object -) { - const filter = - timeFieldName && fromDate && toDate - ? [ - { - range: { - [timeFieldName]: { - format: 'strict_date_optional_time', - gte: fromDate, - lte: toDate, - }, - }, - }, - dslQuery, - ] - : [dslQuery]; - - const query = { - bool: { - filter, - }, - }; - return query; -} - -/** - * Exported only for unit tests. - */ -export function existingFields(filteredFields: FieldSpec[], allFields: Field[]): string[] { - const filteredFieldsSet = new Set(filteredFields.map((f) => f.name)); - - return allFields - .filter((field) => field.isScript || field.runtimeField || filteredFieldsSet.has(field.name)) - .map((f) => f.name); -} - -/** - * Exported only for unit tests. - */ -export function legacyExistingFields(docs: estypes.SearchHit[], fields: Field[]): string[] { - const missingFields = new Set(fields); - - for (const doc of docs) { - if (missingFields.size === 0) { - break; - } - - missingFields.forEach((field) => { - let fieldStore = doc.fields!; - if (field.isMeta) { - fieldStore = doc; - } - const value = fieldStore[field.name]; - if (Array.isArray(value) && value.length) { - missingFields.delete(field); - } else if (!Array.isArray(value) && value) { - missingFields.delete(field); - } - }); - } - - return fields.filter((field) => !missingFields.has(field)).map((f) => f.name); -} diff --git a/x-pack/plugins/lens/server/routes/index.ts b/x-pack/plugins/lens/server/routes/index.ts deleted file mode 100644 index c8145cd54b9a0..0000000000000 --- a/x-pack/plugins/lens/server/routes/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 { CoreSetup, Logger } from '@kbn/core/server'; -import { PluginStartContract } from '../plugin'; -import { existingFieldsRoute } from './existing_fields'; - -export function setupRoutes(setup: CoreSetup, logger: Logger) { - existingFieldsRoute(setup, logger); -} diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index a7d55139b7f5a..c6f423184f926 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -380,7 +380,6 @@ describe('Exceptions Lists API', () => { test('it invokes "fetchExceptionListsItemsByListIds" with expected url and body values', async () => { await fetchExceptionListsItemsByListIds({ - filterOptions: [], http: httpMock, listIds: ['myList', 'myOtherListId'], namespaceTypes: ['single', 'single'], @@ -405,14 +404,9 @@ describe('Exceptions Lists API', () => { }); }); - test('it invokes with expected url and body values when a filter exists and "namespaceType" of "single"', async () => { + test('it invokes with expected url and body values when a filter exists', async () => { await fetchExceptionListsItemsByListIds({ - filterOptions: [ - { - filter: 'hello world', - tags: [], - }, - ], + filter: 'exception-list.attributes.entries.field:hello world*', http: httpMock, listIds: ['myList'], namespaceTypes: ['single'], @@ -438,80 +432,8 @@ describe('Exceptions Lists API', () => { }); }); - test('it invokes with expected url and body values when a filter exists and "namespaceType" of "agnostic"', async () => { - await fetchExceptionListsItemsByListIds({ - filterOptions: [ - { - filter: 'hello world', - tags: [], - }, - ], - http: httpMock, - listIds: ['myList'], - namespaceTypes: ['agnostic'], - pagination: { - page: 1, - perPage: 20, - }, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/items/_find', { - method: 'GET', - query: { - filter: 'exception-list-agnostic.attributes.entries.field:hello world*', - list_id: 'myList', - namespace_type: 'agnostic', - page: '1', - per_page: '20', - sort_field: 'exception-list.created_at', - sort_order: 'desc', - }, - signal: abortCtrl.signal, - }); - }); - - test('it invokes with expected url and body values when tags exists', async () => { + test('it invokes with expected url and body values when search exists', async () => { await fetchExceptionListsItemsByListIds({ - filterOptions: [ - { - filter: '', - tags: ['malware'], - }, - ], - http: httpMock, - listIds: ['myList'], - namespaceTypes: ['agnostic'], - pagination: { - page: 1, - perPage: 20, - }, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/items/_find', { - method: 'GET', - query: { - filter: 'exception-list-agnostic.attributes.tags:malware', - list_id: 'myList', - namespace_type: 'agnostic', - page: '1', - per_page: '20', - sort_field: 'exception-list.created_at', - sort_order: 'desc', - }, - signal: abortCtrl.signal, - }); - }); - - test('it invokes with expected url and body values when filter and tags exists', async () => { - await fetchExceptionListsItemsByListIds({ - filterOptions: [ - { - filter: 'host.name', - tags: ['malware'], - }, - ], http: httpMock, listIds: ['myList'], namespaceTypes: ['agnostic'], @@ -519,18 +441,18 @@ describe('Exceptions Lists API', () => { page: 1, perPage: 20, }, + search: '-@timestamp', signal: abortCtrl.signal, }); expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/items/_find', { method: 'GET', query: { - filter: - 'exception-list-agnostic.attributes.entries.field:host.name* AND exception-list-agnostic.attributes.tags:malware', list_id: 'myList', namespace_type: 'agnostic', page: '1', per_page: '20', + search: '-@timestamp', sort_field: 'exception-list.created_at', sort_order: 'desc', }, @@ -540,7 +462,6 @@ describe('Exceptions Lists API', () => { test('it returns expected format when call succeeds', async () => { const exceptionResponse = await fetchExceptionListsItemsByListIds({ - filterOptions: [], http: httpMock, listIds: ['endpoint_list_id'], namespaceTypes: ['single'], @@ -561,7 +482,6 @@ describe('Exceptions Lists API', () => { await expect( fetchExceptionListsItemsByListIds({ - filterOptions: [], http: httpMock, listIds: ['myList'], namespaceTypes: ['single'], diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts index 0bd97dffb34f8..bf10fb57f1a5c 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts @@ -297,7 +297,6 @@ describe('useApi', () => { await waitForNextUpdate(); await result.current.getExceptionListsItems({ - filterOptions: [], lists: [ { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, ], @@ -313,7 +312,6 @@ describe('useApi', () => { }); const expected: ApiCallByListIdProps = { - filterOptions: [], http: mockKibanaHttpService, listIds: ['list_id'], namespaceTypes: ['single'], @@ -351,7 +349,6 @@ describe('useApi', () => { await waitForNextUpdate(); await result.current.getExceptionListsItems({ - filterOptions: [], lists: [ { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, ], @@ -389,7 +386,6 @@ describe('useApi', () => { await waitForNextUpdate(); await result.current.getExceptionListsItems({ - filterOptions: [], lists: [ { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, ], diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts deleted file mode 100644 index d8ae72d4d6205..0000000000000 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 type { - ExceptionListItemSchema, - UseExceptionListItemsSuccess, - UseExceptionListProps, -} from '@kbn/securitysolution-io-ts-list-types'; -import * as api from '@kbn/securitysolution-list-api'; -import { - ReturnExceptionListAndItems, - transformInput, - useExceptionListItems, -} from '@kbn/securitysolution-list-hooks'; -import { coreMock } from '@kbn/core/public/mocks'; - -import { getFoundExceptionListItemSchemaMock } from '../../../common/schemas/response/found_exception_list_item_schema.mock'; - -jest.mock('uuid', () => ({ - v4: jest.fn().mockReturnValue('123'), -})); -jest.mock('@kbn/securitysolution-list-api'); - -const mockKibanaHttpService = coreMock.createStart().http; - -// TODO: Port all of this test code over to the package of: packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts once the mocks and kibana core mocks are figured out - -describe('useExceptionListItems', () => { - const onErrorMock = jest.fn(); - - beforeEach(() => { - jest - .spyOn(api, 'fetchExceptionListsItemsByListIds') - .mockResolvedValue(getFoundExceptionListItemSchemaMock()); - }); - - afterEach(() => { - onErrorMock.mockClear(); - jest.clearAllMocks(); - }); - - test('initializes hook', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook< - UseExceptionListProps, - ReturnExceptionListAndItems - >(() => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }) - ); - await waitForNextUpdate(); - - expect(result.current).toEqual([ - true, - [], - { - page: 1, - perPage: 20, - total: 0, - }, - null, - ]); - }); - }); - - test('fetches exception items', async () => { - await act(async () => { - const onSuccessMock = jest.fn(); - const { result, waitForNextUpdate } = renderHook< - UseExceptionListProps, - ReturnExceptionListAndItems - >(() => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - const expectedListItemsResult: ExceptionListItemSchema[] = - getFoundExceptionListItemSchemaMock().data.map((item) => transformInput(item)); - const expectedResult: UseExceptionListItemsSuccess = { - exceptions: expectedListItemsResult, - pagination: { page: 1, perPage: 1, total: 1 }, - }; - - expect(result.current).toEqual([ - false, - expectedListItemsResult, - { - page: 1, - perPage: 1, - total: 1, - }, - result.current[3], - ]); - expect(onSuccessMock).toHaveBeenCalledWith(expectedResult); - }); - }); - - test('fetches only detection list items if "showDetectionsListsOnly" is true', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - - await act(async () => { - const onSuccessMock = jest.fn(); - const { waitForNextUpdate } = renderHook( - () => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: true, - showEndpointListsOnly: false, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledWith({ - filterOptions: [], - http: mockKibanaHttpService, - listIds: ['list_id'], - namespaceTypes: ['single'], - pagination: { page: 1, perPage: 20 }, - signal: new AbortController().signal, - }); - }); - }); - - test('fetches only detection list items if "showEndpointListsOnly" is true', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - - await act(async () => { - const onSuccessMock = jest.fn(); - const { waitForNextUpdate } = renderHook( - () => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: true, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledWith({ - filterOptions: [], - http: mockKibanaHttpService, - listIds: ['list_id_endpoint'], - namespaceTypes: ['agnostic'], - pagination: { page: 1, perPage: 20 }, - signal: new AbortController().signal, - }); - }); - }); - - test('does not fetch items if no lists to fetch', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - - await act(async () => { - const onSuccessMock = jest.fn(); - const { result, waitForNextUpdate } = renderHook< - UseExceptionListProps, - ReturnExceptionListAndItems - >(() => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: true, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).not.toHaveBeenCalled(); - expect(result.current).toEqual([ - false, - [], - { - page: 0, - perPage: 20, - total: 0, - }, - result.current[3], - ]); - }); - }); - - test('applies first filterOptions filter to all lists if "matchFilters" is true', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - - await act(async () => { - const onSuccessMock = jest.fn(); - const { waitForNextUpdate } = renderHook( - () => - useExceptionListItems({ - filterOptions: [{ filter: 'host.name', tags: [] }], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - matchFilters: true, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledWith({ - filterOptions: [ - { filter: 'host.name', tags: [] }, - { filter: 'host.name', tags: [] }, - ], - http: mockKibanaHttpService, - listIds: ['list_id', 'list_id_endpoint'], - namespaceTypes: ['single', 'agnostic'], - pagination: { page: 1, perPage: 20 }, - signal: new AbortController().signal, - }); - }); - }); - - test('fetches a new exception list and its items', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - const onSuccessMock = jest.fn(); - await act(async () => { - const { rerender, waitForNextUpdate } = renderHook< - UseExceptionListProps, - ReturnExceptionListAndItems - >( - ({ - filterOptions, - http, - lists, - matchFilters, - pagination, - onError, - onSuccess, - showDetectionsListsOnly, - showEndpointListsOnly, - }) => - useExceptionListItems({ - filterOptions, - http, - lists, - matchFilters, - onError, - onSuccess, - pagination, - showDetectionsListsOnly, - showEndpointListsOnly, - }), - { - initialProps: { - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }, - } - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - rerender({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'newListId', listId: 'new_list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - onSuccess: onSuccessMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }); - // NOTE: Only need one call here because hook already initilaized - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledTimes(2); - }); - }); - - test('fetches list and items when refreshExceptionList callback invoked', async () => { - const spyOnfetchExceptionListsItemsByListIds = jest.spyOn( - api, - 'fetchExceptionListsItemsByListIds' - ); - await act(async () => { - const { result, waitForNextUpdate } = renderHook< - UseExceptionListProps, - ReturnExceptionListAndItems - >(() => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(typeof result.current[3]).toEqual('function'); - - if (result.current[3] != null) { - result.current[3](); - } - // NOTE: Only need one call here because hook already initilaized - await waitForNextUpdate(); - - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledTimes(2); - }); - }); - - test('invokes "onError" callback if "fetchExceptionListsItemsByListIds" fails', async () => { - const mockError = new Error('failed to fetches list items'); - const spyOnfetchExceptionListsItemsByListIds = jest - .spyOn(api, 'fetchExceptionListsItemsByListIds') - .mockRejectedValue(mockError); - await act(async () => { - const { waitForNextUpdate } = renderHook( - () => - useExceptionListItems({ - filterOptions: [], - http: mockKibanaHttpService, - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - ], - matchFilters: false, - onError: onErrorMock, - pagination: { - page: 1, - perPage: 20, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: false, - }) - ); - // NOTE: First `waitForNextUpdate` is initialization - // Second call applies the params - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(onErrorMock).toHaveBeenCalledWith(mockError); - expect(spyOnfetchExceptionListsItemsByListIds).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts index b28ebf26b012c..f77a3a7327d69 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -42,6 +42,7 @@ export const findExceptionListItemRoute = (router: ListsPluginRouter): void => { namespace_type: namespaceType, page, per_page: perPage, + search, sort_field: sortField, sort_order: sortOrder, } = request.query; @@ -59,6 +60,7 @@ export const findExceptionListItemRoute = (router: ListsPluginRouter): void => { page, perPage, pit: undefined, + search, searchAfter: undefined, sortField, sortOrder, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index c586a9d764147..baa9d943127f7 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -717,6 +717,7 @@ export class ExceptionListClient { perPage, pit, page, + search, searchAfter, sortField, sortOrder, @@ -750,6 +751,7 @@ export class ExceptionListClient { perPage, pit, savedObjectsClient, + search, searchAfter, sortField, sortOrder, @@ -764,6 +766,7 @@ export class ExceptionListClient { * @param options.perPage How many per page to return * @param options.pit The Point in Time (pit) id if there is one, otherwise "undefined" can be sent in * @param options.page The page number or "undefined" if there is no page number to continue from + * @param options.search The simple query search parameter if there is one, otherwise "undefined" can be sent in * @param options.searchAfter The search_after parameter if there is one, otherwise "undefined" can be sent in * @param options.sortField The sort field string if there is one, otherwise "undefined" can be sent in * @param options.sortOder The sort order string of "asc", "desc", otherwise "undefined" if there is no preference @@ -776,6 +779,7 @@ export class ExceptionListClient { perPage, pit, page, + search, searchAfter, sortField, sortOrder, @@ -793,6 +797,7 @@ export class ExceptionListClient { page, perPage, pit, + search, searchAfter, sortField, sortOrder, @@ -809,6 +814,7 @@ export class ExceptionListClient { perPage, pit, savedObjectsClient, + search, searchAfter, sortField, sortOrder, @@ -898,6 +904,7 @@ export class ExceptionListClient { * @param options.perPage How many per page to return * @param options.page The page number or "undefined" if there is no page number to continue from * @param options.pit The Point in Time (pit) id if there is one, otherwise "undefined" can be sent in + * @param options.search The simple query search parameter if there is one, otherwise "undefined" can be sent in * @param options.searchAfter The search_after parameter if there is one, otherwise "undefined" can be sent in * @param options.sortField The sort field string if there is one, otherwise "undefined" can be sent in * @param options.sortOrder The sort order of "asc" or "desc", otherwise "undefined" can be sent in @@ -908,6 +915,7 @@ export class ExceptionListClient { perPage, page, pit, + search, searchAfter, sortField, sortOrder, @@ -922,6 +930,7 @@ export class ExceptionListClient { perPage, pit, savedObjectsClient, + search, searchAfter, sortField, sortOrder, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index 2b3a800ac5a5a..048930e51b93d 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -46,6 +46,7 @@ import type { PitId, PitOrUndefined, SearchAfterOrUndefined, + SearchOrUndefined, SortFieldOrUndefined, SortOrderOrUndefined, Tags, @@ -361,6 +362,8 @@ export interface FindExceptionListItemOptions { perPage: PerPageOrUndefined; /** The Point in Time (pit) id if there is one, otherwise "undefined" can be send in */ pit?: PitOrUndefined; + /** The simple search parameter if there is one, otherwise "undefined" can be sent in */ + search?: SearchOrUndefined; /** The search_after parameter if there is one, otherwise "undefined" can be sent in */ searchAfter?: SearchAfterOrUndefined; /** The page number or "undefined" if there is no page number to continue from */ @@ -382,6 +385,8 @@ export interface FindEndpointListItemOptions { perPage: PerPageOrUndefined; /** The Point in Time (pit) id if there is one, otherwise "undefined" can be sent in */ pit?: PitOrUndefined; + /** The simple search parameter if there is one, otherwise "undefined" can be sent in */ + search?: SearchOrUndefined; /** The search_after parameter if there is one, otherwise "undefined" can be sent in */ searchAfter?: SearchAfterOrUndefined; /** The page number or "undefined" if there is no page number to continue from */ @@ -407,6 +412,8 @@ export interface FindExceptionListsItemOptions { perPage: PerPageOrUndefined; /** The Point in Time (pit) id if there is one, otherwise "undefined" can be sent in */ pit?: PitOrUndefined; + /** The simple search parameter if there is one, otherwise "undefined" can be sent in */ + search?: SearchOrUndefined; /** The search_after parameter if there is one, otherwise "undefined" can be sent in */ searchAfter?: SearchAfterOrUndefined; /** The page number or "undefined" if there is no page number to continue from */ diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts index c08057d77ebbe..fc41afd7563c7 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts @@ -15,6 +15,7 @@ import type { PerPageOrUndefined, PitOrUndefined, SearchAfterOrUndefined, + SearchOrUndefined, SortFieldOrUndefined, SortOrderOrUndefined, } from '@kbn/securitysolution-io-ts-list-types'; @@ -29,6 +30,7 @@ interface FindExceptionListItemOptions { page: PageOrUndefined; perPage: PerPageOrUndefined; pit: PitOrUndefined; + search: SearchOrUndefined; sortField: SortFieldOrUndefined; sortOrder: SortOrderOrUndefined; searchAfter: SearchAfterOrUndefined; @@ -42,6 +44,7 @@ export const findExceptionListItem = async ({ page, perPage, pit, + search, searchAfter, sortField, sortOrder, @@ -54,6 +57,7 @@ export const findExceptionListItem = async ({ perPage, pit, savedObjectsClient, + search, searchAfter, sortField, sortOrder, diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts index f0e1fa07749f8..f3fd291ecd067 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts @@ -13,6 +13,7 @@ import type { PerPageOrUndefined, PitOrUndefined, SearchAfterOrUndefined, + SearchOrUndefined, SortFieldOrUndefined, SortOrderOrUndefined, } from '@kbn/securitysolution-io-ts-list-types'; @@ -39,6 +40,7 @@ interface FindExceptionListItemsOptions { sortField: SortFieldOrUndefined; sortOrder: SortOrderOrUndefined; searchAfter: SearchAfterOrUndefined; + search: SearchOrUndefined; } export const findExceptionListsItem = async ({ @@ -49,6 +51,7 @@ export const findExceptionListsItem = async ({ page, pit, perPage, + search, searchAfter, sortField, sortOrder, @@ -74,6 +77,7 @@ export const findExceptionListsItem = async ({ page, perPage, pit, + search, searchAfter, sortField, sortOrder, 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 d61e3e46a1119..a1d29c8db1363 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 @@ -23,7 +23,7 @@ import { TiledSingleLayerVectorSourceDescriptor, VectorLayerDescriptor, } from '../../../../../common/descriptor_types'; -import { SOURCE_TYPES } from '../../../../../common/constants'; +import { LAYER_TYPE, SOURCE_TYPES } from '../../../../../common/constants'; import { MvtVectorLayer } from './mvt_vector_layer'; const defaultConfig = { @@ -63,6 +63,11 @@ function createLayer( return new MvtVectorLayer({ layerDescriptor, source: mvtSource, customIcons: [] }); } +test('should have type MVT_VECTOR_LAYER', () => { + const layer: MvtVectorLayer = createLayer({}, {}); + expect(layer.getType()).toEqual(LAYER_TYPE.MVT_VECTOR); +}); + describe('visiblity', () => { it('should get minzoom from source', async () => { const layer: MvtVectorLayer = createLayer({}, {}); 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 3c78bf954e258..35a5caa7ff9b8 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 @@ -123,7 +123,8 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { mapColors?: string[] ): VectorLayerDescriptor { const layerDescriptor = super.createDescriptor(options) as VectorLayerDescriptor; - layerDescriptor.type = LAYER_TYPE.GEOJSON_VECTOR; + layerDescriptor.type = + layerDescriptor.type !== undefined ? layerDescriptor.type : LAYER_TYPE.GEOJSON_VECTOR; if (!options.style) { const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); diff --git a/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx b/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx index 5d9a7fd3a8416..2a8ce6e04144c 100644 --- a/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx +++ b/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx @@ -6,6 +6,7 @@ */ import React, { FC } from 'react'; +import { pick } from 'lodash'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; @@ -20,9 +21,7 @@ import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; import { MlPageHeader } from '../components/page_header'; export const ExplainLogRateSpikesPage: FC = () => { - const { - services: { docLinks }, - } = useMlKibana(); + const { services } = useMlKibana(); const context = useMlContext(); const dataView = context.currentDataView; @@ -43,8 +42,25 @@ export const ExplainLogRateSpikesPage: FC = () => { - {dataView && } - + {dataView && ( + + )} + ); }; diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 8e9a7d67d10ac..fbfea9ef9a05b 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -85,12 +85,12 @@ const App: FC = ({ coreStart, deps, appMountParams }) => { maps: deps.maps, triggersActionsUi: deps.triggersActionsUi, dataVisualizer: deps.dataVisualizer, - aiops: deps.aiops, usageCollection: deps.usageCollection, fieldFormats: deps.fieldFormats, dashboard: deps.dashboard, charts: deps.charts, cases: deps.cases, + unifiedSearch: deps.unifiedSearch, ...coreStart, }; @@ -140,7 +140,6 @@ export const renderApp = ( dashboard: deps.dashboard, maps: deps.maps, dataVisualizer: deps.dataVisualizer, - aiops: deps.aiops, dataViews: deps.data.dataViews, }); diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts index e8e694b1b6f21..941352c8f8520 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/kibana_context.ts @@ -16,13 +16,13 @@ import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { MapsStartApi } from '@kbn/maps-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; -import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import type { DashboardSetup } from '@kbn/dashboard-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { CasesUiStart } from '@kbn/cases-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { MlServicesContext } from '../../app'; interface StartPlugins { @@ -34,13 +34,13 @@ interface StartPlugins { maps?: MapsStartApi; triggersActionsUi?: TriggersAndActionsUIPublicPluginStart; dataVisualizer?: DataVisualizerPluginStart; - aiops?: AiopsPluginStart; usageCollection?: UsageCollectionSetup; fieldFormats: FieldFormatsRegistry; dashboard: DashboardSetup; spacesApi: SpacesPluginStart; charts: ChartsPluginStart; cases?: CasesUiStart; + unifiedSearch: UnifiedSearchPublicPluginStart; } export type StartServices = CoreStart & StartPlugins & { diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index 00895cdb3990e..3680f8b63b0c9 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -27,7 +27,6 @@ import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { SecurityPluginSetup } from '@kbn/security-plugin/public'; import type { MapsStartApi } from '@kbn/maps-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; -import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; export interface DependencyCache { timefilter: DataPublicPluginSetup['query']['timefilter'] | null; @@ -49,7 +48,6 @@ export interface DependencyCache { dashboard: DashboardStart | null; maps: MapsStartApi | null; dataVisualizer: DataVisualizerPluginStart | null; - aiops: AiopsPluginStart | null; dataViews: DataViewsContract | null; } @@ -73,7 +71,6 @@ const cache: DependencyCache = { dashboard: null, maps: null, dataVisualizer: null, - aiops: null, dataViews: null, }; @@ -96,7 +93,6 @@ export function setDependencyCache(deps: Partial) { cache.i18n = deps.i18n || null; cache.dashboard = deps.dashboard || null; cache.dataVisualizer = deps.dataVisualizer || null; - cache.aiops = deps.aiops || null; cache.dataViews = deps.dataViews || null; } diff --git a/x-pack/plugins/ml/public/maps/util.ts b/x-pack/plugins/ml/public/maps/util.ts index 51acd123398a7..9685d378556a1 100644 --- a/x-pack/plugins/ml/public/maps/util.ts +++ b/x-pack/plugins/ml/public/maps/util.ts @@ -9,7 +9,10 @@ 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, LayerDescriptor } from '@kbn/maps-plugin/common'; -import { ESSearchSourceDescriptor } from '@kbn/maps-plugin/common/descriptor_types'; +import { + ESSearchSourceDescriptor, + VectorStyleDescriptor, +} from '@kbn/maps-plugin/common/descriptor_types'; import type { SerializableRecord } from '@kbn/utility-types'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; import { ESSearchResponse } from '@kbn/core/types/elasticsearch'; @@ -20,6 +23,7 @@ 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 { tabColor } from '../../common/util/group_color_utils'; import { getIndexPattern } from '../application/explorer/reducers/explorer_reducer/get_index_pattern'; import { AnomalySource } from './anomaly_source'; import { SourceIndexGeoFields } from '../application/explorer/explorer_utils'; @@ -119,9 +123,28 @@ export function getInitialSourceIndexFieldLayers(sourceIndexWithGeoFields: Sourc const { dataViewId, geoFields } = sourceIndexWithGeoFields[index]; geoFields.forEach((geoField) => { + const color = tabColor(geoField); + initialLayers.push({ id: htmlIdGenerator()(), - type: LAYER_TYPE.MVT_VECTOR, + type: LAYER_TYPE.GEOJSON_VECTOR, + style: { + type: 'VECTOR', + properties: { + fillColor: { + type: 'STATIC', + options: { + color, + }, + }, + lineColor: { + type: 'STATIC', + options: { + color, + }, + }, + }, + } as unknown as VectorStyleDescriptor, sourceDescriptor: { id: htmlIdGenerator()(), type: SOURCE_TYPES.ES_SEARCH, diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 75d4a8d0fe54b..9d084708e6529 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -38,7 +38,6 @@ import { TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; -import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/public'; @@ -62,7 +61,6 @@ export interface MlStartDependencies { maps?: MapsStartApi; triggersActionsUi?: TriggersAndActionsUIPublicPluginStart; dataVisualizer: DataVisualizerPluginStart; - aiops: AiopsPluginStart; fieldFormats: FieldFormatsStart; dashboard: DashboardStart; charts: ChartsPluginStart; @@ -132,7 +130,6 @@ export class MlPlugin implements Plugin { kibanaVersion, triggersActionsUi: pluginsStart.triggersActionsUi, dataVisualizer: pluginsStart.dataVisualizer, - aiops: pluginsStart.aiops, usageCollection: pluginsSetup.usageCollection, fieldFormats: pluginsStart.fieldFormats, lens: pluginsStart.lens, diff --git a/x-pack/plugins/monitoring/server/lib/standalone_clusters/standalone_cluster_query_filter.ts b/x-pack/plugins/monitoring/server/lib/standalone_clusters/standalone_cluster_query_filter.ts index b8712704f11f9..12d140b97e27e 100644 --- a/x-pack/plugins/monitoring/server/lib/standalone_clusters/standalone_cluster_query_filter.ts +++ b/x-pack/plugins/monitoring/server/lib/standalone_clusters/standalone_cluster_query_filter.ts @@ -23,6 +23,11 @@ export const standaloneClusterFilter = { field: 'cluster_uuid', }, }, + { + exists: { + field: 'error', + }, + }, ], }, }, diff --git a/x-pack/plugins/observability/public/pages/rule_details/index.tsx b/x-pack/plugins/observability/public/pages/rule_details/index.tsx index 88602fd3ed255..27bbc1fc0f4a0 100644 --- a/x-pack/plugins/observability/public/pages/rule_details/index.tsx +++ b/x-pack/plugins/observability/public/pages/rule_details/index.tsx @@ -185,7 +185,7 @@ export function RuleDetailsPage() { }), 'data-test-subj': 'eventLogListTab', content: getRuleEventLogList<'default'>({ - rule, + ruleId: rule?.id, ruleType, } as RuleEventLogListProps), }, diff --git a/x-pack/plugins/observability/server/assets/constants.ts b/x-pack/plugins/observability/server/assets/constants.ts index 09d22022caffd..8afa22d5f695e 100644 --- a/x-pack/plugins/observability/server/assets/constants.ts +++ b/x-pack/plugins/observability/server/assets/constants.ts @@ -7,6 +7,9 @@ export const SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME = 'observability-slo-mappings'; export const SLO_COMPONENT_TEMPLATE_SETTINGS_NAME = 'observability-slo-settings'; -export const SLO_INDEX_TEMPLATE_NAME = 'observability-slo-data'; +export const SLO_INDEX_TEMPLATE_NAME = 'slo-observability.sli'; export const SLO_INGEST_PIPELINE_NAME = 'observability-slo-monthly-index'; export const SLO_RESOURCES_VERSION = 1; + +export const getSLODestinationIndexName = (spaceId: string) => + `${SLO_INDEX_TEMPLATE_NAME}-v${SLO_RESOURCES_VERSION}-${spaceId}`; diff --git a/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts b/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts new file mode 100644 index 0000000000000..6b313bdb76c5a --- /dev/null +++ b/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.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 { + TransformDestination, + TransformPivot, + TransformPutTransformRequest, + TransformSource, +} from '@elastic/elasticsearch/lib/api/types'; + +export const getSLOTransformTemplate = ( + transformId: string, + source: TransformSource, + destination: TransformDestination, + groupBy: TransformPivot['group_by'] = {}, + aggregations: TransformPivot['aggregations'] = {} +): TransformPutTransformRequest => ({ + transform_id: transformId, + source, + frequency: '1m', + dest: destination, + settings: { + deduce_mappings: false, + }, + sync: { + time: { + field: '@timestamp', + delay: '60s', + }, + }, + pivot: { + group_by: groupBy, + aggregations, + }, + _meta: { + version: 1, + }, +}); diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 5b47bbead8300..4a1f91719bca6 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -145,7 +145,6 @@ export class ObservabilityPlugin implements Plugin { const start = () => core.getStartServices().then(([coreStart]) => coreStart); const { spacesService } = plugins.spaces; - const { ruleDataService } = plugins.ruleRegistry; registerRoutes({ diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index e868bc99a5417..c5b2e7d1030e6 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -5,6 +5,17 @@ * 2.0. */ +import uuid from 'uuid'; +import { + KibanaSavedObjectsSLORepository, + ResourceInstaller, + TransformInstaller, +} from '../../services/slo'; +import { + ApmTransactionDurationTransformGenerator, + ApmTransactionErrorRateTransformGenerator, +} from '../../services/slo/transform_generators'; +import { SLO } from '../../types/models'; import { createSLOParamsSchema } from '../../types/schema'; import { createObservabilityServerRoute } from '../create_observability_server_route'; @@ -14,8 +25,36 @@ const createSLORoute = createObservabilityServerRoute({ tags: [], }, params: createSLOParamsSchema, - handler: async ({ context, request, params }) => { - return { success: true }; + handler: async ({ context, request, params, logger, spacesService }) => { + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + const soClient = (await context.core).savedObjects.client; + const spaceId = spacesService.getSpaceId(request); + + const resourceInstaller = new ResourceInstaller(esClient, logger); + const repository = new KibanaSavedObjectsSLORepository(soClient); + const transformInstaller = new TransformInstaller( + { + 'slo.apm.transaction_duration': new ApmTransactionDurationTransformGenerator(), + 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + }, + esClient, + logger + ); + + await resourceInstaller.ensureCommonResourcesInstalled(spaceId); + + const slo: SLO = { + ...params.body, + id: uuid.v1(), + settings: { + destination_index: params.body.settings?.destination_index, + }, + }; + + await repository.save(slo); + await transformInstaller.installAndStartTransform(slo, spaceId); + + return slo; }, }); diff --git a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts new file mode 100644 index 0000000000000..c6bdb2c5a1e77 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/fixtures/slo.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 uuid from 'uuid'; +import { SLI, SLO } from '../../../types/models'; + +export const createSLO = (indicator: SLI): SLO => ({ + id: uuid.v1(), + name: 'irrelevant', + description: 'irrelevant', + indicator, + time_window: { + duration: '7d', + is_rolling: true, + }, + budgeting_method: 'occurrences', + objective: { + target: 0.999, + }, + settings: { + destination_index: 'some-index', + }, +}); + +export const createAPMTransactionErrorRateIndicator = (params = {}): SLI => ({ + type: 'slo.apm.transaction_error_rate', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_name: 'irrelevant', + transaction_type: 'irrelevant', + good_status_codes: ['2xx', '3xx', '4xx'], + ...params, + }, +}); + +export const createAPMTransactionDurationIndicator = (params = {}): SLI => ({ + type: 'slo.apm.transaction_duration', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_name: 'irrelevant', + transaction_type: 'irrelevant', + 'threshold.us': 500000, + ...params, + }, +}); diff --git a/x-pack/plugins/observability/server/services/slo/index.ts b/x-pack/plugins/observability/server/services/slo/index.ts index 39c288bbbf539..d6b7d96fc112b 100644 --- a/x-pack/plugins/observability/server/services/slo/index.ts +++ b/x-pack/plugins/observability/server/services/slo/index.ts @@ -7,3 +7,4 @@ export * from './resource_installer'; export * from './slo_repository'; +export * from './transform_installer'; diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.ts index 92ea496e256df..81b2a0e0eb457 100644 --- a/x-pack/plugins/observability/server/services/slo/resource_installer.ts +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.ts @@ -67,7 +67,9 @@ export class ResourceInstaller { } private getPipelinePrefix(version: number, spaceId: string): string { - return `${SLO_INDEX_TEMPLATE_NAME}-version-${version}-${spaceId}-`; + // Following https://www.elastic.co/blog/an-introduction-to-the-elastic-data-stream-naming-scheme + // slo-observability.sli--. + return `${SLO_INDEX_TEMPLATE_NAME}-v${version}-${spaceId}.`; } private async areResourcesAlreadyInstalled(): Promise { diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts index 8e7b7bbcac427..265cc355860d9 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import uuid from 'uuid'; import { SavedObject } from '@kbn/core-saved-objects-common'; import { SavedObjectsClientContract } from '@kbn/core/server'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; @@ -13,33 +12,18 @@ import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { SLO, StoredSLO } from '../../types/models'; import { SO_SLO_TYPE } from '../../saved_objects'; import { KibanaSavedObjectsSLORepository } from './slo_repository'; +import { createSLO } from './fixtures/slo'; -const anSLO: SLO = { - id: uuid.v1(), - name: 'irrelevant', - description: 'irrelevant', - indicator: { - type: 'slo.apm.transaction_duration', - params: { - environment: 'irrelevant', - service: 'irrelevant', - transaction_type: 'irrelevant', - transaction_name: 'irrelevant', - 'threshold.us': 200000, - }, - }, - time_window: { - duration: '7d', - is_rolling: true, - }, - budgeting_method: 'occurrences', - objective: { - target: 0.999, +const anSLO = createSLO({ + type: 'slo.apm.transaction_duration', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_type: 'irrelevant', + transaction_name: 'irrelevant', + 'threshold.us': 200000, }, - settings: { - destination_index: 'some-index', - }, -}; +}); function aStoredSLO(slo: SLO): SavedObject { return { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap new file mode 100644 index 0000000000000..ade6f8b90d894 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap @@ -0,0 +1,139 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM Transaction Duration Transform Generator does not include the query filter when params are 'ALL' 1`] = ` +Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + ], + }, +} +`; + +exports[`APM Transaction Duration Transform Generator returns the correct transform params with every specified indicator params 1`] = ` +Object { + "_meta": Object { + "version": 1, + }, + "dest": Object { + "index": "some-index", + }, + "frequency": "1m", + "pivot": Object { + "aggregations": Object { + "_numerator": Object { + "range": Object { + "field": "transaction.duration.histogram", + "ranges": Array [ + Object { + "to": 500000, + }, + ], + }, + }, + "slo.denominator": Object { + "value_count": Object { + "field": "transaction.duration.histogram", + }, + }, + "slo.numerator": Object { + "bucket_script": Object { + "buckets_path": Object { + "numerator": "_numerator['*-500000.0']>_count", + }, + "script": "params.numerator", + }, + }, + }, + "group_by": Object { + "@timestamp": Object { + "date_histogram": Object { + "calendar_interval": "1m", + "field": "@timestamp", + }, + }, + "slo.context.service.environment": Object { + "terms": Object { + "field": "service.environment", + }, + }, + "slo.context.service.name": Object { + "terms": Object { + "field": "service.name", + }, + }, + "slo.context.transaction.name": Object { + "terms": Object { + "field": "transaction.name", + }, + }, + "slo.context.transaction.type": Object { + "terms": Object { + "field": "transaction.type", + }, + }, + "slo.id": Object { + "terms": Object { + "field": "slo.id", + }, + }, + }, + }, + "settings": Object { + "deduce_mappings": false, + }, + "source": Object { + "index": "metrics-apm*", + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + Object { + "match": Object { + "service.name": "irrelevant", + }, + }, + Object { + "match": Object { + "service.environment": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.name": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.type": "irrelevant", + }, + }, + ], + }, + }, + "runtime_mappings": Object { + "slo.id": Object { + "script": Object { + "source": Any, + }, + "type": "keyword", + }, + }, + }, + "sync": Object { + "time": Object { + "delay": "60s", + "field": "@timestamp", + }, + }, + "transform_id": Any, +} +`; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap new file mode 100644 index 0000000000000..d07a06e0724cf --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap @@ -0,0 +1,177 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM Transaction Error Rate Transform Generator does not include the query filter when params are 'ALL' 1`] = ` +Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + ], + }, +} +`; + +exports[`APM Transaction Error Rate Transform Generator returns the correct transform params with every specified indicator params 1`] = ` +Object { + "_meta": Object { + "version": 1, + }, + "dest": Object { + "index": "some-index", + }, + "frequency": "1m", + "pivot": Object { + "aggregations": Object { + "slo.denominator": Object { + "value_count": Object { + "field": "transaction.duration.histogram", + }, + }, + "slo.numerator": Object { + "filter": Object { + "bool": Object { + "should": Array [ + Object { + "match": Object { + "transaction.result": "HTTP 2xx", + }, + }, + Object { + "match": Object { + "transaction.result": "HTTP 3xx", + }, + }, + Object { + "match": Object { + "transaction.result": "HTTP 4xx", + }, + }, + ], + }, + }, + }, + }, + "group_by": Object { + "@timestamp": Object { + "date_histogram": Object { + "calendar_interval": "1m", + "field": "@timestamp", + }, + }, + "slo.context.service.environment": Object { + "terms": Object { + "field": "service.environment", + }, + }, + "slo.context.service.name": Object { + "terms": Object { + "field": "service.name", + }, + }, + "slo.context.transaction.name": Object { + "terms": Object { + "field": "transaction.name", + }, + }, + "slo.context.transaction.type": Object { + "terms": Object { + "field": "transaction.type", + }, + }, + "slo.id": Object { + "terms": Object { + "field": "slo.id", + }, + }, + }, + }, + "settings": Object { + "deduce_mappings": false, + }, + "source": Object { + "index": "metrics-apm*", + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + Object { + "match": Object { + "service.name": "irrelevant", + }, + }, + Object { + "match": Object { + "service.environment": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.name": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.type": "irrelevant", + }, + }, + ], + }, + }, + "runtime_mappings": Object { + "slo.id": Object { + "script": Object { + "source": Any, + }, + "type": "keyword", + }, + }, + }, + "sync": Object { + "time": Object { + "delay": "60s", + "field": "@timestamp", + }, + }, + "transform_id": Any, +} +`; + +exports[`APM Transaction Error Rate Transform Generator uses default values when 'good_status_codes' is not specified 1`] = ` +Object { + "slo.denominator": Object { + "value_count": Object { + "field": "transaction.duration.histogram", + }, + }, + "slo.numerator": Object { + "filter": Object { + "bool": Object { + "should": Array [ + Object { + "match": Object { + "transaction.result": "HTTP 2xx", + }, + }, + Object { + "match": Object { + "transaction.result": "HTTP 3xx", + }, + }, + Object { + "match": Object { + "transaction.result": "HTTP 4xx", + }, + }, + ], + }, + }, + }, +} +`; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts new file mode 100644 index 0000000000000..1671e11d4cf2a --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.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 { createAPMTransactionDurationIndicator, createSLO } from '../fixtures/slo'; +import { ApmTransactionDurationTransformGenerator } from './apm_transaction_duration'; + +const generator = new ApmTransactionDurationTransformGenerator(); + +describe('APM Transaction Duration Transform Generator', () => { + it('returns the correct transform params with every specified indicator params', async () => { + const anSLO = createSLO(createAPMTransactionDurationIndicator()); + const transform = generator.getTransformParams(anSLO, 'my-namespace'); + + expect(transform).toMatchSnapshot({ + transform_id: expect.any(String), + source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, + }); + expect(transform.transform_id).toEqual(`slo-${anSLO.id}`); + expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ + script: { source: `emit('${anSLO.id}')` }, + }); + }); + + it("does not include the query filter when params are 'ALL'", async () => { + const anSLO = createSLO( + createAPMTransactionDurationIndicator({ + environment: 'ALL', + service: 'ALL', + transaction_name: 'ALL', + transaction_type: 'ALL', + }) + ); + const transform = generator.getTransformParams(anSLO, 'my-namespace'); + + expect(transform.source.query).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts new file mode 100644 index 0000000000000..c00ba8f69d805 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts @@ -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 { + AggregationsCalendarInterval, + MappingRuntimeFieldType, + TransformPutTransformRequest, +} from '@elastic/elasticsearch/lib/api/types'; +import { getSLODestinationIndexName, SLO_INGEST_PIPELINE_NAME } from '../../../assets/constants'; +import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; +import { + SLO, + apmTransactionDurationSLOSchema, + APMTransactionDurationSLO, +} from '../../../types/models'; +import { ALL_VALUE } from '../../../types/schema'; +import { TransformGenerator } from '.'; + +const APM_SOURCE_INDEX = 'metrics-apm*'; + +export class ApmTransactionDurationTransformGenerator implements TransformGenerator { + public getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest { + if (!apmTransactionDurationSLOSchema.is(slo)) { + throw new Error(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); + } + + return getSLOTransformTemplate( + this.buildTransformId(slo), + this.buildSource(slo), + this.buildDestination(slo, spaceId), + this.buildGroupBy(), + this.buildAggregations(slo) + ); + } + + private buildTransformId(slo: APMTransactionDurationSLO): string { + return `slo-${slo.id}`; + } + + private buildSource(slo: APMTransactionDurationSLO) { + const queryFilter = []; + if (slo.indicator.params.service !== ALL_VALUE) { + queryFilter.push({ + match: { + 'service.name': slo.indicator.params.service, + }, + }); + } + + if (slo.indicator.params.environment !== ALL_VALUE) { + queryFilter.push({ + match: { + 'service.environment': slo.indicator.params.environment, + }, + }); + } + + if (slo.indicator.params.transaction_name !== ALL_VALUE) { + queryFilter.push({ + match: { + 'transaction.name': slo.indicator.params.transaction_name, + }, + }); + } + + if (slo.indicator.params.transaction_type !== ALL_VALUE) { + queryFilter.push({ + match: { + 'transaction.type': slo.indicator.params.transaction_type, + }, + }); + } + + return { + index: APM_SOURCE_INDEX, + runtime_mappings: { + 'slo.id': { + type: 'keyword' as MappingRuntimeFieldType, + script: { + source: `emit('${slo.id}')`, + }, + }, + }, + query: { + bool: { + filter: [ + { + match: { + 'transaction.root': true, + }, + }, + ...queryFilter, + ], + }, + }, + }; + } + + private buildDestination(slo: APMTransactionDurationSLO, spaceId: string) { + if (slo.settings.destination_index === undefined) { + return { + pipeline: SLO_INGEST_PIPELINE_NAME, + index: getSLODestinationIndexName(spaceId), + }; + } + + return { index: slo.settings.destination_index }; + } + + private buildGroupBy() { + return { + 'slo.id': { + terms: { + field: 'slo.id', + }, + }, + '@timestamp': { + date_histogram: { + field: '@timestamp', + calendar_interval: '1m' as AggregationsCalendarInterval, + }, + }, + 'slo.context.transaction.name': { + terms: { + field: 'transaction.name', + }, + }, + 'slo.context.transaction.type': { + terms: { + field: 'transaction.type', + }, + }, + 'slo.context.service.name': { + terms: { + field: 'service.name', + }, + }, + 'slo.context.service.environment': { + terms: { + field: 'service.environment', + }, + }, + }; + } + + private buildAggregations(slo: APMTransactionDurationSLO) { + const truncatedThreshold = Math.trunc(slo.indicator.params['threshold.us']); + + return { + _numerator: { + range: { + field: 'transaction.duration.histogram', + ranges: [ + { + to: truncatedThreshold, + }, + ], + }, + }, + 'slo.numerator': { + bucket_script: { + buckets_path: { + numerator: `_numerator['*-${truncatedThreshold}.0']>_count`, + }, + script: 'params.numerator', + }, + }, + 'slo.denominator': { + value_count: { + field: 'transaction.duration.histogram', + }, + }, + }; + } +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts new file mode 100644 index 0000000000000..0e9fb14f85468 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.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 { createAPMTransactionErrorRateIndicator, createSLO } from '../fixtures/slo'; +import { ApmTransactionErrorRateTransformGenerator } from './apm_transaction_error_rate'; + +const generator = new ApmTransactionErrorRateTransformGenerator(); + +describe('APM Transaction Error Rate Transform Generator', () => { + it('returns the correct transform params with every specified indicator params', async () => { + const anSLO = createSLO(createAPMTransactionErrorRateIndicator()); + const transform = generator.getTransformParams(anSLO, 'my-namespace'); + + expect(transform).toMatchSnapshot({ + transform_id: expect.any(String), + source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, + }); + expect(transform.transform_id).toEqual(`slo-${anSLO.id}`); + expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ + script: { source: `emit('${anSLO.id}')` }, + }); + }); + + it("uses default values when 'good_status_codes' is not specified", async () => { + const anSLO = createSLO(createAPMTransactionErrorRateIndicator({ good_status_codes: [] })); + const transform = generator.getTransformParams(anSLO, 'my-namespace'); + + expect(transform.pivot?.aggregations).toMatchSnapshot(); + }); + + it("does not include the query filter when params are 'ALL'", async () => { + const anSLO = createSLO( + createAPMTransactionErrorRateIndicator({ + environment: 'ALL', + service: 'ALL', + transaction_name: 'ALL', + transaction_type: 'ALL', + }) + ); + const transform = generator.getTransformParams(anSLO, 'my-namespace'); + + expect(transform.source.query).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts new file mode 100644 index 0000000000000..c66de8913b6ef --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + AggregationsCalendarInterval, + MappingRuntimeFieldType, + TransformPutTransformRequest, +} from '@elastic/elasticsearch/lib/api/types'; +import { getSLODestinationIndexName, SLO_INGEST_PIPELINE_NAME } from '../../../assets/constants'; +import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; +import { + apmTransactionErrorRateSLOSchema, + APMTransactionErrorRateSLO, + SLO, +} from '../../../types/models'; +import { ALL_VALUE } from '../../../types/schema'; +import { TransformGenerator } from '.'; + +const APM_SOURCE_INDEX = 'metrics-apm*'; +const ALLOWED_STATUS_CODES = ['2xx', '3xx', '4xx', '5xx']; +const DEFAULT_GOOD_STATUS_CODES = ['2xx', '3xx', '4xx']; + +export class ApmTransactionErrorRateTransformGenerator implements TransformGenerator { + public getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest { + if (!apmTransactionErrorRateSLOSchema.is(slo)) { + throw new Error(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); + } + + return getSLOTransformTemplate( + this.buildTransformId(slo), + this.buildSource(slo), + this.buildDestination(slo, spaceId), + this.buildGroupBy(), + this.buildAggregations(slo) + ); + } + + private buildTransformId(slo: APMTransactionErrorRateSLO): string { + return `slo-${slo.id}`; + } + + private buildSource(slo: APMTransactionErrorRateSLO) { + const queryFilter = []; + if (slo.indicator.params.service !== ALL_VALUE) { + queryFilter.push({ + match: { + 'service.name': slo.indicator.params.service, + }, + }); + } + + if (slo.indicator.params.environment !== ALL_VALUE) { + queryFilter.push({ + match: { + 'service.environment': slo.indicator.params.environment, + }, + }); + } + + if (slo.indicator.params.transaction_name !== ALL_VALUE) { + queryFilter.push({ + match: { + 'transaction.name': slo.indicator.params.transaction_name, + }, + }); + } + + if (slo.indicator.params.transaction_type !== ALL_VALUE) { + queryFilter.push({ + match: { + 'transaction.type': slo.indicator.params.transaction_type, + }, + }); + } + + return { + index: APM_SOURCE_INDEX, + runtime_mappings: { + 'slo.id': { + type: 'keyword' as MappingRuntimeFieldType, + script: { + source: `emit('${slo.id}')`, + }, + }, + }, + query: { + bool: { + filter: [ + { + match: { + 'transaction.root': true, + }, + }, + ...queryFilter, + ], + }, + }, + }; + } + + private buildDestination(slo: APMTransactionErrorRateSLO, spaceId: string) { + if (slo.settings.destination_index === undefined) { + return { + pipeline: SLO_INGEST_PIPELINE_NAME, + index: getSLODestinationIndexName(spaceId), + }; + } + + return { index: slo.settings.destination_index }; + } + + private buildGroupBy() { + return { + 'slo.id': { + terms: { + field: 'slo.id', + }, + }, + '@timestamp': { + date_histogram: { + field: '@timestamp', + calendar_interval: '1m' as AggregationsCalendarInterval, + }, + }, + 'slo.context.transaction.name': { + terms: { + field: 'transaction.name', + }, + }, + 'slo.context.transaction.type': { + terms: { + field: 'transaction.type', + }, + }, + 'slo.context.service.name': { + terms: { + field: 'service.name', + }, + }, + 'slo.context.service.environment': { + terms: { + field: 'service.environment', + }, + }, + }; + } + + private buildAggregations(slo: APMTransactionErrorRateSLO) { + const goodStatusCodesFilter = this.getGoodStatusCodesFilter( + slo.indicator.params.good_status_codes + ); + + return { + 'slo.numerator': { + filter: { + bool: { + should: goodStatusCodesFilter, + }, + }, + }, + 'slo.denominator': { + value_count: { + field: 'transaction.duration.histogram', + }, + }, + }; + } + + private getGoodStatusCodesFilter(goodStatusCodes: string[] | undefined) { + let statusCodes = goodStatusCodes?.filter((code) => ALLOWED_STATUS_CODES.includes(code)); + if (statusCodes === undefined || statusCodes.length === 0) { + statusCodes = DEFAULT_GOOD_STATUS_CODES; + } + + return statusCodes.map((code) => ({ + match: { + 'transaction.result': `HTTP ${code}`, + }, + })); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts new file mode 100644 index 0000000000000..6f0484c2044ad --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './transform_generator'; +export * from './apm_transaction_error_rate'; +export * from './apm_transaction_duration'; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts new file mode 100644 index 0000000000000..21a917ea1af6d --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SLO } from '../../../types/models'; + +export interface TransformGenerator { + getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest; +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_installer.test.ts b/x-pack/plugins/observability/server/services/slo/transform_installer.test.ts new file mode 100644 index 0000000000000..cc65aac74c32e --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_installer.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* eslint-disable max-classes-per-file */ + +import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { ElasticsearchClient } from '@kbn/core/server'; +import { MockedLogger } from '@kbn/logging-mocks'; +import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; + +import { TransformInstaller } from './transform_installer'; +import { + ApmTransactionErrorRateTransformGenerator, + TransformGenerator, +} from './transform_generators'; +import { SLO, SLITypes } from '../../types/models'; +import { createAPMTransactionErrorRateIndicator, createSLO } from './fixtures/slo'; + +describe('TransformerGenerator', () => { + let esClientMock: jest.Mocked; + let loggerMock: jest.Mocked; + + beforeEach(() => { + esClientMock = elasticsearchServiceMock.createElasticsearchClient(); + loggerMock = loggingSystemMock.createLogger(); + }); + + describe('Unhappy path', () => { + it('throws when no generator exists for the slo indicator type', async () => { + // @ts-ignore defining only a subset of the possible SLI + const generators: Record = { + 'slo.apm.transaction_duration': new DummyTransformGenerator(), + }; + const service = new TransformInstaller(generators, esClientMock, loggerMock); + + expect(() => + service.installAndStartTransform( + createSLO({ + type: 'slo.apm.transaction_error_rate', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_name: 'irrelevant', + transaction_type: 'irrelevant', + }, + }) + ) + ).rejects.toThrowError('Unsupported SLO type: slo.apm.transaction_error_rate'); + }); + + it('throws when transform generator fails', async () => { + // @ts-ignore defining only a subset of the possible SLI + const generators: Record = { + 'slo.apm.transaction_duration': new FailTransformGenerator(), + }; + const service = new TransformInstaller(generators, esClientMock, loggerMock); + + expect(() => + service.installAndStartTransform( + createSLO({ + type: 'slo.apm.transaction_duration', + params: { + environment: 'irrelevant', + service: 'irrelevant', + transaction_name: 'irrelevant', + transaction_type: 'irrelevant', + 'threshold.us': 250000, + }, + }) + ) + ).rejects.toThrowError('Some error'); + }); + }); + + it('installs and starts the transform', async () => { + // @ts-ignore defining only a subset of the possible SLI + const generators: Record = { + 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + }; + const service = new TransformInstaller(generators, esClientMock, loggerMock); + + await service.installAndStartTransform(createSLO(createAPMTransactionErrorRateIndicator())); + + expect(esClientMock.transform.putTransform).toHaveBeenCalledTimes(1); + expect(esClientMock.transform.startTransform).toHaveBeenCalledTimes(1); + }); +}); + +class DummyTransformGenerator implements TransformGenerator { + getTransformParams(slo: SLO): TransformPutTransformRequest { + return {} as TransformPutTransformRequest; + } +} + +class FailTransformGenerator implements TransformGenerator { + getTransformParams(slo: SLO): TransformPutTransformRequest { + throw new Error('Some error'); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_installer.ts b/x-pack/plugins/observability/server/services/slo/transform_installer.ts new file mode 100644 index 0000000000000..cd677e10491ca --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_installer.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 { errors } from '@elastic/elasticsearch'; + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { SLO, SLITypes } from '../../types/models'; +import { TransformGenerator } from './transform_generators'; + +export class TransformInstaller { + constructor( + private generators: Record, + private esClient: ElasticsearchClient, + private logger: Logger + ) {} + + async installAndStartTransform(slo: SLO, spaceId: string = 'default'): Promise { + const generator = this.generators[slo.indicator.type]; + if (!generator) { + this.logger.error(`No transform generator found for ${slo.indicator.type} SLO type`); + throw new Error(`Unsupported SLO type: ${slo.indicator.type}`); + } + + const transformParams = generator.getTransformParams(slo, spaceId); + try { + await this.esClient.transform.putTransform(transformParams); + } catch (err) { + // swallow the error if the transform already exists. + const isAlreadyExistError = + err instanceof errors.ResponseError && + err?.body?.error?.type === 'resource_already_exists_exception'; + if (!isAlreadyExistError) { + this.logger.error(`Cannot create transform for ${slo.indicator.type} SLO type: ${err}`); + throw err; + } + } + + try { + await this.esClient.transform.startTransform( + { transform_id: transformParams.transform_id }, + { ignore: [409] } + ); + } catch (err) { + this.logger.error(`Cannot start transform id ${transformParams.transform_id}: ${err}`); + throw err; + } + } +} diff --git a/x-pack/plugins/observability/server/types/models/slo.ts b/x-pack/plugins/observability/server/types/models/slo.ts index 94017b50eb65a..0cbb60531cc36 100644 --- a/x-pack/plugins/observability/server/types/models/slo.ts +++ b/x-pack/plugins/observability/server/types/models/slo.ts @@ -7,14 +7,20 @@ import * as t from 'io-ts'; -import { indicatorSchema, rollingTimeWindowSchema } from '../schema'; +import { + apmTransactionDurationIndicatorSchema, + apmTransactionErrorRateIndicatorSchema, + indicatorSchema, + indicatorTypesSchema, + rollingTimeWindowSchema, +} from '../schema'; const baseSLOSchema = t.type({ id: t.string, name: t.string, description: t.string, - indicator: indicatorSchema, time_window: rollingTimeWindowSchema, + indicator: indicatorSchema, budgeting_method: t.literal('occurrences'), objective: t.type({ target: t.number, @@ -24,10 +30,26 @@ const baseSLOSchema = t.type({ }), }); +export const apmTransactionErrorRateSLOSchema = t.intersection([ + baseSLOSchema, + t.type({ indicator: apmTransactionErrorRateIndicatorSchema }), +]); + +export const apmTransactionDurationSLOSchema = t.intersection([ + baseSLOSchema, + t.type({ indicator: apmTransactionDurationIndicatorSchema }), +]); + const storedSLOSchema = t.intersection([ baseSLOSchema, t.type({ created_at: t.string, updated_at: t.string }), ]); export type SLO = t.TypeOf; +export type APMTransactionErrorRateSLO = t.TypeOf; +export type APMTransactionDurationSLO = t.TypeOf; + +export type SLI = t.TypeOf; +export type SLITypes = t.TypeOf; + export type StoredSLO = t.TypeOf; diff --git a/x-pack/plugins/observability/server/types/schema/slo.ts b/x-pack/plugins/observability/server/types/schema/slo.ts index 62495ff26d4f4..2896e443e2c37 100644 --- a/x-pack/plugins/observability/server/types/schema/slo.ts +++ b/x-pack/plugins/observability/server/types/schema/slo.ts @@ -7,10 +7,12 @@ import * as t from 'io-ts'; -const allOrAnyString = t.union([t.literal('ALL'), t.string]); +export const ALL_VALUE = 'ALL'; +const allOrAnyString = t.union([t.literal(ALL_VALUE), t.string]); -const apmTransactionDurationIndicatorSchema = t.type({ - type: t.literal('slo.apm.transaction_duration'), +const apmTransactionDurationIndicatorTypeSchema = t.literal('slo.apm.transaction_duration'); +export const apmTransactionDurationIndicatorSchema = t.type({ + type: apmTransactionDurationIndicatorTypeSchema, params: t.type({ environment: allOrAnyString, service: allOrAnyString, @@ -20,8 +22,9 @@ const apmTransactionDurationIndicatorSchema = t.type({ }), }); -const apmTransactionErrorRateIndicatorSchema = t.type({ - type: t.literal('slo.apm.transaction_error_rate'), +const apmTransactionErrorRateIndicatorTypeSchema = t.literal('slo.apm.transaction_error_rate'); +export const apmTransactionErrorRateIndicatorSchema = t.type({ + type: apmTransactionErrorRateIndicatorTypeSchema, params: t.intersection([ t.type({ environment: allOrAnyString, @@ -42,6 +45,11 @@ export const rollingTimeWindowSchema = t.type({ is_rolling: t.literal(true), }); +export const indicatorTypesSchema = t.union([ + apmTransactionDurationIndicatorTypeSchema, + apmTransactionErrorRateIndicatorTypeSchema, +]); + export const indicatorSchema = t.union([ apmTransactionDurationIndicatorSchema, apmTransactionErrorRateIndicatorSchema, 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 3ed54c451f38b..96afe9deb98e2 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import type { EuiAccordionProps } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { EuiButton, @@ -13,16 +12,15 @@ import { EuiSpacer, EuiFlexGroup, EuiFlexItem, - EuiAccordion, EuiCard, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { useForm as useHookForm, FormProvider } from 'react-hook-form'; - import { isEmpty, map, find, pickBy } from 'lodash'; import { i18n } from '@kbn/i18n'; + import type { SavedQuerySOFormData } from '../../saved_queries/form/use_saved_query_form'; import type { EcsMappingFormField, @@ -33,8 +31,6 @@ import { convertECSMappingToObject } from '../../../common/schemas/common/utils' import { useKibana } from '../../common/lib/kibana'; import { ResultTabs } from '../../routes/saved_queries/edit/tabs'; import { SavedQueryFlyout } from '../../saved_queries'; -import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; -import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; import { usePacks } from '../../packs/use_packs'; import { PackQueriesStatusTable } from './pack_queries_status_table'; import { useCreateLiveQuery } from '../use_create_live_query_action'; @@ -99,13 +95,6 @@ const StyledEuiCard = styled(EuiCard)` } `; -const StyledEuiAccordion = styled(EuiAccordion)` - ${({ isDisabled }: { isDisabled?: boolean }) => isDisabled && 'display: none;'} - .euiAccordion__button { - color: ${({ theme }) => theme.eui.euiColorPrimary}; - } -`; - type FormType = 'simple' | 'steps'; interface LiveQueryFormProps { @@ -123,7 +112,6 @@ const LiveQueryFormComponent: React.FC = ({ defaultValue, onSuccess, queryField = true, - ecsMappingField = true, formType = 'steps', enabled = true, hideAgentsField = false, @@ -161,8 +149,6 @@ const LiveQueryFormComponent: React.FC = ({ [permissions] ); - const [advancedContentState, setAdvancedContentState] = - useState('closed'); const [showSavedQueryFlyout, setShowSavedQueryFlyout] = useState(false); const [queryType, setQueryType] = useState('query'); const [isLive, setIsLive] = useState(false); @@ -208,43 +194,14 @@ const LiveQueryFormComponent: React.FC = ({ [queryStatus] ); - const handleSavedQueryChange = useCallback( - (savedQuery) => { - if (savedQuery) { - setValue('query', savedQuery.query); - setValue('savedQueryId', savedQuery.savedQueryId); - setValue( - 'ecs_mapping', - !isEmpty(savedQuery.ecs_mapping) - ? map(savedQuery.ecs_mapping, (value, key) => ({ - key, - result: { - type: Object.keys(value)[0], - value: Object.values(value)[0] as string, - }, - })) - : [defaultEcsFormData] - ); - - if (!isEmpty(savedQuery.ecs_mapping)) { - setAdvancedContentState('open'); - } - } else { - setValue('savedQueryId', null); - } - }, - [setValue] - ); - const onSubmit = useCallback( - // not sure why, but submitOnCmdEnter doesn't have proper form values so I am passing them in manually - async (values: LiveQueryFormFields = watchedValues) => { + async (values: LiveQueryFormFields) => { const serializedData = pickBy( { agentSelection: values.agentSelection, saved_query_id: values.savedQueryId, query: values.query, - pack_id: packId?.length ? packId[0] : undefined, + pack_id: values?.packId?.length ? values?.packId[0] : undefined, ...(values.ecs_mapping ? { ecs_mapping: convertECSMappingToObject(values.ecs_mapping) } : {}), @@ -259,25 +216,7 @@ const LiveQueryFormComponent: React.FC = ({ } catch (e) {} } }, - [errors, mutateAsync, packId, watchedValues] - ); - const commands = useMemo( - () => [ - { - name: 'submitOnCmdEnter', - bindKey: { win: 'ctrl+enter', mac: 'cmd+enter' }, - // @ts-expect-error update types - explanation in onSubmit() - exec: () => handleSubmit(onSubmit)(watchedValues), - }, - ], - [handleSubmit, onSubmit, watchedValues] - ); - - const queryComponentProps = useMemo( - () => ({ - commands, - }), - [commands] + [errors, mutateAsync] ); const serializedData: SavedQuerySOFormData = useMemo( @@ -285,23 +224,6 @@ const LiveQueryFormComponent: React.FC = ({ [watchedValues] ); - const handleToggle = useCallback((isOpen) => { - const newState = isOpen ? 'open' : 'closed'; - setAdvancedContentState(newState); - }, []); - - const ecsFieldProps = useMemo( - () => ({ - isDisabled: !permissions.writeLiveQueries, - }), - [permissions.writeLiveQueries] - ); - - const isSavedQueryDisabled = useMemo( - () => !permissions.runSavedQueries || !permissions.readSavedQueries, - [permissions.readSavedQueries, permissions.runSavedQueries] - ); - const { data: packsData, isFetched: isPackDataFetched } = usePacks({}); const selectedPackData = useMemo( @@ -309,6 +231,8 @@ const LiveQueryFormComponent: React.FC = ({ [packId, packsData] ); + const handleSubmitForm = useMemo(() => handleSubmit(onSubmit), [handleSubmit, onSubmit]); + const submitButtonContent = useMemo( () => ( @@ -330,7 +254,7 @@ const LiveQueryFormComponent: React.FC = ({ = ({ handleShowSaveQueryFlyout, enabled, isSubmitting, - handleSubmit, - onSubmit, - ] - ); - - const queryFieldStepContent = useMemo( - () => ( - <> - {queryField && ( - <> - {!isSavedQueryDisabled && ( - <> - - - )} - - - )} - {ecsMappingField && ( - <> - - - - - - - )} - - ), - [ - queryField, - isSavedQueryDisabled, - handleSavedQueryChange, - queryComponentProps, - queryType, - ecsMappingField, - advancedContentState, - handleToggle, - ecsFieldProps, + handleSubmitForm, ] ); @@ -589,7 +467,9 @@ const LiveQueryFormComponent: React.FC = ({ ) : ( <> - {queryFieldStepContent} + + + {submitButtonContent} {resultsStepContent} diff --git a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx index e3516f982cc0b..2938251e177be 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx @@ -5,33 +5,45 @@ * 2.0. */ -import { EuiCodeBlock, EuiFormRow } from '@elastic/eui'; -import React from 'react'; +import { isEmpty, map } from 'lodash'; +import type { EuiAccordionProps } from '@elastic/eui'; +import { EuiCodeBlock, EuiFormRow, EuiAccordion, EuiSpacer } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; - -import { useController } from 'react-hook-form'; +import { useController, useFormContext } from 'react-hook-form'; import { i18n } from '@kbn/i18n'; -import type { EuiCodeEditorProps } from '../../shared_imports'; import { OsqueryEditor } from '../../editor'; import { useKibana } from '../../common/lib/kibana'; import { MAX_QUERY_LENGTH } from '../../packs/queries/validations'; +import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; +import type { SavedQueriesDropdownProps } from '../../saved_queries/saved_queries_dropdown'; +import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; + +const StyledEuiAccordion = styled(EuiAccordion)` + ${({ isDisabled }: { isDisabled?: boolean }) => isDisabled && 'display: none;'} + .euiAccordion__button { + color: ${({ theme }) => theme.eui.euiColorPrimary}; + } +`; const StyledEuiCodeBlock = styled(EuiCodeBlock)` min-height: 100px; `; -interface LiveQueryQueryFieldProps { +export interface LiveQueryQueryFieldProps { disabled?: boolean; - commands?: EuiCodeEditorProps['commands']; - queryType: string; + handleSubmitForm?: () => void; } const LiveQueryQueryFieldComponent: React.FC = ({ disabled, - commands, - queryType, + handleSubmitForm, }) => { + const formContext = useFormContext(); + const [advancedContentState, setAdvancedContentState] = + useState('closed'); const permissions = useKibana().services.application.capabilities.osquery; + const queryType = formContext?.watch('queryType', 'query'); const { field: { onChange, value }, @@ -43,7 +55,7 @@ const LiveQueryQueryFieldComponent: React.FC = ({ message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyQueryError', { defaultMessage: 'Query is a required field', }), - value: queryType === 'query', + value: queryType !== 'pack', }, maxLength: { message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', { @@ -56,27 +68,108 @@ const LiveQueryQueryFieldComponent: React.FC = ({ defaultValue: '', }); + const handleSavedQueryChange: SavedQueriesDropdownProps['onChange'] = useCallback( + (savedQuery) => { + if (savedQuery) { + formContext?.setValue('query', savedQuery.query); + formContext?.setValue('savedQueryId', savedQuery.savedQueryId); + if (!isEmpty(savedQuery.ecs_mapping)) { + formContext?.setValue( + 'ecs_mapping', + map(savedQuery.ecs_mapping, (ecsValue, key) => ({ + key, + result: { + type: Object.keys(ecsValue)[0], + value: Object.values(ecsValue)[0] as string, + }, + })) + ); + } else { + formContext?.resetField('ecs_mapping'); + } + + if (!isEmpty(savedQuery.ecs_mapping)) { + setAdvancedContentState('open'); + } + } else { + formContext?.setValue('savedQueryId', null); + } + }, + [formContext] + ); + + const handleToggle = useCallback((isOpen) => { + const newState = isOpen ? 'open' : 'closed'; + setAdvancedContentState(newState); + }, []); + + const ecsFieldProps = useMemo( + () => ({ + isDisabled: !permissions.writeLiveQueries, + }), + [permissions.writeLiveQueries] + ); + + const isSavedQueryDisabled = useMemo( + () => !permissions.runSavedQueries || !permissions.readSavedQueries, + [permissions.readSavedQueries, permissions.runSavedQueries] + ); + + const commands = useMemo( + () => + handleSubmitForm + ? [ + { + name: 'submitOnCmdEnter', + bindKey: { win: 'ctrl+enter', mac: 'cmd+enter' }, + exec: handleSubmitForm, + }, + ] + : [], + [handleSubmitForm] + ); + return ( - - {!permissions.writeLiveQueries || disabled ? ( - - {value} - - ) : ( - + <> + {!isSavedQueryDisabled && ( + )} - + + {!permissions.writeLiveQueries || disabled ? ( + + {value} + + ) : ( + + )} + + + + + + + + + ); }; export const LiveQueryQueryField = React.memo(LiveQueryQueryFieldComponent); + +// eslint-disable-next-line import/no-default-export +export { LiveQueryQueryField as default }; 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 40eb009a71bd1..7a67c6fdeb65b 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 @@ -18,7 +18,7 @@ import { trim, get, } from 'lodash'; -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { EuiComboBoxProps, EuiComboBoxOptionOption } from '@elastic/eui'; import { EuiFormLabel, @@ -625,25 +625,6 @@ export const ECSMappingEditorForm: React.FC = ({ defaultValue: '', }); - const MultiFields = useMemo( - () => ( -
- -
- ), - [item, index, isLastItem, osquerySchemaOptions, isDisabled] - ); - const ecsComboBoxEuiFieldProps = useMemo(() => ({ isDisabled }), [isDisabled]); const handleDeleteClick = useCallback(() => { @@ -676,7 +657,19 @@ export const ECSMappingEditorForm: React.FC = ({
- {MultiFields} + + + {!isDisabled && ( @@ -742,7 +735,7 @@ export const ECSMappingEditorField = React.memo( const fieldsToValidate = prepareEcsFieldsToValidate(fields); // it is always at least 2 - empty fields if (fieldsToValidate.length > 2) { - setTimeout(async () => await trigger('ecs_mapping'), 0); + setTimeout(() => trigger('ecs_mapping'), 0); } }, [fields, query, trigger]); @@ -977,7 +970,7 @@ export const ECSMappingEditorField = React.memo( ); }, [query]); - useLayoutEffect(() => { + useEffect(() => { const ecsList = formData?.ecs_mapping; const lastEcs = formData?.ecs_mapping?.[itemsList?.current.length - 1]; @@ -986,15 +979,16 @@ export const ECSMappingEditorField = React.memo( return; } - // // list contains ecs already, and the last item has values provided + // list contains ecs already, and the last item has values provided if ( - ecsList?.length === itemsList.current.length && - lastEcs?.key?.length && - lastEcs?.result?.value?.length + (ecsList?.length === itemsList.current.length && + lastEcs?.key?.length && + lastEcs?.result?.value?.length) || + !fields?.length ) { return append(defaultEcsFormData); } - }, [append, euiFieldProps?.isDisabled, formData]); + }, [append, fields, formData]); return ( <> diff --git a/x-pack/plugins/osquery/public/plugin.ts b/x-pack/plugins/osquery/public/plugin.ts index 9b8d012e7b084..ddea34a936178 100644 --- a/x-pack/plugins/osquery/public/plugin.ts +++ b/x-pack/plugins/osquery/public/plugin.ts @@ -26,7 +26,11 @@ import { LazyOsqueryManagedPolicyEditExtension, LazyOsqueryManagedCustomButtonExtension, } from './fleet_integration'; -import { getLazyOsqueryAction, useIsOsqueryAvailableSimple } from './shared_components'; +import { + getLazyOsqueryAction, + getLazyLiveQueryField, + useIsOsqueryAvailableSimple, +} from './shared_components'; export class OsqueryPlugin implements Plugin { private kibanaVersion: string; @@ -94,8 +98,10 @@ export class OsqueryPlugin implements Plugin + // eslint-disable-next-line react/display-name + ({ + formMethods, + ...props + }: LiveQueryQueryFieldProps & { + formMethods: UseFormReturn<{ + label: string; + query: string; + ecs_mapping: Record; + }>; + }) => { + const LiveQueryField = lazy(() => import('../live_queries/form/live_query_query_field')); + + return ( + + + + + + + + ); + }; diff --git a/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx b/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx index 5e158c51c02d1..ff464e7782bb7 100644 --- a/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx +++ b/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx @@ -6,15 +6,20 @@ */ import React, { lazy, Suspense } from 'react'; +import ServicesWrapper from './services_wrapper'; +import type { ServicesWrapperProps } from './services_wrapper'; +import type { OsqueryActionProps } from './osquery_action'; -// @ts-expect-error update types -// eslint-disable-next-line react/display-name -export const getLazyOsqueryAction = (services) => (props) => { - const OsqueryAction = lazy(() => import('./osquery_action')); +export const getLazyOsqueryAction = + // eslint-disable-next-line react/display-name + (services: ServicesWrapperProps['services']) => (props: OsqueryActionProps) => { + const OsqueryAction = lazy(() => import('./osquery_action')); - return ( - - - - ); -}; + return ( + + + + + + ); + }; 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 15c6fa645de11..bc039b334a910 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 @@ -5,10 +5,9 @@ * 2.0. */ -import { EuiErrorBoundary, EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; +import { EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { QueryClientProvider } from '@tanstack/react-query'; -import type { CoreStart } from '@kbn/core/public'; + import { AGENT_STATUS_ERROR, EMPTY_PROMPT, @@ -16,17 +15,14 @@ import { PERMISSION_DENIED, SHORT_EMPTY_TITLE, } from './translations'; -import { KibanaContextProvider, useKibana } from '../../common/lib/kibana'; - +import { useKibana } from '../../common/lib/kibana'; import { LiveQuery } from '../../live_queries'; -import { queryClient } from '../../query_client'; import { OsqueryIcon } from '../../components/osquery_icon'; -import { KibanaThemeProvider } from '../../shared_imports'; import { useIsOsqueryAvailable } from './use_is_osquery_available'; -import type { StartPlugins } from '../../types'; -interface OsqueryActionProps { +export interface OsqueryActionProps { agentId?: string; + defaultValues?: {}; formType: 'steps' | 'simple'; hideAgentsField?: boolean; addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; @@ -35,6 +31,7 @@ interface OsqueryActionProps { const OsqueryActionComponent: React.FC = ({ agentId, formType = 'simple', + defaultValues, hideAgentsField, addToTimeline, }) => { @@ -54,7 +51,7 @@ const OsqueryActionComponent: React.FC = ({ const { osqueryAvailable, agentFetched, isLoading, policyFetched, policyLoading, agentData } = useIsOsqueryAvailable(agentId); - if (!agentId || (agentFetched && !agentData)) { + if (agentId && agentFetched && !agentData) { return emptyPrompt; } @@ -77,15 +74,15 @@ const OsqueryActionComponent: React.FC = ({ ); } - if (isLoading) { + if (agentId && isLoading) { return ; } - if (!policyFetched && policyLoading) { + if (agentId && !policyFetched && policyLoading) { return ; } - if (!osqueryAvailable) { + if (agentId && !osqueryAvailable) { return ( } @@ -96,7 +93,7 @@ const OsqueryActionComponent: React.FC = ({ ); } - if (agentData?.status !== 'online') { + if (agentId && agentData?.status !== 'online') { return ( } @@ -113,38 +110,14 @@ const OsqueryActionComponent: React.FC = ({ agentId={agentId} hideAgentsField={hideAgentsField} addToTimeline={addToTimeline} + {...defaultValues} /> ); }; -export const OsqueryAction = React.memo(OsqueryActionComponent); - -type OsqueryActionWrapperProps = { services: CoreStart & StartPlugins } & OsqueryActionProps; +OsqueryActionComponent.displayName = 'OsqueryAction'; -const OsqueryActionWrapperComponent: React.FC = ({ - services, - agentId, - formType, - hideAgentsField = false, - addToTimeline, -}) => ( - - - - - - - - - -); - -const OsqueryActionWrapper = React.memo(OsqueryActionWrapperComponent); +export const OsqueryAction = React.memo(OsqueryActionComponent); // eslint-disable-next-line import/no-default-export -export { OsqueryActionWrapper as default }; +export { OsqueryAction as default }; 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 927d408884d20..ba56cfa0da62d 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 @@ -81,13 +81,6 @@ describe('Osquery Action', () => { const { getByText } = renderWithContext(); expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); }); - it('should return empty prompt when no agentId', async () => { - spyOsquery(); - mockKibana(); - - const { getByText } = renderWithContext(); - expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); - }); it('should return permission denied when agentFetched and agentData available', async () => { spyOsquery({ agentData: {} }); mockKibana(); diff --git a/x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx b/x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx new file mode 100644 index 0000000000000..7b6949696bbee --- /dev/null +++ b/x-pack/plugins/osquery/public/shared_components/services_wrapper.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 { EuiErrorBoundary } from '@elastic/eui'; +import React from 'react'; +import { QueryClientProvider } from '@tanstack/react-query'; +import type { CoreStart } from '@kbn/core/public'; +import { KibanaContextProvider } from '../common/lib/kibana'; + +import { queryClient } from '../query_client'; +import { KibanaThemeProvider } from '../shared_imports'; +import type { StartPlugins } from '../types'; + +export interface ServicesWrapperProps { + services: CoreStart & StartPlugins; + children: React.ReactNode; +} + +const ServicesWrapperComponent: React.FC = ({ services, children }) => ( + + + + {children} + + + +); + +const ServicesWrapper = React.memo(ServicesWrapperComponent); + +// eslint-disable-next-line import/no-default-export +export { ServicesWrapper as default }; diff --git a/x-pack/plugins/osquery/public/types.ts b/x-pack/plugins/osquery/public/types.ts index 69c4befec1b6c..c19dd10802f32 100644 --- a/x-pack/plugins/osquery/public/types.ts +++ b/x-pack/plugins/osquery/public/types.ts @@ -16,12 +16,13 @@ import type { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; -import type { getLazyOsqueryAction } from './shared_components'; +import type { getLazyLiveQueryField, getLazyOsqueryAction } from './shared_components'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface OsqueryPluginSetup {} export interface OsqueryPluginStart { OsqueryAction?: ReturnType; + LiveQueryField?: ReturnType; isOsqueryAvailable: (props: { agentId: string }) => boolean; } diff --git a/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.test.ts b/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.test.ts index d51e0e8bd03b1..0b4a814370177 100644 --- a/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.test.ts +++ b/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.test.ts @@ -59,4 +59,10 @@ describe('getTableColumnDefinition', () => { // we know this returns a function even if the generic column signature allows other types expect((sortable as Function)(savedObject)).toEqual('Tag 1'); }); + + it('returns a non-sortable definition when `serverPaging` is `true`', () => { + const { sortable } = getTableColumnDefinition({ serverPaging: true }); + + expect(sortable).toEqual(false); + }); }); diff --git a/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx b/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx index ffde6e2e5e825..ae9e96625eaa9 100644 --- a/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx @@ -11,11 +11,12 @@ import { SavedObject, SavedObjectReference } from '@kbn/core/public'; import { SavedObjectsTaggingApiUi, SavedObjectsTaggingApiUiComponent, + GetTableColumnDefinitionOptions, } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { ITagsCache } from '../services'; import { getTagsFromReferences, byNameTagSorter } from '../utils'; -export interface GetTableColumnDefinitionOptions { +export interface BuildGetTableColumnDefinitionOptions { components: SavedObjectsTaggingApiUiComponent; cache: ITagsCache; } @@ -23,8 +24,8 @@ export interface GetTableColumnDefinitionOptions { export const buildGetTableColumnDefinition = ({ components, cache, -}: GetTableColumnDefinitionOptions): SavedObjectsTaggingApiUi['getTableColumnDefinition'] => { - return () => { +}: BuildGetTableColumnDefinitionOptions): SavedObjectsTaggingApiUi['getTableColumnDefinition'] => { + return ({ serverPaging = false }: GetTableColumnDefinitionOptions = {}) => { return { field: 'references', name: i18n.translate('xpack.savedObjectsTagging.uiApi.table.columnTagsName', { @@ -33,11 +34,13 @@ export const buildGetTableColumnDefinition = ({ description: i18n.translate('xpack.savedObjectsTagging.uiApi.table.columnTagsDescription', { defaultMessage: 'Tags associated with this saved object', }), - sortable: (object: SavedObject) => { - const { tags } = getTagsFromReferences(object.references, cache.getState()); - tags.sort(byNameTagSorter); - return tags.length ? tags[0].name : undefined; - }, + sortable: serverPaging + ? false + : (object: SavedObject) => { + const { tags } = getTagsFromReferences(object.references, cache.getState()); + tags.sort(byNameTagSorter); + return tags.length ? tags[0].name : undefined; + }, render: (references: SavedObjectReference[], object: SavedObject) => { return ; }, diff --git a/x-pack/plugins/screenshotting/README.md b/x-pack/plugins/screenshotting/README.md index aefa4cc90762b..3a3ea87448e64 100644 --- a/x-pack/plugins/screenshotting/README.md +++ b/x-pack/plugins/screenshotting/README.md @@ -89,7 +89,6 @@ Option | Required | Default | Description `layout` | no | `{}` | Page layout parameters describing characteristics of the capturing screenshot (e.g., dimensions, zoom, etc.). `request` | no | _none_ | Kibana Request reference to extract headers from. `timeouts` | no | _none_ | Timeouts for each phase of the screenshot. -`timeouts.loadDelay` | no | `3000` | The amount of time in milliseconds before taking a screenshot when visualizations are not evented. All visualizations that ship with Kibana are evented, so this setting should not have much effect. If you are seeing empty images instead of visualizations, try increasing this value. `timeouts.openUrl` | no | `60000` | The timeout in milliseconds to allow the Chromium browser to wait for the "Loading…" screen to dismiss and find the initial data for the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. `timeouts.renderComplete` | no | `30000` | The timeout in milliseconds to allow the Chromium browser to wait for all visualizations to fetch and render the data. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. `timeouts.waitForElements` | no | `30000` | The timeout in milliseconds to allow the Chromium browser to wait for all visualization panels to load on the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. diff --git a/x-pack/plugins/screenshotting/server/config/schema.test.ts b/x-pack/plugins/screenshotting/server/config/schema.test.ts index 58fb4b5ab559e..c2febf5906249 100644 --- a/x-pack/plugins/screenshotting/server/config/schema.test.ts +++ b/x-pack/plugins/screenshotting/server/config/schema.test.ts @@ -20,7 +20,6 @@ describe('ConfigSchema', () => { }, }, "capture": Object { - "loadDelay": "PT3S", "timeouts": Object { "openUrl": "PT1M", "renderComplete": "PT30S", @@ -81,7 +80,6 @@ describe('ConfigSchema', () => { }, }, "capture": Object { - "loadDelay": "PT3S", "timeouts": Object { "openUrl": "PT1M", "renderComplete": "PT30S", diff --git a/x-pack/plugins/screenshotting/server/config/schema.ts b/x-pack/plugins/screenshotting/server/config/schema.ts index 1e103a6b6e4d0..4900a5c9d775e 100644 --- a/x-pack/plugins/screenshotting/server/config/schema.ts +++ b/x-pack/plugins/screenshotting/server/config/schema.ts @@ -81,9 +81,7 @@ export const ConfigSchema = schema.object({ }), }), zoom: schema.number({ defaultValue: 2 }), - loadDelay: schema.oneOf([schema.number(), schema.duration()], { - defaultValue: moment.duration({ seconds: 3 }), - }), + loadDelay: schema.maybe(schema.oneOf([schema.number(), schema.duration()])), // deprecated, unused }), poolSize: schema.number({ defaultValue: 1, min: 1 }), }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts index 11ce25e0f86f1..70aca733a03d1 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { Logger, PackageInfo } from '@kbn/core/server'; import { httpServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { lastValueFrom, of, throwError } from 'rxjs'; @@ -14,12 +15,11 @@ import { SCREENSHOTTING_EXPRESSION, SCREENSHOTTING_EXPRESSION_INPUT, } from '../../common'; -import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import * as errors from '../../common/errors'; 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'; @@ -72,7 +72,6 @@ describe('Screenshot Observable Pipeline', () => { waitForElements: 30000, renderComplete: 30000, }, - loadDelay: 5000000000, zoom: 2, }, networkPolicy: { enabled: false, rules: [] }, @@ -125,13 +124,13 @@ describe('Screenshot Observable Pipeline', () => { }); it('captures screenshot of an expression', async () => { - await screenshots - .getScreenshots({ + await lastValueFrom( + screenshots.getScreenshots({ ...options, expression: 'kibana', input: 'something', } as PngScreenshotOptions) - .toPromise(); + ); expect(driver.open).toHaveBeenCalledTimes(1); expect(driver.open).toHaveBeenCalledWith( @@ -148,7 +147,7 @@ describe('Screenshot Observable Pipeline', () => { describe('error handling', () => { it('recovers if waitForSelector fails', async () => { - driver.waitForSelector.mockImplementation((selectorArg: string) => { + driver.waitForSelector.mockImplementation(() => { throw new Error('Mock error!'); }); const result = await lastValueFrom( @@ -169,14 +168,14 @@ describe('Screenshot Observable Pipeline', () => { driverFactory.createPage.mockReturnValue( of({ driver, - error$: throwError('Instant timeout has fired!'), + error$: throwError(() => 'Instant timeout has fired!'), close: () => of({}), }) ); - await expect(screenshots.getScreenshots(options).toPromise()).rejects.toMatchInlineSnapshot( - `"Instant timeout has fired!"` - ); + await expect( + lastValueFrom(screenshots.getScreenshots(options)) + ).rejects.toMatchInlineSnapshot(`"Instant timeout has fired!"`); }); it(`uses defaults for element positions and size when Kibana page is not ready`, async () => { diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts index 57f8440c34817..0c6c6f409f848 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts @@ -203,7 +203,6 @@ export class Screenshots { openUrl: 60000, waitForElements: 30000, renderComplete: 30000, - loadDelay: 3000, }, urls: [], } diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts index cb0fa6720ff7d..363c30ad83f33 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts @@ -6,7 +6,7 @@ */ import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { interval, of, throwError } from 'rxjs'; +import { interval, lastValueFrom, of, throwError } from 'rxjs'; import { map } from 'rxjs/operators'; import { createMockBrowserDriver } from '../browsers/mock'; import type { ConfigType } from '../config'; @@ -26,7 +26,6 @@ describe('ScreenshotObservableHandler', () => { config = { capture: { timeouts: { openUrl: 30000, waitForElements: 30000, renderComplete: 30000 }, - loadDelay: 5000, zoom: 13, }, } as ConfigType; @@ -55,14 +54,14 @@ describe('ScreenshotObservableHandler', () => { }) ); - const testPipeline = () => test$.toPromise(); + const testPipeline = () => lastValueFrom(test$); await expect(testPipeline).rejects.toMatchInlineSnapshot( `[Error: Screenshotting encountered a timeout error: "Test Config" took longer than 0.2 seconds. You may need to increase "xpack.screenshotting.testConfig" in kibana.yml.]` ); }); it('catches other Errors', async () => { - const test$ = throwError(new Error(`Test Error to Throw`)).pipe( + const test$ = throwError(() => new Error(`Test Error to Throw`)).pipe( screenshots.waitUntil({ timeoutValue: 200, label: 'Test Config', @@ -70,7 +69,7 @@ describe('ScreenshotObservableHandler', () => { }) ); - const testPipeline = () => test$.toPromise(); + const testPipeline = () => lastValueFrom(test$); await expect(testPipeline).rejects.toMatchInlineSnapshot( `[Error: The "Test Config" phase encountered an error: Error: Test Error to Throw]` ); @@ -85,7 +84,7 @@ describe('ScreenshotObservableHandler', () => { }) ); - await expect(test$.toPromise()).resolves.toBe(`nice to see you`); + await expect(lastValueFrom(test$)).resolves.toBe(`nice to see you`); }); }); @@ -104,7 +103,7 @@ describe('ScreenshotObservableHandler', () => { }) ); - await expect(test$.toPromise()).rejects.toMatchInlineSnapshot( + await expect(lastValueFrom(test$)).rejects.toMatchInlineSnapshot( `[Error: Browser was closed unexpectedly! Check the server logs for more info.]` ); }); @@ -117,7 +116,7 @@ describe('ScreenshotObservableHandler', () => { }) ); - await expect(test$.toPromise()).resolves.toBe(234455); + await expect(lastValueFrom(test$)).resolves.toBe(234455); }); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.ts index efd0974612c59..f5662ee920bf4 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.ts @@ -124,7 +124,6 @@ const getTimeouts = (captureConfig: ConfigType['capture']) => ({ configValue: `xpack.screenshotting.capture.timeouts.renderComplete`, label: 'render complete', }, - loadDelay: toNumber(captureConfig.loadDelay), }); export class ScreenshotObservableHandler { @@ -132,7 +131,7 @@ export class ScreenshotObservableHandler { constructor( private readonly driver: HeadlessChromiumDriver, - private readonly config: ConfigType, + config: ConfigType, private readonly eventLogger: EventLogger, private readonly layout: Layout, private options: ScreenshotObservableOptions @@ -222,12 +221,7 @@ export class ScreenshotObservableHandler { throw error; } - await waitForRenderComplete( - driver, - eventLogger, - toNumber(this.config.capture.loadDelay), - layout - ); + await waitForRenderComplete(driver, eventLogger, layout); }).pipe( mergeMap(() => forkJoin({ 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 8cf8174be152f..ed4ad83736d42 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts @@ -13,7 +13,6 @@ import { Actions, EventLogger } from './event_logger'; export const waitForRenderComplete = async ( browser: HeadlessChromiumDriver, eventLogger: EventLogger, - loadDelay: number, layout: Layout ) => { const spanEnd = eventLogger.logScreenshottingEvent( @@ -22,54 +21,35 @@ export const waitForRenderComplete = async ( 'wait' ); - return await browser - .evaluate( - { - fn: (selector, visLoadDelay) => { - // wait for visualizations to finish loading - const visualizations: NodeListOf = document.querySelectorAll(selector); - const visCount = visualizations.length; - const renderedTasks = []; - - function waitForRender(visualization: Element) { - return new Promise((resolve) => { - visualization.addEventListener('renderComplete', () => resolve()); - }); - } - - function waitForRenderDelay() { - return new Promise((resolve) => { - setTimeout(resolve, visLoadDelay); - }); + await browser.evaluate( + { + fn: async (selector) => { + const visualizations: NodeListOf = document.querySelectorAll(selector); + const visCount = visualizations.length; + const renderedTasks = []; + + function waitForRender(visualization: Element) { + return new Promise((resolve) => { + visualization.addEventListener('renderComplete', () => resolve()); + }); + } + + for (let i = 0; i < visCount; i++) { + const visualization = visualizations[i]; + const isRendered = visualization.getAttribute('data-render-complete'); + + if (isRendered === 'false') { + renderedTasks.push(waitForRender(visualization)); } + } - for (let i = 0; i < visCount; i++) { - const visualization = visualizations[i]; - const isRendered = visualization.getAttribute('data-render-complete'); - - if (isRendered === 'disabled') { - renderedTasks.push(waitForRenderDelay()); - } else if (isRendered === 'false') { - renderedTasks.push(waitForRender(visualization)); - } - } - - // The renderComplete fires before the visualizations are in the DOM, so - // we wait for the event loop to flush before telling reporting to continue. This - // seems to correct a timing issue that was causing reporting to occasionally - // capture the first visualization before it was actually in the DOM. - // Note: 100 proved too short, see https://github.com/elastic/kibana/issues/22581, - // bumping to 250. - const hackyWaitForVisualizations = () => new Promise((r) => setTimeout(r, 250)); - - return Promise.all(renderedTasks).then(hackyWaitForVisualizations); - }, - args: [layout.selectors.renderComplete, loadDelay], + return await Promise.all(renderedTasks); }, - { context: CONTEXT_WAITFORRENDER }, - eventLogger.kbnLogger - ) - .then(() => { - spanEnd(); - }); + args: [layout.selectors.renderComplete], + }, + { context: CONTEXT_WAITFORRENDER }, + eventLogger.kbnLogger + ); + + spanEnd(); }; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index e86e57e00ca34..622c74efd8281 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -113,7 +113,7 @@ export enum SecurityPageName { noPage = '', overview = 'overview', policies = 'policy', - responseActions = 'response_actions', + actionHistory = 'action_history', rules = 'rules', rulesCreate = 'rules-create', sessions = 'sessions', @@ -159,7 +159,7 @@ export const EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/event_filters` as const; export const HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/host_isolation_exceptions` as const; export const BLOCKLIST_PATH = `${MANAGEMENT_PATH}/blocklist` as const; -export const RESPONSE_ACTIONS_PATH = `${MANAGEMENT_PATH}/response_actions` as const; +export const ACTION_HISTORY_PATH = `${MANAGEMENT_PATH}/action_history` as const; export const ENTITY_ANALYTICS_PATH = '/entity_analytics' as const; export const APP_OVERVIEW_PATH = `${APP_PATH}${OVERVIEW_PATH}` as const; export const APP_LANDING_PATH = `${APP_PATH}${LANDING_PATH}` as const; @@ -183,7 +183,7 @@ export const APP_EVENT_FILTERS_PATH = `${APP_PATH}${EVENT_FILTERS_PATH}` as cons export const APP_HOST_ISOLATION_EXCEPTIONS_PATH = `${APP_PATH}${HOST_ISOLATION_EXCEPTIONS_PATH}` as const; export const APP_BLOCKLIST_PATH = `${APP_PATH}${BLOCKLIST_PATH}` as const; -export const APP_RESPONSE_ACTIONS_PATH = `${APP_PATH}${RESPONSE_ACTIONS_PATH}` as const; +export const APP_ACTION_HISTORY_PATH = `${APP_PATH}${ACTION_HISTORY_PATH}` as const; export const APP_ENTITY_ANALYTICS_PATH = `${APP_PATH}${ENTITY_ANALYTICS_PATH}` as const; // cloud logs to exclude from default index pattern @@ -290,10 +290,13 @@ export const prebuiltSavedObjectsBulkCreateUrl = (templateName: string) => * Internal detection engine routes */ export const INTERNAL_DETECTION_ENGINE_URL = '/internal/detection_engine' as const; +export const INTERNAL_DETECTION_ENGINE_RULES_URL = '/internal/detection_engine/rules' as const; export const DETECTION_ENGINE_INSTALLED_INTEGRATIONS_URL = `${INTERNAL_DETECTION_ENGINE_URL}/fleet/integrations/installed` as const; export const DETECTION_ENGINE_ALERTS_INDEX_URL = `${INTERNAL_DETECTION_ENGINE_URL}/signal/index` as const; +export const DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL = + `${INTERNAL_DETECTION_ENGINE_RULES_URL}/exceptions/_find_references` as const; /** * Telemetry detection endpoint for any previews requested of what data we are * providing through UI/UX and for e2e tests. @@ -455,3 +458,6 @@ export enum BulkActionsDryRunErrCode { MACHINE_LEARNING_AUTH = 'MACHINE_LEARNING_AUTH', MACHINE_LEARNING_INDEX_PATTERN = 'MACHINE_LEARNING_INDEX_PATTERN', } + +export const RISKY_HOSTS_DOC_LINK = + 'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/host-risk-score.md'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.test.ts new file mode 100644 index 0000000000000..425928d8fa208 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils'; +import { findExceptionReferencesOnRuleSchema } from './find_exception_list_references_schema'; +import type { FindExceptionReferencesOnRuleSchema } from './find_exception_list_references_schema'; + +describe('find_exception_list_references_schema', () => { + test('validates all fields', () => { + const payload: FindExceptionReferencesOnRuleSchema = { + ids: 'abc,def', + list_ids: '123,456', + namespace_types: 'single,agnostic', + }; + + const decoded = findExceptionReferencesOnRuleSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + ids: ['abc', 'def'], + list_ids: ['123', '456'], + namespace_types: ['single', 'agnostic'], + }); + }); + + test('"ids" cannot be undefined', () => { + const payload: Omit = { + list_ids: '123,456', + namespace_types: 'single,agnostic', + }; + + const decoded = findExceptionReferencesOnRuleSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual(['Invalid value "undefined" supplied to "ids"']); + expect(output.schema).toEqual({}); + }); + + test('"list_ids" cannot be undefined', () => { + const payload: Omit = { + ids: 'abc', + namespace_types: 'single', + }; + + const decoded = findExceptionReferencesOnRuleSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([ + 'Invalid value "undefined" supplied to "list_ids"', + ]); + expect(output.schema).toEqual({}); + }); + + test('defaults "namespacetypes" to ["single"] if none set', () => { + const payload: Omit = { + ids: 'abc', + list_ids: '123', + }; + + const decoded = findExceptionReferencesOnRuleSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + ids: ['abc'], + list_ids: ['123'], + namespace_types: ['single'], + }); + }); + + test('cannot add extra values', () => { + const payload: FindExceptionReferencesOnRuleSchema & { extra_value?: string } = { + ids: 'abc,def', + list_ids: '123,456', + namespace_types: 'single,agnostic', + extra_value: 'aaa', + }; + + const decoded = findExceptionReferencesOnRuleSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual(['invalid keys "extra_value"']); + expect(output.schema).toEqual({}); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.ts new file mode 100644 index 0000000000000..8cd21df8ab08b --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/find_exception_list_references_schema.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 * as t from 'io-ts'; +import { NonEmptyStringArray } from '@kbn/securitysolution-io-ts-types'; +import { DefaultNamespaceArray } from '@kbn/securitysolution-io-ts-list-types'; + +export const findExceptionReferencesOnRuleSchema = t.exact( + t.type({ + ids: NonEmptyStringArray, + list_ids: NonEmptyStringArray, + namespace_types: DefaultNamespaceArray, + }) +); + +export type FindExceptionReferencesOnRuleSchema = t.OutputOf< + typeof findExceptionReferencesOnRuleSchema +>; + +export type FindExceptionReferencesOnRuleSchemaDecoded = t.TypeOf< + typeof findExceptionReferencesOnRuleSchema +>; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.test.ts new file mode 100644 index 0000000000000..743645ba0f818 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.test.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 { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils'; +import { + ruleReferenceSchema, + rulesReferencedByExceptionListsSchema, +} from './find_exception_list_references_schema'; +import type { + RuleReferenceSchema, + RulesReferencedByExceptionListsSchema, +} from './find_exception_list_references_schema'; + +describe('find_exception_list_references_schema', () => { + describe('ruleReferenceSchema', () => { + test('validates all fields', () => { + const payload: RuleReferenceSchema = { + name: 'My rule', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + rule_id: 'my-rule-id', + exception_lists: [ + { + id: 'myListId', + list_id: 'my-list-id', + namespace_type: 'single', + type: 'detection', + }, + ], + }; + + const decoded = ruleReferenceSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + exception_lists: [ + { id: 'myListId', list_id: 'my-list-id', namespace_type: 'single', type: 'detection' }, + ], + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + name: 'My rule', + rule_id: 'my-rule-id', + }); + }); + + test('cannot add extra values', () => { + const payload: RuleReferenceSchema & { extra_value?: string } = { + name: 'My rule', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + rule_id: 'my-rule-id', + extra_value: 'foo', + exception_lists: [ + { + id: 'myListId', + list_id: 'my-list-id', + namespace_type: 'single', + type: 'detection', + }, + ], + }; + + const decoded = ruleReferenceSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual(['invalid keys "extra_value"']); + expect(output.schema).toEqual({}); + }); + }); + + describe('rulesReferencedByExceptionListsSchema', () => { + test('validates all fields', () => { + const payload: RulesReferencedByExceptionListsSchema = { + references: [ + { + 'my-list-id': [ + { + name: 'My rule', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + rule_id: 'my-rule-id', + exception_lists: [ + { + id: 'myListId', + list_id: 'my-list-id', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }, + ], + }; + + const decoded = rulesReferencedByExceptionListsSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + references: [ + { + 'my-list-id': [ + { + exception_lists: [ + { + id: 'myListId', + list_id: 'my-list-id', + namespace_type: 'single', + type: 'detection', + }, + ], + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + name: 'My rule', + rule_id: 'my-rule-id', + }, + ], + }, + ], + }); + }); + + test('validates "references" with empty array', () => { + const payload: RulesReferencedByExceptionListsSchema = { + references: [], + }; + + const decoded = rulesReferencedByExceptionListsSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + references: [], + }); + }); + + test('cannot add extra values', () => { + const payload: RulesReferencedByExceptionListsSchema & { extra_value?: string } = { + extra_value: 'foo', + references: [ + { + 'my-list-id': [ + { + name: 'My rule', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + rule_id: 'my-rule-id', + exception_lists: [ + { + id: 'myListId', + list_id: 'my-list-id', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }, + ], + }; + + const decoded = rulesReferencedByExceptionListsSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const output = foldLeftRight(checked); + expect(formatErrors(output.errors)).toEqual(['invalid keys "extra_value"']); + expect(output.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.ts new file mode 100644 index 0000000000000..a7f2527edc096 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/find_exception_list_references_schema.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { listArray, list_id } from '@kbn/securitysolution-io-ts-list-types'; + +import { rule_id, id, name } from '../common/schemas'; + +export const ruleReferenceSchema = t.exact( + t.type({ + name, + id, + rule_id, + exception_lists: listArray, + }) +); + +export type RuleReferenceSchema = t.OutputOf; + +export const rulesReferencedByExceptionListSchema = t.record(list_id, t.array(ruleReferenceSchema)); + +export type RuleReferencesSchema = t.OutputOf; + +export const rulesReferencedByExceptionListsSchema = t.exact( + t.type({ + references: t.array(rulesReferencedByExceptionListSchema), + }) +); + +export type RulesReferencedByExceptionListsSchema = t.OutputOf< + typeof rulesReferencedByExceptionListsSchema +>; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/index.ts index 1b688ce641a7a..e12fbf2918302 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/index.ts @@ -7,6 +7,7 @@ export * from './error_schema'; export * from './get_installed_integrations_response_schema'; +export * from './find_exception_list_references_schema'; export * from './import_rules_schema'; export * from './prepackaged_rules_schema'; export * from './prepackaged_rules_status_schema'; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts index 8c917b4ef6898..868129f3a6737 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts @@ -167,7 +167,9 @@ export class BaseDataGenerator { } protected randomVersion(): string { - return [7, ...this.randomNGenerator(20, 2)].map((x) => x.toString()).join('.'); + // the `major` is sometimes (30%) 7 and most of the time (70%) 8 + const major = this.randomBoolean(0.4) ? 7 : 8; + return [major, ...this.randomNGenerator(20, 2)].map((x) => x.toString()).join('.'); } protected randomChoice(choices: T[] | readonly T[]): T { diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts new file mode 100644 index 0000000000000..67ff2d3605093 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.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 type { DeepPartial } from 'utility-types'; +import { merge } from 'lodash'; +import { gte } from 'semver'; +import { BaseDataGenerator } from './base_data_generator'; +import type { HostMetadataInterface, OSFields } from '../types'; +import { EndpointStatus, HostPolicyResponseActionStatus } from '../types'; + +/** + * Metadata generator for docs that are sent by the Endpoint running on hosts + */ +export class EndpointMetadataGenerator extends BaseDataGenerator { + /** Generate an Endpoint host metadata document */ + generate(overrides: DeepPartial = {}): HostMetadataInterface { + const ts = overrides['@timestamp'] ?? new Date().getTime(); + const hostName = this.randomHostname(); + const agentVersion = overrides?.agent?.version ?? this.randomVersion(); + const agentId = this.seededUUIDv4(); + const isIsolated = this.randomBoolean(0.3); + const capabilities = ['isolation']; + + // v8.4 introduced additional endpoint capabilities + if (gte(agentVersion, '8.4.0')) { + capabilities.push('kill_process', 'suspend_process', 'running_processes'); + } + + const hostMetadataDoc: HostMetadataInterface = { + '@timestamp': ts, + event: { + created: ts, + id: this.seededUUIDv4(), + kind: 'metric', + category: ['host'], + type: ['info'], + module: 'endpoint', + action: 'endpoint_metadata', + dataset: 'endpoint.metadata', + }, + data_stream: { + type: 'metrics', + dataset: 'endpoint.metadata', + namespace: 'default', + }, + agent: { + version: agentVersion, + id: agentId, + type: 'endpoint', + }, + elastic: { + agent: { + id: agentId, + }, + }, + host: { + id: this.seededUUIDv4(), + hostname: hostName, + name: hostName, + architecture: this.randomString(10), + ip: this.randomArray(3, () => this.randomIP()), + mac: this.randomArray(3, () => this.randomMac()), + os: this.randomOsFields(), + }, + Endpoint: { + status: EndpointStatus.enrolled, + policy: { + applied: { + name: 'With Eventing', + id: 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', + status: HostPolicyResponseActionStatus.success, + endpoint_policy_version: 3, + version: 5, + }, + }, + configuration: { + isolation: isIsolated, + }, + state: { + isolation: isIsolated, + }, + capabilities, + }, + }; + + return merge(hostMetadataDoc, overrides); + } + + protected randomOsFields(): OSFields { + return this.randomChoice([ + { + name: 'Windows', + full: 'Windows 10', + version: '10.0', + platform: 'Windows', + family: 'windows', + Ext: { + variant: 'Windows Pro', + }, + }, + { + name: 'Windows', + full: 'Windows Server 2016', + version: '10.0', + platform: 'Windows', + family: 'windows', + Ext: { + variant: 'Windows Server', + }, + }, + { + name: 'Windows', + full: 'Windows Server 2012', + version: '6.2', + platform: 'Windows', + family: 'windows', + Ext: { + variant: 'Windows Server', + }, + }, + { + name: 'Windows', + full: 'Windows Server 2012R2', + version: '6.3', + platform: 'Windows', + family: 'windows', + Ext: { + variant: 'Windows Server Release 2', + }, + }, + { + Ext: { + variant: 'Debian', + }, + kernel: '4.19.0-21-cloud-amd64 #1 SMP Debian 4.19.249-2 (2022-06-30)', + name: 'Linux', + family: 'debian', + type: 'linux', + version: '10.12', + platform: 'debian', + full: 'Debian 10.12', + }, + ]); + } +} diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts index 1586e47aa1f4c..a005004a90682 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts @@ -504,7 +504,7 @@ describe('data generator', () => { events[previousProcessEventIndex].process?.parent?.entity_id ); expect(events[events.length - 1].event?.kind).toEqual('alert'); - expect(events[events.length - 1].event?.category).toEqual('malware'); + expect(events[events.length - 1].event?.category).toEqual('behavior'); }); }); diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index c9b554fc89031..59808b1df430c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -14,17 +14,17 @@ import type { KibanaAssetReference, } from '@kbn/fleet-plugin/common'; import { agentPolicyStatuses } from '@kbn/fleet-plugin/common'; +import { EndpointMetadataGenerator } from './data_generators/endpoint_metadata_generator'; import type { AlertEvent, DataStream, - Host, HostMetadata, + HostMetadataInterface, HostPolicyResponse, - OSFields, PolicyData, SafeEndpointEvent, } from './types'; -import { EndpointStatus, HostPolicyResponseActionStatus } from './types'; +import { HostPolicyResponseActionStatus } from './types'; import { policyFactory } from './models/policy_config'; import { ancestryArray, @@ -49,55 +49,6 @@ export type Event = AlertEvent | SafeEndpointEvent; */ export const ANCESTRY_LIMIT: number = 2; -const Windows: OSFields[] = [ - { - name: 'Windows', - full: 'Windows 10', - version: '10.0', - platform: 'Windows', - family: 'windows', - Ext: { - variant: 'Windows Pro', - }, - }, - { - name: 'Windows', - full: 'Windows Server 2016', - version: '10.0', - platform: 'Windows', - family: 'windows', - Ext: { - variant: 'Windows Server', - }, - }, - { - name: 'Windows', - full: 'Windows Server 2012', - version: '6.2', - platform: 'Windows', - family: 'windows', - Ext: { - variant: 'Windows Server', - }, - }, - { - name: 'Windows', - full: 'Windows Server 2012R2', - version: '6.3', - platform: 'Windows', - family: 'windows', - Ext: { - variant: 'Windows Server Release 2', - }, - }, -]; - -const Linux: OSFields[] = []; - -const Mac: OSFields[] = []; - -const OS: OSFields[] = [...Windows, ...Mac, ...Linux]; - const POLICY_RESPONSE_STATUSES: HostPolicyResponseActionStatus[] = [ HostPolicyResponseActionStatus.success, HostPolicyResponseActionStatus.failure, @@ -105,13 +56,7 @@ const POLICY_RESPONSE_STATUSES: HostPolicyResponseActionStatus[] = [ HostPolicyResponseActionStatus.unsupported, ]; -const APPLIED_POLICIES: Array<{ - name: string; - id: string; - status: HostPolicyResponseActionStatus; - endpoint_policy_version: number; - version: number; -}> = [ +const APPLIED_POLICIES: Array = [ { name: 'Default', id: '00000000-0000-0000-0000-000000000000', @@ -231,38 +176,7 @@ const OTHER_EVENT_CATEGORIES: Record< }, }; -interface HostInfo { - elastic: { - agent: { - id: string; - }; - }; - agent: { - version: string; - id: string; - type: string; - }; - host: Host; - Endpoint: { - status: EndpointStatus; - policy: { - applied: { - id: string; - status: HostPolicyResponseActionStatus; - name: string; - endpoint_policy_version: number; - version: number; - }; - }; - configuration?: { - isolation: boolean; - }; - state?: { - isolation: boolean; - }; - capabilities?: string[]; - }; -} +type CommonHostInfo = Pick; interface NodeState { event: Event; @@ -403,17 +317,32 @@ const alertsDefaultDataStream = { namespace: 'default', }; +/** + * Generator to create various ElasticSearch documents that are normally streamed by the Endpoint. + * + * NOTE: this generator currently reuses certain data (ex. `this.commonInfo`) across several + * documents, thus use caution if manipulating/mutating value in the generated data + * (ex. in tests). Individual standalone generators exist, whose generated data does not + * contain shared data structures. + */ export class EndpointDocGenerator extends BaseDataGenerator { - commonInfo: HostInfo; + commonInfo: CommonHostInfo; sequence: number = 0; + private readonly metadataGenerator: EndpointMetadataGenerator; + /** * The EndpointDocGenerator parameters * * @param seed either a string to seed the random number generator or a random number generator function + * @param MetadataGenerator */ - constructor(seed: string | seedrandom.prng = Math.random().toString()) { + constructor( + seed: string | seedrandom.prng = Math.random().toString(), + MetadataGenerator: typeof EndpointMetadataGenerator = EndpointMetadataGenerator + ) { super(seed); + this.metadataGenerator = new MetadataGenerator(seed); this.commonInfo = this.createHostData(); } @@ -456,47 +385,12 @@ export class EndpointDocGenerator extends BaseDataGenerator { }; } - private createHostData(): HostInfo { - const hostName = this.randomHostname(); - const isIsolated = this.randomBoolean(0.3); - const agentVersion = this.randomVersion(); - const capabilities = ['isolation', 'kill_process', 'suspend_process', 'running_processes']; - const agentId = this.seededUUIDv4(); + private createHostData(): CommonHostInfo { + const { agent, elastic, host, Endpoint } = this.metadataGenerator.generate({ + Endpoint: { policy: { applied: this.randomChoice(APPLIED_POLICIES) } }, + }); - return { - agent: { - version: agentVersion, - id: agentId, - type: 'endpoint', - }, - elastic: { - agent: { - id: agentId, - }, - }, - host: { - id: this.seededUUIDv4(), - hostname: hostName, - name: hostName, - architecture: this.randomString(10), - ip: this.randomArray(3, () => this.randomIP()), - mac: this.randomArray(3, () => this.randomMac()), - os: this.randomChoice(OS), - }, - Endpoint: { - status: EndpointStatus.enrolled, - policy: { - applied: this.randomChoice(APPLIED_POLICIES), - }, - configuration: { - isolation: isIsolated, - }, - state: { - isolation: isIsolated, - }, - capabilities, - }, - }; + return { agent, elastic, host, Endpoint }; } /** @@ -508,21 +402,11 @@ export class EndpointDocGenerator extends BaseDataGenerator { ts = new Date().getTime(), metadataDataStream = metadataDefaultDataStream ): HostMetadata { - return { + return this.metadataGenerator.generate({ '@timestamp': ts, - event: { - created: ts, - id: this.seededUUIDv4(), - kind: 'metric', - category: ['host'], - type: ['info'], - module: 'endpoint', - action: 'endpoint_metadata', - dataset: 'endpoint.metadata', - }, - ...this.commonInfo, data_stream: metadataDataStream, - }; + ...this.commonInfo, + }); } /** @@ -1628,7 +1512,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { } /** - * Generates an Ingest `package policy` that includes the Endpoint Policy data + * Generates a Fleet `package policy` that includes the Endpoint Policy data */ public generatePolicyPackagePolicy(): PolicyData { const created = new Date(Date.now() - 8.64e7).toISOString(); // 24h ago @@ -1673,7 +1557,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { } /** - * Generate an Agent Policy (ingest) + * Generate an Agent Policy (Fleet) */ public generateAgentPolicy(): GetAgentPoliciesResponseItem { // FIXME: remove and use new FleetPackagePolicyGenerator (#2262) @@ -1693,7 +1577,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { } /** - * Generate an EPM Package for Endpoint + * Generate a Fleet EPM Package for Endpoint */ public generateEpmPackage(): GetPackagesResponse['items'][0] { return { diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index ea01e62fbc807..4971dc83c29aa 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -43,6 +43,7 @@ export type IndexedHostsAndAlertsResponse = IndexedHostsResponse; * @param alertsPerHost * @param fleet * @param options + * @param DocGenerator */ export async function indexHostsAndAlerts( client: Client, @@ -56,7 +57,8 @@ export async function indexHostsAndAlerts( alertIndex: string, alertsPerHost: number, fleet: boolean, - options: TreeOptions = {} + options: TreeOptions = {}, + DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator ): Promise { const random = seedrandom(seed); const epmEndpointPackage = await getEndpointPackageInfo(kbnClient); @@ -91,7 +93,7 @@ export async function indexHostsAndAlerts( const realPolicies: Record = {}; for (let i = 0; i < numHosts; i++) { - const generator = new EndpointDocGenerator(random); + const generator = new DocGenerator(random); const indexedHosts = await indexEndpointHostDocs({ numDocs, client, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 0f791a1f409d1..cdadf9619f008 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -492,9 +492,11 @@ export type HostInfo = Immutable<{ }; }>; -// HostMetadataDetails is now just HostMetadata -// HostDetails is also just HostMetadata -export type HostMetadata = Immutable<{ +// Host metadata document streamed up to ES by the Endpoint running on host machines. +// NOTE: `HostMetadata` type is the original and defined as Immutable. If needing to +// work with metadata that is not mutable, use `HostMetadataInterface` +export type HostMetadata = Immutable; +export interface HostMetadataInterface { '@timestamp': number; event: { created: number; @@ -542,10 +544,11 @@ export type HostMetadata = Immutable<{ agent: { id: string; version: string; + type: string; }; host: Host; data_stream: DataStream; -}>; +} export type UnitedAgentMetadata = Immutable<{ agent: { diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts index 2eee56f15f083..bbb2991551f26 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -25,33 +25,35 @@ export interface RiskScoreRequestOptions extends IEsSearchRequest { export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse { inspect?: Maybe; totalCount: number; - data: HostsRiskScore[] | undefined; + data: HostRiskScore[] | undefined; } export interface UsersRiskScoreStrategyResponse extends IEsSearchResponse { inspect?: Maybe; totalCount: number; - data: UsersRiskScore[] | undefined; + data: UserRiskScore[] | undefined; } -export interface RiskScore { - '@timestamp': string; - risk: string; - risk_stats: { - rule_risks: RuleRisk[]; - risk_score: number; - }; +export interface RiskStats { + rule_risks: RuleRisk[]; + calculated_score_norm: number; + multipliers: string[]; + calculated_level: RiskSeverity; } -export interface HostsRiskScore extends RiskScore { +export interface HostRiskScore { + '@timestamp': string; host: { name: string; + risk: RiskStats; }; } -export interface UsersRiskScore extends RiskScore { +export interface UserRiskScore { + '@timestamp': string; user: { name: string; + risk: RiskStats; }; } @@ -66,17 +68,23 @@ export type RiskScoreSortField = SortField; export const enum RiskScoreFields { timestamp = '@timestamp', hostName = 'host.name', + hostRiskScore = 'host.risk.calculated_score_norm', + hostRisk = 'host.risk.calculated_level', userName = 'user.name', - riskScore = 'risk_stats.risk_score', - risk = 'risk', + userRiskScore = 'user.risk.calculated_score_norm', + userRisk = 'user.risk.calculated_level', } export interface RiskScoreItem { _id?: Maybe; [RiskScoreFields.hostName]: Maybe; [RiskScoreFields.userName]: Maybe; - [RiskScoreFields.risk]: Maybe; - [RiskScoreFields.riskScore]: Maybe; + + [RiskScoreFields.hostRisk]: Maybe; + [RiskScoreFields.userRisk]: Maybe; + + [RiskScoreFields.hostRiskScore]: Maybe; + [RiskScoreFields.userRiskScore]: Maybe; } export const enum RiskSeverity { @@ -86,3 +94,6 @@ export const enum RiskSeverity { high = 'High', critical = 'Critical', } + +export const isUserRiskScore = (risk: HostRiskScore | UserRiskScore): risk is UserRiskScore => + 'user' in risk; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts index c6f651440edb9..b7ef7a4c5cbda 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts @@ -33,4 +33,7 @@ export enum RiskQueries { kpiRiskScore = 'kpiRiskScore', } -export type RiskScoreAggByFields = 'host.name' | 'user.name'; +export const enum RiskScoreEntity { + host = 'host', + user = 'user', +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts index 2fe24f4440088..4d95846a4f740 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts @@ -6,7 +6,7 @@ */ import type { IEsSearchRequest, IEsSearchResponse } from '@kbn/data-plugin/common'; -import type { FactoryQueryTypes, RiskScoreAggByFields, RiskSeverity } from '../..'; +import type { FactoryQueryTypes, RiskScoreEntity, RiskSeverity } from '../..'; import type { ESQuery } from '../../../../typed_json'; import type { Inspect, Maybe } from '../../../common'; @@ -15,7 +15,7 @@ export interface KpiRiskScoreRequestOptions extends IEsSearchRequest { defaultIndex: string[]; factoryQueryType?: FactoryQueryTypes; filterQuery?: ESQuery | string | undefined; - aggBy: RiskScoreAggByFields; + entity: RiskScoreEntity; } export interface KpiRiskScoreStrategyResponse extends IEsSearchResponse { diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts index c5cb4351757a0..81ef6daf184a8 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts @@ -5,22 +5,15 @@ * 2.0. */ -import type { CommonFields, Maybe, RiskSeverity, SortField } from '../../..'; +import type { CommonFields, Maybe, RiskScoreFields, RiskSeverity, SortField } from '../../..'; import type { HostEcs } from '../../../../ecs/host'; import type { UserEcs } from '../../../../ecs/user'; -export const enum UserRiskScoreFields { - timestamp = '@timestamp', - userName = 'user.name', - riskScore = 'risk_stats.risk_score', - risk = 'risk', -} - export interface UserRiskScoreItem { _id?: Maybe; - [UserRiskScoreFields.userName]: Maybe; - [UserRiskScoreFields.risk]: Maybe; - [UserRiskScoreFields.riskScore]: Maybe; + [RiskScoreFields.userName]: Maybe; + [RiskScoreFields.userRisk]: Maybe; + [RiskScoreFields.userRiskScore]: Maybe; } export interface UserItem { diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_data_view_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_data_view_exception.spec.ts deleted file mode 100644 index c818f4e51060f..0000000000000 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_data_view_exception.spec.ts +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getException } from '../../objects/exception'; -import { getNewRule } from '../../objects/rule'; - -import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; - -import { addExceptionFromFirstAlert, goToClosedAlerts, goToOpenedAlerts } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; -import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; -import { esArchiverLoad, esArchiverUnload, esArchiverResetKibana } from '../../tasks/es_archiver'; -import { login, visitWithoutDateRange } from '../../tasks/login'; -import { - addsException, - addsExceptionFromRuleSettings, - editException, - goToAlertsTab, - goToExceptionsTab, - removeException, - waitForTheRuleToBeExecuted, -} from '../../tasks/rule_details'; - -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { deleteAlertsAndRules, postDataView } from '../../tasks/common'; -import { - EXCEPTION_EDIT_FLYOUT_SAVE_BTN, - EXCEPTION_ITEM_CONTAINER, - FIELD_INPUT, -} from '../../screens/exceptions'; -import { - addExceptionEntryFieldValueOfItemX, - addExceptionEntryFieldValueValue, -} from '../../tasks/exceptions'; - -describe('Adds rule exception using data views', () => { - const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; - - before(() => { - esArchiverResetKibana(); - esArchiverLoad('exceptions'); - login(); - - postDataView('exceptions-*'); - }); - - beforeEach(() => { - deleteAlertsAndRules(); - createCustomRuleEnabled( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { dataView: 'exceptions-*', type: 'dataView' }, - }, - 'rule_testing', - '1s' - ); - visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - goToRuleDetails(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - }); - - afterEach(() => { - esArchiverUnload('exceptions_2'); - }); - - after(() => { - esArchiverUnload('exceptions'); - }); - - it('Creates an exception from an alert and deletes it', () => { - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS); - // Create an exception from the alerts actions menu that matches - // the existing alert - addExceptionFromFirstAlert(); - addsException(getException()); - - // Alerts table should now be empty from having added exception and closed - // matching alert - cy.get(EMPTY_ALERT_TABLE).should('exist'); - - // Closed alert should appear in table - goToClosedAlerts(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - - // Remove the exception and load an event that would have matched that exception - // to show that said exception now starts to show up again - goToExceptionsTab(); - removeException(); - esArchiverLoad('exceptions_2'); - goToAlertsTab(); - goToOpenedAlerts(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - }); - - it('Creates an exception from a rule and deletes it', () => { - // Create an exception from the exception tab that matches - // the existing alert - goToExceptionsTab(); - addsExceptionFromRuleSettings(getException()); - - // Alerts table should now be empty from having added exception and closed - // matching alert - goToAlertsTab(); - cy.get(EMPTY_ALERT_TABLE).should('exist'); - - // Closed alert should appear in table - goToClosedAlerts(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - - // Remove the exception and load an event that would have matched that exception - // to show that said exception now starts to show up again - goToExceptionsTab(); - removeException(); - esArchiverLoad('exceptions_2'); - goToAlertsTab(); - goToOpenedAlerts(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - }); - - it('Edits an exception', () => { - goToExceptionsTab(); - addsExceptionFromRuleSettings(getException()); - - editException(); - - // check that the existing item's field is being populated - cy.get(EXCEPTION_ITEM_CONTAINER) - .eq(0) - .find(FIELD_INPUT) - .eq(0) - .should('have.text', 'agent.name'); - - // check that you can select a different field - addExceptionEntryFieldValueOfItemX('user.name{downarrow}{enter}', 0, 0); - addExceptionEntryFieldValueValue('test', 0); - - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click(); - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('have.attr', 'disabled'); - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('not.exist'); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_exception.spec.ts deleted file mode 100644 index 886f772bc25e5..0000000000000 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_edit_exception.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getException } from '../../objects/exception'; -import { getNewRule } from '../../objects/rule'; - -import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; - -import { addExceptionFromFirstAlert, goToClosedAlerts, goToOpenedAlerts } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; -import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; -import { esArchiverLoad, esArchiverUnload, esArchiverResetKibana } from '../../tasks/es_archiver'; -import { login, visitWithoutDateRange } from '../../tasks/login'; -import { - addsException, - addsExceptionFromRuleSettings, - editException, - goToAlertsTab, - goToExceptionsTab, - removeException, - waitForTheRuleToBeExecuted, -} from '../../tasks/rule_details'; - -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { deleteAlertsAndRules } from '../../tasks/common'; -import { - EXCEPTION_EDIT_FLYOUT_SAVE_BTN, - EXCEPTION_ITEM_CONTAINER, - FIELD_INPUT, -} from '../../screens/exceptions'; -import { - addExceptionEntryFieldValueOfItemX, - addExceptionEntryFieldValueValue, -} from '../../tasks/exceptions'; - -describe('Adds rule exception', () => { - const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; - - before(() => { - esArchiverResetKibana(); - esArchiverLoad('exceptions'); - login(); - }); - - beforeEach(() => { - deleteAlertsAndRules(); - createCustomRuleEnabled( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, - }, - 'rule_testing', - '1s' - ); - visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - goToRuleDetails(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - }); - - afterEach(() => { - esArchiverUnload('exceptions_2'); - }); - - after(() => { - esArchiverUnload('exceptions'); - }); - - it('Creates an exception from an alert and deletes it', () => { - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS); - // Create an exception from the alerts actions menu that matches - // the existing alert - addExceptionFromFirstAlert(); - addsException(getException()); - - // Alerts table should now be empty from having added exception and closed - // matching alert - cy.get(EMPTY_ALERT_TABLE).should('exist'); - - // Closed alert should appear in table - goToClosedAlerts(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - - // Remove the exception and load an event that would have matched that exception - // to show that said exception now starts to show up again - goToExceptionsTab(); - removeException(); - esArchiverLoad('exceptions_2'); - goToAlertsTab(); - goToOpenedAlerts(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - }); - - it('Creates an exception from a rule and deletes it', () => { - // Create an exception from the exception tab that matches - // the existing alert - goToExceptionsTab(); - addsExceptionFromRuleSettings(getException()); - - // Alerts table should now be empty from having added exception and closed - // matching alert - goToAlertsTab(); - cy.get(EMPTY_ALERT_TABLE).should('exist'); - - // Closed alert should appear in table - goToClosedAlerts(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - - // Remove the exception and load an event that would have matched that exception - // to show that said exception now starts to show up again - goToExceptionsTab(); - removeException(); - esArchiverLoad('exceptions_2'); - goToAlertsTab(); - goToOpenedAlerts(); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); - }); - - it('Edits an exception', () => { - goToExceptionsTab(); - addsExceptionFromRuleSettings(getException()); - - editException(); - - // check that the existing item's field is being populated - cy.get(EXCEPTION_ITEM_CONTAINER) - .eq(0) - .find(FIELD_INPUT) - .eq(0) - .should('have.text', 'agent.name'); - - // check that you can select a different field - addExceptionEntryFieldValueOfItemX('user.name{downarrow}{enter}', 0, 0); - addExceptionEntryFieldValueValue('test', 0); - - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click(); - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('have.attr', 'disabled'); - cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('not.exist'); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_table_flow/add_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_table_flow/add_exception.spec.ts new file mode 100644 index 0000000000000..f1d6d2f1cc063 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_table_flow/add_exception.spec.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getException } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; + +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../../screens/alerts'; + +import { + addExceptionFromFirstAlert, + goToClosedAlerts, + goToOpenedAlerts, +} from '../../../tasks/alerts'; +import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { + esArchiverLoad, + esArchiverUnload, + esArchiverResetKibana, +} from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { + addsException, + goToAlertsTab, + goToExceptionsTab, + removeException, + waitForTheRuleToBeExecuted, +} from '../../../tasks/rule_details'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { deleteAlertsAndRules } from '../../../tasks/common'; + +describe('Adds rule exception from alerts flow', () => { + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; + + before(() => { + esArchiverResetKibana(); + esArchiverLoad('exceptions'); + login(); + }); + + beforeEach(() => { + deleteAlertsAndRules(); + createCustomRuleEnabled( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, + }, + 'rule_testing', + '1s' + ); + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + }); + + afterEach(() => { + esArchiverUnload('exceptions_2'); + }); + + after(() => { + esArchiverUnload('exceptions'); + }); + + it('Creates an exception from an alert and deletes it', () => { + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS); + // Create an exception from the alerts actions menu that matches + // the existing alert + addExceptionFromFirstAlert(); + addsException(getException()); + + // Alerts table should now be empty from having added exception and closed + // matching alert + cy.get(EMPTY_ALERT_TABLE).should('exist'); + + // Closed alert should appear in table + goToClosedAlerts(); + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); + + // Remove the exception and load an event that would have matched that exception + // to show that said exception now starts to show up again + goToExceptionsTab(); + removeException(); + esArchiverLoad('exceptions_2'); + goToAlertsTab(); + goToOpenedAlerts(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', '2 alerts'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts index 8c2e2af4b8bad..20f55a4fffd4f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts @@ -14,7 +14,7 @@ import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { esArchiverLoad, esArchiverResetKibana, esArchiverUnload } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { - openExceptionFlyoutFromRuleSettings, + openExceptionFlyoutFromEmptyViewerPrompt, goToExceptionsTab, editException, } from '../../tasks/rule_details'; @@ -34,7 +34,7 @@ import { FIELD_INPUT, LOADING_SPINNER, EXCEPTION_ITEM_CONTAINER, - ADD_EXCEPTIONS_BTN, + ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN, EXCEPTION_FIELD_LIST, EXCEPTION_EDIT_FLYOUT_SAVE_BTN, EXCEPTION_FLYOUT_VERSION_CONFLICT, @@ -94,7 +94,7 @@ describe('Exceptions flyout', () => { it('Validates empty entry values correctly', () => { cy.root() .pipe(($el) => { - $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + $el.find(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).trigger('click'); return $el.find(ADD_AND_BTN); }) .should('be.visible'); @@ -123,7 +123,7 @@ describe('Exceptions flyout', () => { it('Does not overwrite values and-ed together', () => { cy.root() .pipe(($el) => { - $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + $el.find(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).trigger('click'); return $el.find(ADD_AND_BTN); }) .should('be.visible'); @@ -146,7 +146,7 @@ describe('Exceptions flyout', () => { it('Does not overwrite values or-ed together', () => { cy.root() .pipe(($el) => { - $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + $el.find(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).trigger('click'); return $el.find(ADD_AND_BTN); }) .should('be.visible'); @@ -201,7 +201,7 @@ describe('Exceptions flyout', () => { }); it('Does not overwrite values of nested entry items', () => { - openExceptionFlyoutFromRuleSettings(); + openExceptionFlyoutFromEmptyViewerPrompt(); cy.get(LOADING_SPINNER).should('not.exist'); // exception item 1 @@ -267,7 +267,7 @@ describe('Exceptions flyout', () => { it('Contains custom index fields', () => { cy.root() .pipe(($el) => { - $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + $el.find(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).trigger('click'); return $el.find(ADD_AND_BTN); }) .should('be.visible'); @@ -303,7 +303,7 @@ describe('Exceptions flyout', () => { goToExceptionsTab(); }); - context('When updating an item with version conflict', () => { + context.skip('When updating an item with version conflict', () => { it('Displays version conflict error', () => { editException(); @@ -334,7 +334,7 @@ describe('Exceptions flyout', () => { }); }); - context('When updating an item for a list that has since been deleted', () => { + context.skip('When updating an item for a list that has since been deleted', () => { it('Displays missing exception list error', () => { editException(); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_management_flow/all_exception_lists_read_only.spec.ts similarity index 70% rename from x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts rename to x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_management_flow/all_exception_lists_read_only.spec.ts index e17bc694ab48a..c1c4fc18960e6 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_management_flow/all_exception_lists_read_only.spec.ts @@ -5,14 +5,18 @@ * 2.0. */ -import { ROLES } from '../../../common/test'; -import { getExceptionList } from '../../objects/exception'; -import { EXCEPTIONS_TABLE_SHOWING_LISTS } from '../../screens/exceptions'; -import { createExceptionList } from '../../tasks/api_calls/exceptions'; -import { dismissCallOut, getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts'; -import { esArchiverResetKibana } from '../../tasks/es_archiver'; -import { login, visitWithoutDateRange } from '../../tasks/login'; -import { EXCEPTIONS_URL } from '../../urls/navigation'; +import { ROLES } from '../../../../common/test'; +import { getExceptionList } from '../../../objects/exception'; +import { EXCEPTIONS_TABLE_SHOWING_LISTS } from '../../../screens/exceptions'; +import { createExceptionList } from '../../../tasks/api_calls/exceptions'; +import { + dismissCallOut, + getCallOut, + waitForCallOutToBeShown, +} from '../../../tasks/common/callouts'; +import { esArchiverResetKibana } from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { EXCEPTIONS_URL } from '../../../urls/navigation'; const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges'; diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception.spec.ts new file mode 100644 index 0000000000000..add3f01798129 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception.spec.ts @@ -0,0 +1,221 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getException, getExceptionList } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; + +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../../screens/alerts'; +import { createCustomRule, createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { goToClosedAlerts, goToOpenedAlerts } from '../../../tasks/alerts'; +import { + esArchiverLoad, + esArchiverUnload, + esArchiverResetKibana, +} from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { + addExceptionFromRuleDetails, + addFirstExceptionFromRuleDetails, + goToAlertsTab, + goToExceptionsTab, + removeException, + searchForExceptionItem, + waitForTheRuleToBeExecuted, +} from '../../../tasks/rule_details'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { deleteAlertsAndRules } from '../../../tasks/common'; +import { + NO_EXCEPTIONS_EXIST_PROMPT, + EXCEPTION_ITEM_VIEWER_CONTAINER, + NO_EXCEPTIONS_SEARCH_RESULTS_PROMPT, +} from '../../../screens/exceptions'; +import { + createExceptionList, + createExceptionListItem, + deleteExceptionList, +} from '../../../tasks/api_calls/exceptions'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; + +describe('Add exception from rule details', () => { + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; + + before(() => { + esArchiverResetKibana(); + esArchiverLoad('exceptions'); + login(); + }); + + after(() => { + esArchiverUnload('exceptions'); + }); + + describe('rule with existing exceptions', () => { + const exceptionList = getExceptionList(); + beforeEach(() => { + deleteAlertsAndRules(); + deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); + // create rule with exceptions + createExceptionList(exceptionList, exceptionList.list_id).then((response) => { + createCustomRule( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, + exceptionLists: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + }, + '2' + ); + createExceptionListItem(exceptionList.list_id, { + list_id: exceptionList.list_id, + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + name: 'Sample Exception List Item', + namespace_type: 'single', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match_any', + value: ['bar'], + }, + ], + }); + createExceptionListItem(exceptionList.list_id, { + list_id: exceptionList.list_id, + item_id: 'simple_list_item_2', + tags: [], + type: 'simple', + description: 'Test exception item 2', + name: 'Sample Exception List Item 2', + namespace_type: 'single', + entries: [ + { + field: 'unique_value.test', + operator: 'included', + type: 'match_any', + value: ['foo'], + }, + ], + }); + }); + + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + goToExceptionsTab(); + }); + + it('Creates an exception item', () => { + // displays existing exception items + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist'); + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2); + + // clicks prompt button to add a new exception item + addExceptionFromRuleDetails(getException()); + + // new exception item displays + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 3); + }); + + // Trying to figure out with EUI why the search won't trigger + it.skip('Can search for items', () => { + // displays existing exception items + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist'); + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2); + + // can search for an exception value + searchForExceptionItem('foo'); + + // new exception item displays + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // displays empty search result view if no matches found + searchForExceptionItem('abc'); + + // new exception item displays + cy.get(NO_EXCEPTIONS_SEARCH_RESULTS_PROMPT).should('exist'); + }); + }); + + describe('rule without existing exceptions', () => { + beforeEach(() => { + deleteAlertsAndRules(); + createCustomRuleEnabled( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, + }, + 'rule_testing', + '1s' + ); + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + goToExceptionsTab(); + }); + + afterEach(() => { + esArchiverUnload('exceptions_2'); + }); + + it('Creates an exception item when none exist', () => { + // when no exceptions exist, empty component shows with action to add exception + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); + + // clicks prompt button to add first exception that will also select to close + // all matching alerts + addFirstExceptionFromRuleDetails({ + field: 'agent.name', + operator: 'is', + values: ['foo'], + }); + + // new exception item displays + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // Alerts table should now be empty from having added exception and closed + // matching alert + goToAlertsTab(); + cy.get(EMPTY_ALERT_TABLE).should('exist'); + + // Closed alert should appear in table + goToClosedAlerts(); + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); + + // Remove the exception and load an event that would have matched that exception + // to show that said exception now starts to show up again + goToExceptionsTab(); + + // when removing exception and again, no more exist, empty screen shows again + removeException(); + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); + + // load more docs + esArchiverLoad('exceptions_2'); + + // now that there are no more exceptions, the docs should match and populate alerts + goToAlertsTab(); + goToOpenedAlerts(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', '2 alerts'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception_data_view.spect.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception_data_view.spect.ts new file mode 100644 index 0000000000000..05b21abe52565 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/add_exception_data_view.spect.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getNewRule } from '../../../objects/rule'; +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../../screens/alerts'; +import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { goToClosedAlerts, goToOpenedAlerts } from '../../../tasks/alerts'; +import { + esArchiverLoad, + esArchiverUnload, + esArchiverResetKibana, +} from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { + addFirstExceptionFromRuleDetails, + goToAlertsTab, + goToExceptionsTab, + removeException, + waitForTheRuleToBeExecuted, +} from '../../../tasks/rule_details'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { postDataView, deleteAlertsAndRules } from '../../../tasks/common'; +import { + NO_EXCEPTIONS_EXIST_PROMPT, + EXCEPTION_ITEM_VIEWER_CONTAINER, +} from '../../../screens/exceptions'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; + +describe('Add exception using data views from rule details', () => { + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; + + before(() => { + esArchiverResetKibana(); + esArchiverLoad('exceptions'); + login(); + postDataView('exceptions-*'); + }); + + after(() => { + esArchiverUnload('exceptions'); + }); + + beforeEach(() => { + deleteAlertsAndRules(); + createCustomRuleEnabled( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { dataView: 'exceptions-*', type: 'dataView' }, + }, + 'rule_testing', + '1s' + ); + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + goToExceptionsTab(); + }); + + afterEach(() => { + esArchiverUnload('exceptions_2'); + }); + + it('Creates an exception item when none exist', () => { + // when no exceptions exist, empty component shows with action to add exception + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); + + // clicks prompt button to add first exception that will also select to close + // all matching alerts + addFirstExceptionFromRuleDetails({ + field: 'agent.name', + operator: 'is', + values: ['foo'], + }); + + // new exception item displays + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // Alerts table should now be empty from having added exception and closed + // matching alert + goToAlertsTab(); + cy.get(EMPTY_ALERT_TABLE).should('exist'); + + // Closed alert should appear in table + goToClosedAlerts(); + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); + + // Remove the exception and load an event that would have matched that exception + // to show that said exception now starts to show up again + goToExceptionsTab(); + + // when removing exception and again, no more exist, empty screen shows again + removeException(); + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); + + // load more docs + esArchiverLoad('exceptions_2'); + + // now that there are no more exceptions, the docs should match and populate alerts + goToAlertsTab(); + goToOpenedAlerts(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', '2 alerts'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception.spec.ts new file mode 100644 index 0000000000000..26763c9efeebc --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception.spec.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 { getExceptionList } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; + +import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { goToOpenedAlerts } from '../../../tasks/alerts'; +import { + esArchiverLoad, + esArchiverUnload, + esArchiverResetKibana, +} from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { + goToExceptionsTab, + waitForTheRuleToBeExecuted, + editException, + goToAlertsTab, +} from '../../../tasks/rule_details'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { deleteAlertsAndRules } from '../../../tasks/common'; +import { + EXCEPTION_EDIT_FLYOUT_SAVE_BTN, + EXCEPTION_ITEM_VIEWER_CONTAINER, + EXCEPTION_ITEM_CONTAINER, + FIELD_INPUT, +} from '../../../screens/exceptions'; +import { + createExceptionList, + createExceptionListItem, + deleteExceptionList, +} from '../../../tasks/api_calls/exceptions'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { + addExceptionEntryFieldValueOfItemX, + addExceptionEntryFieldValueValue, +} from '../../../tasks/exceptions'; +import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../../screens/alerts'; + +describe('Edit exception from rule details', () => { + const exceptionList = getExceptionList(); + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; + + before(() => { + esArchiverResetKibana(); + esArchiverLoad('exceptions'); + login(); + }); + + after(() => { + esArchiverUnload('exceptions'); + }); + + beforeEach(() => { + deleteAlertsAndRules(); + deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); + // create rule with exceptions + createExceptionList(exceptionList, exceptionList.list_id).then((response) => { + createCustomRuleEnabled( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, + exceptionLists: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + }, + '2', + '2s' + ); + createExceptionListItem(exceptionList.list_id, { + list_id: exceptionList.list_id, + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + name: 'Sample Exception List Item', + namespace_type: 'single', + entries: [ + { + field: 'unique_value.test', + operator: 'included', + type: 'match_any', + value: ['bar'], + }, + ], + }); + }); + + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + goToExceptionsTab(); + }); + + afterEach(() => { + esArchiverUnload('exceptions_2'); + }); + + it('Edits an exception item', () => { + // displays existing exception item + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + editException(); + + // check that the existing item's field is being populated + cy.get(EXCEPTION_ITEM_CONTAINER) + .eq(0) + .find(FIELD_INPUT) + .eq(0) + .should('have.text', 'unique_value.test'); + + // check that you can select a different field + addExceptionEntryFieldValueOfItemX('agent.name{downarrow}{enter}', 0, 0); + addExceptionEntryFieldValueValue('foo', 0); + + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click(); + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('have.attr', 'disabled'); + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('not.exist'); + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // Alerts table should still show single alert + goToAlertsTab(); + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); + + // load more docs + esArchiverLoad('exceptions_2'); + + // now that 2 more docs have been added, one should match the edited exception + goToAlertsTab(); + goToOpenedAlerts(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(2); + + // there should be 2 alerts, one is the original alert and the second is for the newly + // matching doc + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', '2 alerts'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception_data_view.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception_data_view.spec.ts new file mode 100644 index 0000000000000..b5178615aa581 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/edit_exception_data_view.spec.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getExceptionList } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; + +import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { goToOpenedAlerts } from '../../../tasks/alerts'; +import { + esArchiverLoad, + esArchiverUnload, + esArchiverResetKibana, +} from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { + goToExceptionsTab, + waitForTheRuleToBeExecuted, + editException, + goToAlertsTab, +} from '../../../tasks/rule_details'; + +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { postDataView, deleteAlertsAndRules } from '../../../tasks/common'; +import { + EXCEPTION_EDIT_FLYOUT_SAVE_BTN, + EXCEPTION_ITEM_VIEWER_CONTAINER, + EXCEPTION_ITEM_CONTAINER, + FIELD_INPUT, +} from '../../../screens/exceptions'; +import { + createExceptionList, + createExceptionListItem, + deleteExceptionList, +} from '../../../tasks/api_calls/exceptions'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { + addExceptionEntryFieldValueOfItemX, + addExceptionEntryFieldValueValue, +} from '../../../tasks/exceptions'; +import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../../screens/alerts'; + +describe('Edit exception using data views from rule details', () => { + const exceptionList = getExceptionList(); + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; + + before(() => { + esArchiverResetKibana(); + esArchiverLoad('exceptions'); + login(); + postDataView('exceptions-*'); + }); + + after(() => { + esArchiverUnload('exceptions'); + }); + + beforeEach(() => { + deleteAlertsAndRules(); + deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); + // create rule with exceptions + createExceptionList(exceptionList, exceptionList.list_id).then((response) => { + createCustomRuleEnabled( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { dataView: 'exceptions-*', type: 'dataView' }, + exceptionLists: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + }, + '2', + '2s' + ); + createExceptionListItem(exceptionList.list_id, { + list_id: exceptionList.list_id, + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + name: 'Sample Exception List Item', + namespace_type: 'single', + entries: [ + { + field: 'unique_value.test', + operator: 'included', + type: 'match_any', + value: ['bar'], + }, + ], + }); + }); + + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + goToExceptionsTab(); + }); + + afterEach(() => { + esArchiverUnload('exceptions_2'); + }); + + it('Edits an exception item', () => { + // displays existing exception item + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + editException(); + + // check that the existing item's field is being populated + cy.get(EXCEPTION_ITEM_CONTAINER) + .eq(0) + .find(FIELD_INPUT) + .eq(0) + .should('have.text', 'unique_value.test'); + + // check that you can select a different field + addExceptionEntryFieldValueOfItemX('agent.name{downarrow}{enter}', 0, 0); + addExceptionEntryFieldValueValue('foo', 0); + + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click(); + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('have.attr', 'disabled'); + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).should('not.exist'); + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // Alerts table should still show single alert + goToAlertsTab(); + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); + + // load more docs + esArchiverLoad('exceptions_2'); + + // now that 2 more docs have been added, one should match the edited exception + goToAlertsTab(); + goToOpenedAlerts(); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(2); + + // there should be 2 alerts, one is the original alert and the second is for the newly + // matching doc + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS).should('have.text', '2 alerts'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/read_only_view.spect.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/read_only_view.spect.ts new file mode 100644 index 0000000000000..b11c688520b1a --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/rule_details_flow/read_only_view.spect.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 { getExceptionList } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; +import { ROLES } from '../../../../common/test'; +import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { esArchiverResetKibana } from '../../../tasks/es_archiver'; +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details'; +import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { deleteAlertsAndRules } from '../../../tasks/common'; +import { + NO_EXCEPTIONS_EXIST_PROMPT, + EXCEPTION_ITEM_VIEWER_CONTAINER, + ADD_EXCEPTIONS_BTN_FROM_VIEWER_HEADER, + ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN, +} from '../../../screens/exceptions'; +import { EXCEPTION_ITEM_ACTIONS_BUTTON } from '../../../screens/rule_details'; +import { + createExceptionList, + createExceptionListItem, + deleteExceptionList, +} from '../../../tasks/api_calls/exceptions'; + +describe('Exceptions viewer read only', () => { + const exceptionList = getExceptionList(); + + before(() => { + esArchiverResetKibana(); + // create rule with exceptions + createExceptionList(exceptionList, exceptionList.list_id).then((response) => { + createCustomRule( + { + ...getNewRule(), + customQuery: 'agent.name:*', + dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, + exceptionLists: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + }, + '2' + ); + }); + + login(ROLES.reader); + visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL, ROLES.reader); + goToRuleDetails(); + goToExceptionsTab(); + }); + + after(() => { + deleteAlertsAndRules(); + deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); + }); + + it('Cannot add an exception from empty viewer screen', () => { + // when no exceptions exist, empty component shows with action to add exception + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); + + // cannot add an exception from empty view + cy.get(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).should('have.attr', 'disabled'); + }); + + it('Cannot take actions on exception', () => { + createExceptionListItem(exceptionList.list_id, { + list_id: exceptionList.list_id, + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + name: 'Sample Exception List Item', + namespace_type: 'single', + entries: [ + { + field: 'unique_value.test', + operator: 'included', + type: 'match_any', + value: ['bar'], + }, + ], + }); + + goToAlertsTab(); + goToExceptionsTab(); + + // can view exceptions + cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist'); + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); + + // cannot access edit/delete actions of item + cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).should('have.attr', 'disabled'); + + // does not display add exception button + cy.get(ADD_EXCEPTIONS_BTN_FROM_VIEWER_HEADER).should('not.exist'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts deleted file mode 100644 index 686acd5fd048c..0000000000000 --- a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON, - OVERVIEW_RISKY_HOSTS_LINKS, - OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL, - OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL, - OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT, - OVERVIEW_RISKY_HOSTS_DOC_LINK, - OVERVIEW_RISKY_HOSTS_IMPORT_DASHBOARD_BUTTON, -} from '../../screens/overview'; - -import { login, visit } from '../../tasks/login'; -import { OVERVIEW_URL } from '../../urls/navigation'; -import { cleanKibana } from '../../tasks/common'; -import { changeSpace } from '../../tasks/kibana_navigation'; -import { createSpace, removeSpace } from '../../tasks/api_calls/spaces'; -import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; - -const testSpaceName = 'test'; - -describe('Risky Hosts Link Panel', () => { - before(() => { - cleanKibana(); - login(); - }); - - it('renders disabled panel view as expected', () => { - visit(OVERVIEW_URL); - cy.get(`${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL}`).should( - 'exist' - ); - cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts'); - cy.get(`${OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON}`).should('exist'); - cy.get(`${OVERVIEW_RISKY_HOSTS_DOC_LINK}`) - .should('have.attr', 'href') - .and('match', /host-risk-score.md/); - }); - - describe('enabled module', () => { - before(() => { - esArchiverLoad('risky_hosts'); - createSpace(testSpaceName); - }); - - after(() => { - esArchiverUnload('risky_hosts'); - removeSpace(testSpaceName); - }); - - it('renders disabled dashboard module as expected when there are no hosts in the selected time period', () => { - visit( - `${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))` - ); - cy.get( - `${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}` - ).should('exist'); - cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts'); - }); - - it('renders space aware dashboard module as expected when there are hosts in the selected time period', () => { - visit(OVERVIEW_URL); - cy.get( - `${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}` - ).should('not.exist'); - cy.get(`${OVERVIEW_RISKY_HOSTS_IMPORT_DASHBOARD_BUTTON}`).should('exist'); - cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 6 hosts'); - - changeSpace(testSpaceName); - cy.visit(`/s/${testSpaceName}${OVERVIEW_URL}`); - cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts'); - cy.get(`${OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON}`).should('exist'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index a02a318bfb307..1ca8ded946300 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -5,8 +5,6 @@ * 2.0. */ -export const ADD_EXCEPTIONS_BTN = '[data-test-subj="exceptionsHeaderAddExceptionBtn"]'; - export const CLOSE_ALERTS_CHECKBOX = '[data-test-subj="bulk-close-alert-on-add-add-exception-checkbox"]'; @@ -67,3 +65,20 @@ export const EXCEPTION_FLYOUT_VERSION_CONFLICT = '[data-test-subj="exceptionsFlyoutVersionConflict"]'; export const EXCEPTION_FLYOUT_LIST_DELETED_ERROR = '[data-test-subj="errorCalloutContainer"]'; + +// Exceptions all items view +export const NO_EXCEPTIONS_EXIST_PROMPT = + '[data-test-subj="exceptionItemViewerEmptyPrompts-empty-detection"]'; + +export const ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN = + '[data-test-subj="exceptionsEmptyPromptButton"]'; + +export const EXCEPTION_ITEM_VIEWER_CONTAINER = '[data-test-subj="exceptionItemContainer"]'; + +export const ADD_EXCEPTIONS_BTN_FROM_VIEWER_HEADER = + '[data-test-subj="exceptionsHeaderAddExceptionBtn"]'; + +export const NO_EXCEPTIONS_SEARCH_RESULTS_PROMPT = + '[data-test-subj="exceptionItemViewerEmptyPrompts-emptySearch"]'; + +export const EXCEPTION_ITEM_VIEWER_SEARCH = 'input[data-test-subj="exceptionsViewerSearchBar"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/overview.ts b/x-pack/plugins/security_solution/cypress/screens/overview.ts index 1e91e4fe462b9..14bfce599dfaf 100644 --- a/x-pack/plugins/security_solution/cypress/screens/overview.ts +++ b/x-pack/plugins/security_solution/cypress/screens/overview.ts @@ -151,19 +151,4 @@ export const OVERVIEW_CTI_LINKS_ERROR_INNER_PANEL = '[data-test-subj="cti-inner- export const OVERVIEW_CTI_TOTAL_EVENT_COUNT = `${OVERVIEW_CTI_LINKS} [data-test-subj="header-panel-subtitle"]`; export const OVERVIEW_CTI_ENABLE_MODULE_BUTTON = '[data-test-subj="cti-enable-module-button"]'; -export const OVERVIEW_RISKY_HOSTS_LINKS = '[data-test-subj="risky-hosts-dashboard-links"]'; -export const OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL = - '[data-test-subj="risky-hosts-inner-panel-danger"]'; -export const OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL = - '[data-test-subj="risky-hosts-inner-panel-warning"]'; -export const OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON = - '[data-test-subj="risky-hosts-view-dashboard-button"]'; -export const OVERVIEW_RISKY_HOSTS_IMPORT_DASHBOARD_BUTTON = - '[data-test-subj="create-saved-object-button"]'; -export const OVERVIEW_RISKY_HOSTS_DOC_LINK = - '[data-test-subj="risky-hosts-inner-panel-danger-learn-more"]'; -export const OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT = `${OVERVIEW_RISKY_HOSTS_LINKS} [data-test-subj="header-panel-subtitle"]`; -export const OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON = - '[data-test-subj="disabled-open-in-console-button-with-tooltip"]'; - export const OVERVIEW_ALERTS_HISTOGRAM = '[data-test-subj="alerts-histogram-panel"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index 989353cf7a253..80883d825c18f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -31,7 +31,7 @@ export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; export const DETAILS_TITLE = '.euiDescriptionList__title'; -export const EXCEPTIONS_TAB = '[data-test-subj="navigation-rule_exceptions"]'; +export const EXCEPTIONS_TAB = 'a[data-test-subj="navigation-rule_exceptions"]'; export const FALSE_POSITIVES_DETAILS = 'False positive examples'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index cb2fc257f7dc7..afee74e1a943b 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -218,6 +218,7 @@ export const createCustomRuleEnabled = ( query: rule.customQuery, language: 'kuery', enabled: true, + exceptions_list: rule.exceptionLists ?? [], tags: ['rule1'], max_signals: maxSignals, building_block_type: rule.buildingBlockType, @@ -243,6 +244,7 @@ export const createCustomRuleEnabled = ( query: rule.customQuery, language: 'kuery', enabled: true, + exceptions_list: rule.exceptionLists ?? [], tags: ['rule1'], max_signals: maxSignals, building_block_type: rule.buildingBlockType, diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index dcc4163fa9bf5..1cec924eae55a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -8,9 +8,11 @@ import type { Exception } from '../objects/exception'; import { RULE_STATUS } from '../screens/create_new_rule'; import { - ADD_EXCEPTIONS_BTN, + ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN, + ADD_EXCEPTIONS_BTN_FROM_VIEWER_HEADER, CLOSE_ALERTS_CHECKBOX, CONFIRM_BTN, + EXCEPTION_ITEM_VIEWER_SEARCH, FIELD_INPUT, LOADING_SPINNER, OPERATOR_INPUT, @@ -65,14 +67,48 @@ export const addsFieldsToTimeline = (search: string, fields: string[]) => { closeFieldsBrowser(); }; -export const openExceptionFlyoutFromRuleSettings = () => { - cy.get(ADD_EXCEPTIONS_BTN).click(); - cy.get(LOADING_SPINNER).should('not.exist'); - cy.get(FIELD_INPUT).should('be.visible'); +export const openExceptionFlyoutFromEmptyViewerPrompt = () => { + cy.root() + .pipe(($el) => { + $el.find(ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN).trigger('click'); + return $el.find(FIELD_INPUT); + }) + .should('be.visible'); +}; + +export const searchForExceptionItem = (query: string) => { + cy.get(EXCEPTION_ITEM_VIEWER_SEARCH).type(`${query}`).trigger('keydown', { + key: 'Enter', + keyCode: 13, + code: 'Enter', + type: 'keydown', + }); +}; + +export const addExceptionFlyoutFromViewerHeader = () => { + cy.root() + .pipe(($el) => { + $el.find(ADD_EXCEPTIONS_BTN_FROM_VIEWER_HEADER).trigger('click'); + return $el.find(FIELD_INPUT); + }) + .should('be.visible'); +}; + +export const addExceptionFromRuleDetails = (exception: Exception) => { + addExceptionFlyoutFromViewerHeader(); + cy.get(FIELD_INPUT).type(`${exception.field}{downArrow}{enter}`); + cy.get(OPERATOR_INPUT).type(`${exception.operator}{enter}`); + exception.values.forEach((value) => { + cy.get(VALUES_INPUT).type(`${value}{enter}`); + }); + cy.get(CLOSE_ALERTS_CHECKBOX).click({ force: true }); + cy.get(CONFIRM_BTN).click(); + cy.get(CONFIRM_BTN).should('have.attr', 'disabled'); + cy.get(CONFIRM_BTN).should('not.exist'); }; -export const addsExceptionFromRuleSettings = (exception: Exception) => { - openExceptionFlyoutFromRuleSettings(); +export const addFirstExceptionFromRuleDetails = (exception: Exception) => { + openExceptionFlyoutFromEmptyViewerPrompt(); cy.get(FIELD_INPUT).type(`${exception.field}{downArrow}{enter}`); cy.get(OPERATOR_INPUT).type(`${exception.operator}{enter}`); exception.values.forEach((value) => { @@ -89,13 +125,14 @@ export const goToAlertsTab = () => { }; export const goToExceptionsTab = () => { + cy.get(EXCEPTIONS_TAB).should('exist'); cy.get(EXCEPTIONS_TAB).click(); }; export const editException = () => { - cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).click({ force: true }); + cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).eq(0).click({ force: true }); - cy.get(EDIT_EXCEPTION_BTN).click({ force: true }); + cy.get(EDIT_EXCEPTION_BTN).eq(0).click({ force: true }); }; export const removeException = () => { diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 7dad742861ac0..6ac3a0aa7a3ff 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -42,7 +42,7 @@ import { NETWORK, OVERVIEW, POLICIES, - RESPONSE_ACTIONS, + ACTION_HISTORY, ENTITY_ANALYTICS, RULES, TIMELINES, @@ -65,7 +65,7 @@ import { NETWORK_PATH, OVERVIEW_PATH, POLICIES_PATH, - RESPONSE_ACTIONS_PATH, + ACTION_HISTORY_PATH, ENTITY_ANALYTICS_PATH, RULES_CREATE_PATH, RULES_PATH, @@ -514,13 +514,13 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ path: BLOCKLIST_PATH, }, { - ...getSecuritySolutionLink('benchmarks'), - deepLinks: [getSecuritySolutionLink('rules')], + id: SecurityPageName.actionHistory, + title: ACTION_HISTORY, + path: ACTION_HISTORY_PATH, }, { - id: SecurityPageName.responseActions, - title: RESPONSE_ACTIONS, - path: RESPONSE_ACTIONS_PATH, + ...getSecuritySolutionLink('benchmarks'), + deepLinks: [getSecuritySolutionLink('rules')], }, ], }, diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts index 88ef1152b8f34..ae7c15c73a4d2 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts @@ -30,7 +30,7 @@ import { APP_USERS_PATH, APP_KUBERNETES_PATH, APP_LANDING_PATH, - APP_RESPONSE_ACTIONS_PATH, + APP_ACTION_HISTORY_PATH, APP_ENTITY_ANALYTICS_PATH, APP_PATH, } from '../../../common/constants'; @@ -162,10 +162,10 @@ export const navTabs: SecurityNav = { disabled: false, urlKey: 'administration', }, - [SecurityPageName.responseActions]: { - id: SecurityPageName.responseActions, - name: i18n.RESPONSE_ACTIONS, - href: APP_RESPONSE_ACTIONS_PATH, + [SecurityPageName.actionHistory]: { + id: SecurityPageName.actionHistory, + name: i18n.ACTION_HISTORY, + href: APP_ACTION_HISTORY_PATH, disabled: false, urlKey: 'administration', }, diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts index 400642bc1490d..154127f469c96 100644 --- a/x-pack/plugins/security_solution/public/app/translations.ts +++ b/x-pack/plugins/security_solution/public/app/translations.ts @@ -120,12 +120,9 @@ export const BLOCKLIST = i18n.translate('xpack.securitySolution.navigation.block defaultMessage: 'Blocklist', }); -export const RESPONSE_ACTIONS = i18n.translate( - 'xpack.securitySolution.navigation.responseActions', - { - defaultMessage: 'Response Actions', - } -); +export const ACTION_HISTORY = i18n.translate('xpack.securitySolution.navigation.actionHistory', { + defaultMessage: 'Action history', +}); export const CREATE_NEW_RULE = i18n.translate('xpack.securitySolution.navigation.newRuleTitle', { defaultMessage: 'Create new rule', diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx index 945317036e7bc..0c6cf454e73ee 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx @@ -11,10 +11,11 @@ import { render } from '@testing-library/react'; import { TestProviders } from '../../../mock'; import { NO_HOST_RISK_DATA_DESCRIPTION } from './translations'; import { HostRiskSummary } from './host_risk_summary'; +import { RiskSeverity } from '../../../../../common/search_strategy'; describe('HostRiskSummary', () => { it('renders host risk data', () => { - const riskKeyword = 'test risk'; + const riskSeverity = RiskSeverity.low; const hostRisk = { loading: false, isModuleEnabled: true, @@ -23,11 +24,12 @@ describe('HostRiskSummary', () => { '@timestamp': '1641902481', host: { name: 'test-host-name', - }, - risk: riskKeyword, - risk_stats: { - risk_score: 9999, - rule_risks: [], + risk: { + multipliers: [], + calculated_score_norm: 9999, + calculated_level: riskSeverity, + rule_risks: [], + }, }, }, ], @@ -39,7 +41,7 @@ describe('HostRiskSummary', () => { ); - expect(getByText(riskKeyword)).toBeInTheDocument(); + expect(getByText(riskSeverity)).toBeInTheDocument(); }); it('renders spinner when loading', () => { @@ -67,11 +69,12 @@ describe('HostRiskSummary', () => { '@timestamp': '1641902530', host: { name: 'test-host-name', - }, - risk: 'test-risk', - risk_stats: { - risk_score: 9999, - rule_risks: [], + risk: { + multipliers: [], + calculated_score_norm: 9999, + calculated_level: RiskSeverity.low, + rule_risks: [], + }, }, }, ], diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx index 078fb0e1442cd..970656933b938 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx @@ -9,11 +9,10 @@ import React from 'react'; import { EuiLoadingSpinner, EuiPanel, EuiSpacer, EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import * as i18n from './translations'; -import { RISKY_HOSTS_DOC_LINK } from '../../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view'; import { RiskScore } from '../../severity/common'; -import type { RiskSeverity } from '../../../../../common/search_strategy'; import type { HostRisk } from '../../../../risk_score/containers'; +import { RISKY_HOSTS_DOC_LINK } from '../../../../../common/constants'; const HostRiskSummaryComponent: React.FC<{ hostRisk: HostRisk; @@ -25,12 +24,12 @@ const HostRiskSummaryComponent: React.FC<{ toolTipContent={ @@ -56,7 +55,10 @@ const HostRiskSummaryComponent: React.FC<{ + } /> diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx index 5148dde4d6b59..4e9ff49a2b1dd 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/investigation_guide_view.tsx @@ -7,10 +7,11 @@ import { EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; - -import React, { useMemo } from 'react'; +import React, { createContext, useMemo } from 'react'; import styled from 'styled-components'; +import type { GetBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; import * as i18n from './translations'; import { useRuleWithFallback } from '../../../detections/containers/detection_engine/rules/use_rule_with_fallback'; import { MarkdownRenderer } from '../markdown_editor'; @@ -22,6 +23,8 @@ export const Indent = styled.div` word-break: break-word; `; +export const BasicAlertDataContext = createContext>({}); + const InvestigationGuideViewComponent: React.FC<{ data: TimelineEventsDetailsItem[]; }> = ({ data }) => { @@ -32,13 +35,14 @@ const InvestigationGuideViewComponent: React.FC<{ : item?.originalValue ?? null; }, [data]); const { rule: maybeRule } = useRuleWithFallback(ruleId); + const basicAlertData = useBasicDataFromDetailsData(data); if (!maybeRule?.note) { return null; } return ( - <> +
{i18n.INVESTIGATION_GUIDE}
@@ -51,7 +55,7 @@ const InvestigationGuideViewComponent: React.FC<{ - +
); }; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts deleted file mode 100644 index 2372e063b48cf..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 DETECTION_LIST = i18n.translate( - 'xpack.securitySolution.exceptions.detectionListLabel', - { - defaultMessage: 'Detection list', - } -); - -export const ENDPOINT_LIST = i18n.translate('xpack.securitySolution.exceptions.endpointListLabel', { - defaultMessage: 'Endpoint list', -}); - -export const EDIT = i18n.translate('xpack.securitySolution.exceptions.editButtonLabel', { - defaultMessage: 'Edit', -}); - -export const REMOVE = i18n.translate('xpack.securitySolution.exceptions.removeButtonLabel', { - defaultMessage: 'Remove', -}); - -export const COMMENTS_SHOW = (comments: number) => - i18n.translate('xpack.securitySolution.exceptions.showCommentsLabel', { - values: { comments }, - defaultMessage: 'Show ({comments}) {comments, plural, =1 {Comment} other {Comments}}', - }); - -export const COMMENTS_HIDE = (comments: number) => - i18n.translate('xpack.securitySolution.exceptions.hideCommentsLabel', { - values: { comments }, - defaultMessage: 'Hide ({comments}) {comments, plural, =1 {Comment} other {Comments}}', - }); - -export const NAME = i18n.translate('xpack.securitySolution.exceptions.nameLabel', { - defaultMessage: 'Name', -}); - -export const COMMENT = i18n.translate('xpack.securitySolution.exceptions.commentLabel', { - defaultMessage: 'Comment', -}); - -export const COMMENT_EVENT = i18n.translate('xpack.securitySolution.exceptions.commentEventLabel', { - defaultMessage: 'added a comment', -}); - -export const OPERATING_SYSTEM_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.operatingSystemFullLabel', - { - defaultMessage: 'Operating System', - } -); - -export const SEARCH_DEFAULT = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder', - { - defaultMessage: 'Search field (ex: host.name)', - } -); - -export const ADD_EXCEPTION_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.addExceptionLabel', - { - defaultMessage: 'Add new exception', - } -); - -export const ADD_TO_ENDPOINT_LIST = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.addToEndpointListLabel', - { - defaultMessage: 'Add Endpoint exception', - } -); - -export const ADD_TO_DETECTIONS_LIST = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel', - { - defaultMessage: 'Add rule exception', - } -); - -export const EXCEPTION_EMPTY_PROMPT_TITLE = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.emptyPromptTitle', - { - defaultMessage: 'This rule has no exceptions', - } -); - -export const EXCEPTION_NO_SEARCH_RESULTS_PROMPT_BODY = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody', - { - defaultMessage: 'No search results found.', - } -); - -export const EXCEPTION_EMPTY_PROMPT_BODY = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.emptyPromptBody', - { - defaultMessage: - 'You can add exceptions to fine tune the rule so that detection alerts are not created when exception conditions are met. Exceptions improve detection accuracy, which can help reduce the number of false positives.', - } -); - -export const FETCH_LIST_ERROR = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.fetchingListError', - { - defaultMessage: 'Error fetching exceptions', - } -); - -export const DELETE_EXCEPTION_ERROR = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.deleteExceptionError', - { - defaultMessage: 'Error deleting exception', - } -); - -export const ITEMS_PER_PAGE = (items: number) => - i18n.translate('xpack.securitySolution.exceptions.exceptionsPaginationLabel', { - values: { items }, - defaultMessage: 'Items per page: {items}', - }); - -export const NUMBER_OF_ITEMS = (items: number) => - i18n.translate('xpack.securitySolution.exceptions.paginationNumberOfItemsLabel', { - values: { items }, - defaultMessage: '{items} items', - }); - -export const REFRESH = i18n.translate('xpack.securitySolution.exceptions.utilityRefreshLabel', { - defaultMessage: 'Refresh', -}); - -export const SHOWING_EXCEPTIONS = (items: number) => - i18n.translate('xpack.securitySolution.exceptions.utilityNumberExceptionsLabel', { - values: { items }, - defaultMessage: 'Showing {items} {items, plural, =1 {exception} other {exceptions}}', - }); - -export const FIELD = i18n.translate('xpack.securitySolution.exceptions.fieldDescription', { - defaultMessage: 'Field', -}); - -export const OPERATOR = i18n.translate('xpack.securitySolution.exceptions.operatorDescription', { - defaultMessage: 'Operator', -}); - -export const VALUE = i18n.translate('xpack.securitySolution.exceptions.valueDescription', { - defaultMessage: 'Value', -}); - -export const AND = i18n.translate('xpack.securitySolution.exceptions.andDescription', { - defaultMessage: 'AND', -}); - -export const OR = i18n.translate('xpack.securitySolution.exceptions.orDescription', { - defaultMessage: 'OR', -}); - -export const ADD_COMMENT_PLACEHOLDER = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.addCommentPlaceholder', - { - defaultMessage: 'Add a new comment...', - } -); - -export const ADD_TO_CLIPBOARD = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.addToClipboard', - { - defaultMessage: 'Comment', - } -); - -export const DESCRIPTION = i18n.translate('xpack.securitySolution.exceptions.descriptionLabel', { - defaultMessage: 'Description', -}); - -export const TOTAL_ITEMS_FETCH_ERROR = i18n.translate( - 'xpack.securitySolution.exceptions.viewer.fetchTotalsError', - { - defaultMessage: 'Error getting exception item totals', - } -); - -export const CLEAR_EXCEPTIONS_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.clearExceptionsLabel', - { - defaultMessage: 'Remove Exception List', - } -); - -export const ADD_EXCEPTION_FETCH_404_ERROR = (listId: string) => - i18n.translate('xpack.securitySolution.exceptions.fetch404Error', { - values: { listId }, - defaultMessage: - 'The associated exception list ({listId}) no longer exists. Please remove the missing exception list to add additional exceptions to the detection rule.', - }); - -export const ADD_EXCEPTION_FETCH_ERROR = i18n.translate( - 'xpack.securitySolution.exceptions.fetchError', - { - defaultMessage: 'Error fetching exception list', - } -); - -export const ERROR = i18n.translate('xpack.securitySolution.exceptions.errorLabel', { - defaultMessage: 'Error', -}); - -export const CANCEL = i18n.translate('xpack.securitySolution.exceptions.cancelLabel', { - defaultMessage: 'Cancel', -}); - -export const MODAL_ERROR_ACCORDION_TEXT = i18n.translate( - 'xpack.securitySolution.exceptions.modalErrorAccordionText', - { - defaultMessage: 'Show rule reference information:', - } -); - -export const DISSASOCIATE_LIST_SUCCESS = (id: string) => - i18n.translate('xpack.securitySolution.exceptions.dissasociateListSuccessText', { - values: { id }, - defaultMessage: 'Exception list ({id}) has successfully been removed', - }); - -export const DISSASOCIATE_EXCEPTION_LIST_ERROR = i18n.translate( - 'xpack.securitySolution.exceptions.dissasociateExceptionListError', - { - defaultMessage: 'Failed to remove exception list', - } -); - -export const OPERATING_SYSTEM_WINDOWS = i18n.translate( - 'xpack.securitySolution.exceptions.operatingSystemWindows', - { - defaultMessage: 'Windows', - } -); - -export const OPERATING_SYSTEM_MAC = i18n.translate( - 'xpack.securitySolution.exceptions.operatingSystemMac', - { - defaultMessage: 'macOS', - } -); - -export const OPERATING_SYSTEM_WINDOWS_AND_MAC = i18n.translate( - 'xpack.securitySolution.exceptions.operatingSystemWindowsAndMac', - { - defaultMessage: 'Windows and macOS', - } -); - -export const OPERATING_SYSTEM_LINUX = i18n.translate( - 'xpack.securitySolution.exceptions.operatingSystemLinux', - { - defaultMessage: 'Linux', - } -); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx deleted file mode 100644 index b5a24ef3e472d..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx +++ /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 React from 'react'; -import { mount } from 'enzyme'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; - -import { TestProviders } from '../../../../mock'; -import { ExceptionItemCardMetaInfo } from './exception_item_card_meta'; - -describe('ExceptionItemCardMetaInfo', () => { - it('it renders item creation info', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value1"]').at(0).text() - ).toEqual('Apr 20, 2020 @ 15:25:31.830'); - expect( - wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value2"]').at(0).text() - ).toEqual('some user'); - }); - - it('it renders item update info', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value1"]').at(0).text() - ).toEqual('Apr 20, 2020 @ 15:25:31.830'); - expect( - wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value2"]').at(0).text() - ).toEqual('some user'); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx deleted file mode 100644 index 3e9cf5e68d95e..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 } from 'react'; -import { EuiAvatar, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import styled from 'styled-components'; - -import * as i18n from './translations'; -import { FormattedDate, FormattedRelativePreferenceDate } from '../../../formatted_date'; - -const StyledCondition = styled('div')` - padding-top: 4px !important; -`; -export interface ExceptionItemCardMetaInfoProps { - item: ExceptionListItemSchema; - dataTestSubj: string; -} - -export const ExceptionItemCardMetaInfo = memo( - ({ item, dataTestSubj }) => { - return ( - - - } - value2={item.created_by} - dataTestSubj={`${dataTestSubj}-createdBy`} - /> - - - - - - } - value2={item.updated_by} - dataTestSubj={`${dataTestSubj}-updatedBy`} - /> - - - ); - } -); -ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; - -interface MetaInfoDetailsProps { - fieldName: string; - label: string; - value1: JSX.Element | string; - value2: string; - dataTestSubj: string; -} - -const MetaInfoDetails = memo(({ label, value1, value2, dataTestSubj }) => { - return ( - - - - {label} - - - - - {value1} - - - - - {i18n.EXCEPTION_ITEM_META_BY} - - - - - - - - - - {value2} - - - - - - ); -}); - -MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.test.tsx deleted file mode 100644 index 4d38fac340727..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.test.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import { ExceptionsViewerPagination } from './exceptions_pagination'; - -describe('ExceptionsViewerPagination', () => { - it('it renders passed in "pageSize" as selected option', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="exceptionsPerPageBtn"]').at(0).text()).toEqual( - 'Items per page: 50' - ); - }); - - it('it renders all passed in page size options when per page button clicked', () => { - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsPerPageBtn"] button').simulate('click'); - - expect(wrapper.find('button[data-test-subj="exceptionsPerPageItem"]').at(0).text()).toEqual( - '20 items' - ); - expect(wrapper.find('button[data-test-subj="exceptionsPerPageItem"]').at(1).text()).toEqual( - '50 items' - ); - expect(wrapper.find('button[data-test-subj="exceptionsPerPageItem"]').at(2).text()).toEqual( - '100 items' - ); - }); - - it('it invokes "onPaginationChange" when per page item is clicked', () => { - const mockOnPaginationChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsPerPageBtn"] button').simulate('click'); - wrapper.find('button[data-test-subj="exceptionsPerPageItem"]').at(0).simulate('click'); - - expect(mockOnPaginationChange).toHaveBeenCalledWith({ - pagination: { pageIndex: 0, pageSize: 20, totalItemCount: 1 }, - }); - }); - - it('it renders correct total page count', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="exceptionsPagination"]').at(0).prop('pageCount')).toEqual( - 4 - ); - expect( - wrapper.find('[data-test-subj="exceptionsPagination"]').at(0).prop('activePage') - ).toEqual(0); - }); - - it('it invokes "onPaginationChange" when next clicked', () => { - const mockOnPaginationChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="pagination-button-next"]').at(1).simulate('click'); - - expect(mockOnPaginationChange).toHaveBeenCalledWith({ - pagination: { pageIndex: 1, pageSize: 50, totalItemCount: 160 }, - }); - }); - - it('it invokes "onPaginationChange" when page clicked', () => { - const mockOnPaginationChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('button[data-test-subj="pagination-button-3"]').simulate('click'); - - expect(mockOnPaginationChange).toHaveBeenCalledWith({ - pagination: { pageIndex: 3, pageSize: 50, totalItemCount: 160 }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.tsx deleted file mode 100644 index c64130e7eb56d..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_pagination.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { ReactElement } from 'react'; -import React, { useCallback, useState, useMemo } from 'react'; -import { - EuiContextMenuItem, - EuiButtonEmpty, - EuiPagination, - EuiFlexItem, - EuiFlexGroup, - EuiPopover, - EuiContextMenuPanel, -} from '@elastic/eui'; - -import * as i18n from '../translations'; -import type { ExceptionsPagination, Filter } from '../types'; - -interface ExceptionsViewerPaginationProps { - pagination: ExceptionsPagination; - onPaginationChange: (arg: Partial) => void; -} - -const ExceptionsViewerPaginationComponent = ({ - pagination, - onPaginationChange, -}: ExceptionsViewerPaginationProps): JSX.Element => { - const [isOpen, setIsOpen] = useState(false); - - const handleClosePerPageMenu = useCallback((): void => setIsOpen(false), [setIsOpen]); - - const handlePerPageMenuClick = useCallback( - (): void => setIsOpen((isPopoverOpen) => !isPopoverOpen), - [setIsOpen] - ); - - const handlePageClick = useCallback( - (pageIndex: number): void => { - onPaginationChange({ - pagination: { - pageIndex, - pageSize: pagination.pageSize, - totalItemCount: pagination.totalItemCount, - }, - }); - }, - [pagination, onPaginationChange] - ); - - const items = useMemo((): ReactElement[] => { - return pagination.pageSizeOptions.map((rows) => ( - { - onPaginationChange({ - pagination: { - pageIndex: 0, - pageSize: rows, - totalItemCount: pagination.totalItemCount, - }, - }); - handleClosePerPageMenu(); - }} - data-test-subj="exceptionsPerPageItem" - > - {i18n.NUMBER_OF_ITEMS(rows)} - - )); - }, [pagination, onPaginationChange, handleClosePerPageMenu]); - - const totalPages = useMemo((): number => { - if (pagination.totalItemCount > 0) { - return Math.ceil(pagination.totalItemCount / pagination.pageSize); - } else { - return 1; - } - }, [pagination]); - - return ( - - - - {i18n.ITEMS_PER_PAGE(pagination.pageSize)} - - } - isOpen={isOpen} - closePopover={handleClosePerPageMenu} - panelPaddingSize="none" - repositionOnScroll - > - - - - - - - - - ); -}; - -ExceptionsViewerPaginationComponent.displayName = 'ExceptionsViewerPaginationComponent'; - -export const ExceptionsViewerPagination = React.memo(ExceptionsViewerPaginationComponent); - -ExceptionsViewerPagination.displayName = 'ExceptionsViewerPagination'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.test.tsx deleted file mode 100644 index f600fe28f3ecb..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.test.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; - -import { ExceptionsViewerUtility } from './exceptions_utility'; -import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { - euiBreakpoints: { - l: '1200px', - }, - euiSizeM: '10px', - euiBorderThin: '1px solid #ece', - }, -}); - -describe('ExceptionsViewerUtility', () => { - it('it renders correct pluralized text when more than one exception exists', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsShowing"]').at(0).text()).toEqual( - 'Showing 2 exceptions' - ); - }); - - it('it renders correct singular text when less than two exceptions exists', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsShowing"]').at(0).text()).toEqual( - 'Showing 1 exception' - ); - }); - - it('it invokes "onRefreshClick" when refresh button clicked', () => { - const mockOnRefreshClick = jest.fn(); - const wrapper = mountWithIntl( - - - - ); - - wrapper.find('[data-test-subj="exceptionsRefresh"] button').simulate('click'); - - expect(mockOnRefreshClick).toHaveBeenCalledTimes(1); - }); - - it('it does not render any messages when "showEndpointList" and "showDetectionsList" are "false"', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEndpointMessage"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="exceptionsDetectionsMessage"]').exists()).toBeFalsy(); - }); - - it('it does render detections messages when "showDetectionsList" is "true"', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEndpointMessage"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="exceptionsDetectionsMessage"]').exists()).toBeTruthy(); - }); - - it('it does render endpoint messages when "showEndpointList" is "true"', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEndpointMessage"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="exceptionsDetectionsMessage"]').exists()).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.tsx deleted file mode 100644 index fd720377a1b1e..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_utility.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiText, EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import styled from 'styled-components'; - -import * as i18n from '../translations'; -import type { ExceptionsPagination } from '../types'; -import { - UtilityBar, - UtilityBarSection, - UtilityBarGroup, - UtilityBarText, - UtilityBarAction, -} from '../../utility_bar'; - -const StyledText = styled(EuiText)` - font-style: italic; -`; - -const MyUtilities = styled(EuiFlexGroup)` - height: 50px; -`; - -interface ExceptionsViewerUtilityProps { - pagination: ExceptionsPagination; - showEndpointListsOnly: boolean; - showDetectionsListsOnly: boolean; - ruleSettingsUrl: string; - onRefreshClick: () => void; -} - -const ExceptionsViewerUtilityComponent: React.FC = ({ - pagination, - showEndpointListsOnly, - showDetectionsListsOnly, - ruleSettingsUrl, - onRefreshClick, -}): JSX.Element => ( - - - - - - - {i18n.SHOWING_EXCEPTIONS(pagination.totalItemCount ?? 0)} - - - - - - {i18n.REFRESH} - - - - - - - - {showEndpointListsOnly && ( - - - - ), - }} - /> - )} - {showDetectionsListsOnly && ( - - - - ), - }} - /> - )} - - - -); - -ExceptionsViewerUtilityComponent.displayName = 'ExceptionsViewerUtilityComponent'; - -export const ExceptionsViewerUtility = React.memo(ExceptionsViewerUtilityComponent); - -ExceptionsViewerUtility.displayName = 'ExceptionsViewerUtility'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx deleted file mode 100644 index 05cbe352fa72e..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.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 { storiesOf, addDecorator } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { euiLightVars } from '@kbn/ui-theme'; - -import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { ExceptionsViewerHeader } from './exceptions_viewer_header'; - -addDecorator((storyFn) => ( - ({ eui: euiLightVars, darkMode: false })}>{storyFn()} -)); - -storiesOf('Components/ExceptionsViewerHeader', module) - .add('loading', () => { - return ( - - ); - }) - .add('all lists', () => { - return ( - - ); - }) - .add('endpoint only', () => { - return ( - - ); - }) - .add('detections only', () => { - return ( - - ); - }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.test.tsx deleted file mode 100644 index d19a81e222423..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.test.tsx +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import { ExceptionsViewerHeader } from './exceptions_viewer_header'; - -import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; - -describe('ExceptionsViewerHeader', () => { - it('it renders all disabled if "isInitLoading" is true', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find('input[data-test-subj="exceptionsHeaderSearch"]').at(0).prop('disabled') - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="exceptionsDetectionFilterBtn"] button').at(0).prop('disabled') - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="exceptionsEndpointFilterBtn"] button').at(0).prop('disabled') - ).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="exceptionsHeaderAddExceptionPopoverBtn"] button') - .at(0) - .prop('disabled') - ).toBeTruthy(); - }); - - // This occurs if user does not have sufficient privileges - it('it does not display add exception button if no list types available', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"]').exists()).toBeFalsy(); - }); - - it('it displays toggles and add exception popover when more than one list type available', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="exceptionsFilterGroupBtns"]').exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionPopoverBtn"]').exists() - ).toBeTruthy(); - }); - - it('it does not display toggles and add exception popover if only one list type is available', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="exceptionsFilterGroupBtns"]')).toHaveLength(0); - expect(wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionPopoverBtn"]')).toHaveLength( - 0 - ); - }); - - it('it displays add exception button without popover if only one list type is available', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"]').exists() - ).toBeTruthy(); - }); - - it('it renders detections filter toggle selected when clicked', () => { - const mockOnFilterChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsDetectionFilterBtn"] button').simulate('click'); - - expect( - wrapper - .find('EuiFilterButton[data-test-subj="exceptionsDetectionFilterBtn"]') - .at(0) - .prop('hasActiveFilters') - ).toBeTruthy(); - expect( - wrapper - .find('EuiFilterButton[data-test-subj="exceptionsEndpointFilterBtn"]') - .at(0) - .prop('hasActiveFilters') - ).toBeFalsy(); - expect(mockOnFilterChange).toHaveBeenCalledWith({ - filter: { - filter: '', - tags: [], - }, - pagination: { - pageIndex: 0, - }, - showDetectionsListsOnly: true, - showEndpointListsOnly: false, - }); - }); - - it('it renders endpoint filter toggle selected and invokes "onFilterChange" when clicked', () => { - const mockOnFilterChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsEndpointFilterBtn"] button').simulate('click'); - - expect( - wrapper - .find('EuiFilterButton[data-test-subj="exceptionsEndpointFilterBtn"]') - .at(0) - .prop('hasActiveFilters') - ).toBeTruthy(); - expect( - wrapper - .find('EuiFilterButton[data-test-subj="exceptionsDetectionFilterBtn"]') - .at(0) - .prop('hasActiveFilters') - ).toBeFalsy(); - expect(mockOnFilterChange).toHaveBeenCalledWith({ - filter: { - filter: '', - tags: [], - }, - pagination: { - pageIndex: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: true, - }); - }); - - it('it invokes "onAddExceptionClick" when user selects to add an exception item and only endpoint exception lists are available', () => { - const mockOnAddExceptionClick = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"] button').simulate('click'); - - expect(mockOnAddExceptionClick).toHaveBeenCalledTimes(1); - }); - - it('it invokes "onAddDetectionsExceptionClick" when user selects to add an exception item and only endpoint detections lists are available', () => { - const mockOnAddExceptionClick = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"] button').simulate('click'); - - expect(mockOnAddExceptionClick).toHaveBeenCalledTimes(1); - }); - - it('it invokes "onAddEndpointExceptionClick" when user selects to add an exception item to endpoint list from popover', () => { - const mockOnAddExceptionClick = jest.fn(); - const wrapper = mount( - - ); - - wrapper - .find('[data-test-subj="exceptionsHeaderAddExceptionPopoverBtn"] button') - .simulate('click'); - wrapper.find('[data-test-subj="addEndpointExceptionBtn"] button').simulate('click'); - - expect(mockOnAddExceptionClick).toHaveBeenCalledTimes(1); - }); - - it('it invokes "onAddDetectionsExceptionClick" when user selects to add an exception item to endpoint list from popover', () => { - const mockOnAddExceptionClick = jest.fn(); - const wrapper = mount( - - ); - - wrapper - .find('[data-test-subj="exceptionsHeaderAddExceptionPopoverBtn"] button') - .simulate('click'); - wrapper.find('[data-test-subj="addDetectionsExceptionBtn"] button').simulate('click'); - - expect(mockOnAddExceptionClick).toHaveBeenCalledTimes(1); - }); - - it('it invokes "onFilterChange" when search used and "Enter" pressed', () => { - const mockOnFilterChange = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('EuiFieldSearch').at(0).simulate('keyup', { - charCode: 13, - code: 'Enter', - key: 'Enter', - }); - - expect(mockOnFilterChange).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.tsx deleted file mode 100644 index dc234dc0a7ef4..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.tsx +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { EuiContextMenuPanelDescriptor } from '@elastic/eui'; -import { - EuiFieldSearch, - EuiFlexGroup, - EuiFlexItem, - EuiPopover, - EuiContextMenu, - EuiButton, - EuiFilterGroup, - EuiFilterButton, -} from '@elastic/eui'; -import React, { useEffect, useState, useCallback, useMemo } from 'react'; - -import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import * as i18n from '../translations'; -import type { Filter } from '../types'; - -interface ExceptionsViewerHeaderProps { - isInitLoading: boolean; - supportedListTypes: ExceptionListTypeEnum[]; - detectionsListItems: number; - endpointListItems: number; - onFilterChange: (arg: Partial) => void; - onAddExceptionClick: (type: ExceptionListTypeEnum) => void; -} - -/** - * Collection of filters and toggles for filtering exception items. - */ -const ExceptionsViewerHeaderComponent = ({ - isInitLoading, - supportedListTypes, - detectionsListItems, - endpointListItems, - onFilterChange, - onAddExceptionClick, -}: ExceptionsViewerHeaderProps): JSX.Element => { - const [filter, setFilter] = useState(''); - const [tags, setTags] = useState([]); - const [showDetectionsListsOnly, setShowDetectionsList] = useState(false); - const [showEndpointListsOnly, setShowEndpointList] = useState(false); - const [isAddExceptionMenuOpen, setAddExceptionMenuOpen] = useState(false); - - useEffect((): void => { - onFilterChange({ - filter: { filter, tags }, - pagination: { - pageIndex: 0, - }, - showDetectionsListsOnly, - showEndpointListsOnly, - }); - }, [filter, tags, showDetectionsListsOnly, showEndpointListsOnly, onFilterChange]); - - const onAddExceptionDropdownClick = useCallback( - (): void => setAddExceptionMenuOpen(!isAddExceptionMenuOpen), - [setAddExceptionMenuOpen, isAddExceptionMenuOpen] - ); - - const handleDetectionsListClick = useCallback((): void => { - setShowDetectionsList(!showDetectionsListsOnly); - setShowEndpointList(false); - }, [showDetectionsListsOnly, setShowDetectionsList, setShowEndpointList]); - - const handleEndpointListClick = useCallback((): void => { - setShowEndpointList(!showEndpointListsOnly); - setShowDetectionsList(false); - }, [showEndpointListsOnly, setShowEndpointList, setShowDetectionsList]); - - const handleOnSearch = useCallback( - (searchValue: string): void => { - const tagsRegex = /(tags:[^\s]*)/i; - const tagsMatch = searchValue.match(tagsRegex); - const foundTags: string = tagsMatch != null ? tagsMatch[0].split(':')[1] : ''; - const filterString = tagsMatch != null ? searchValue.replace(tagsRegex, '') : searchValue; - - if (foundTags.length > 0) { - setTags(foundTags.split(',')); - } - - setFilter(filterString.trim()); - }, - [setTags, setFilter] - ); - - const onAddException = useCallback( - (type: ExceptionListTypeEnum): void => { - onAddExceptionClick(type); - setAddExceptionMenuOpen(false); - }, - [onAddExceptionClick, setAddExceptionMenuOpen] - ); - - const addExceptionButtonOptions = useMemo( - (): EuiContextMenuPanelDescriptor[] => [ - { - id: 0, - items: [ - { - name: i18n.ADD_TO_ENDPOINT_LIST, - onClick: () => onAddException(ExceptionListTypeEnum.ENDPOINT), - 'data-test-subj': 'addEndpointExceptionBtn', - }, - { - name: i18n.ADD_TO_DETECTIONS_LIST, - onClick: () => onAddException(ExceptionListTypeEnum.DETECTION), - 'data-test-subj': 'addDetectionsExceptionBtn', - }, - ], - }, - ], - [onAddException] - ); - - return ( - - - - - - {supportedListTypes.length === 1 && ( - - onAddException(supportedListTypes[0])} - isDisabled={isInitLoading} - fill - > - {i18n.ADD_EXCEPTION_LABEL} - - - )} - - {supportedListTypes.length > 1 && ( - - - - - - {i18n.DETECTION_LIST} - {detectionsListItems != null ? ` (${detectionsListItems})` : ''} - - - {i18n.ENDPOINT_LIST} - {endpointListItems != null ? ` (${endpointListItems})` : ''} - - - - - - - {i18n.ADD_EXCEPTION_LABEL} - - } - isOpen={isAddExceptionMenuOpen} - closePopover={onAddExceptionDropdownClick} - anchorPosition="downCenter" - panelPaddingSize="none" - repositionOnScroll - > - - - - - - )} - - ); -}; - -ExceptionsViewerHeaderComponent.displayName = 'ExceptionsViewerHeaderComponent'; - -export const ExceptionsViewerHeader = React.memo(ExceptionsViewerHeaderComponent); - -ExceptionsViewerHeader.displayName = 'ExceptionsViewerHeader'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx deleted file mode 100644 index 22c6e7dbf8ecf..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { mount } from 'enzyme'; - -import * as i18n from '../translations'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { ExceptionsViewerItems } from './exceptions_viewer_items'; -import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; -import { TestProviders } from '../../../mock'; - -const mockTheme = getMockTheme({ - eui: { - euiSize: '10px', - euiColorPrimary: '#ece', - euiColorDanger: '#ece', - }, -}); - -describe('ExceptionsViewerItems', () => { - it('it renders empty prompt if "showEmpty" is "true"', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptTitle"]').last().text()).toEqual( - i18n.EXCEPTION_EMPTY_PROMPT_TITLE - ); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').text()).toEqual( - i18n.EXCEPTION_EMPTY_PROMPT_BODY - ); - }); - - it('it renders no search results found prompt if "showNoResults" is "true"', () => { - const wrapper = mount( - - - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptTitle"]').last().text()).toEqual(''); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').text()).toEqual( - i18n.EXCEPTION_NO_SEARCH_RESULTS_PROMPT_BODY - ); - }); - - it('it renders exceptions if "showEmpty" and "isInitLoading" is "false", and exceptions exist', () => { - const wrapper = mount( - - - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeFalsy(); - }); - - it('it does not render exceptions if "isInitLoading" is "true"', () => { - const wrapper = mount( - - - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx deleted file mode 100644 index e1d91ed0a0580..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; - -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import * as i18n from '../translations'; -import { ExceptionItemCard } from './exception_item_card'; -import type { ExceptionListItemIdentifiers } from '../types'; - -const MyFlexItem = styled(EuiFlexItem)` - margin: ${({ theme }) => `${theme.eui.euiSize} 0`}; - - &:first-child { - margin: ${({ theme }) => `${theme.eui.euiSizeXS} 0 ${theme.eui.euiSize}`}; - } -`; - -const MyExceptionItemContainer = styled(EuiFlexGroup)` - margin: ${({ theme }) => `0 ${theme.eui.euiSize} ${theme.eui.euiSize} 0`}; -`; - -interface ExceptionsViewerItemsProps { - showEmpty: boolean; - showNoResults: boolean; - isInitLoading: boolean; - disableActions: boolean; - exceptions: ExceptionListItemSchema[]; - loadingItemIds: ExceptionListItemIdentifiers[]; - onDeleteException: (arg: ExceptionListItemIdentifiers) => void; - onEditExceptionItem: (item: ExceptionListItemSchema) => void; -} - -const ExceptionsViewerItemsComponent: React.FC = ({ - showEmpty, - showNoResults, - isInitLoading, - exceptions, - loadingItemIds, - onDeleteException, - onEditExceptionItem, - disableActions, -}): JSX.Element => ( - - {showEmpty || showNoResults || isInitLoading ? ( - - - {showNoResults ? '' : i18n.EXCEPTION_EMPTY_PROMPT_TITLE} - - } - body={ -

- {showNoResults - ? i18n.EXCEPTION_NO_SEARCH_RESULTS_PROMPT_BODY - : i18n.EXCEPTION_EMPTY_PROMPT_BODY} -

- } - data-test-subj="exceptionsEmptyPrompt" - /> -
- ) : ( - - - {!isInitLoading && - exceptions.length > 0 && - exceptions.map((exception) => ( - - - - ))} - - - )} -
-); - -ExceptionsViewerItemsComponent.displayName = 'ExceptionsViewerItemsComponent'; - -export const ExceptionsViewerItems = React.memo(ExceptionsViewerItemsComponent); - -ExceptionsViewerItems.displayName = 'ExceptionsViewerItems'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx deleted file mode 100644 index eeab8c7e36b70..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.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 from 'react'; -import { mount } from 'enzyme'; -import { ThemeProvider } from 'styled-components'; - -import { ExceptionsViewer } from '.'; -import { useKibana } from '../../../lib/kibana'; -import { useExceptionListItems, useApi } from '@kbn/securitysolution-list-hooks'; - -import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; -import { getFoundExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/found_exception_list_item_schema.mock'; -import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { - euiColorEmptyShade: '#ece', - euiBreakpoints: { - l: '1200px', - }, - euiSizeM: '10px', - }, -}); - -jest.mock('../../../lib/kibana'); -jest.mock('@kbn/securitysolution-list-hooks'); - -describe('ExceptionsViewer', () => { - const ruleName = 'test rule'; - - beforeEach(() => { - (useKibana as jest.Mock).mockReturnValue({ - services: { - http: {}, - application: { - getUrlForApp: () => 'some/url', - }, - }, - }); - - (useApi as jest.Mock).mockReturnValue({ - deleteExceptionItem: jest.fn().mockResolvedValue(true), - getExceptionListsItems: jest.fn().mockResolvedValue(getFoundExceptionListItemSchemaMock()), - }); - - (useExceptionListItems as jest.Mock).mockReturnValue([ - false, - [], - [], - { - page: 1, - perPage: 20, - total: 0, - }, - jest.fn(), - ]); - }); - - it('it renders loader if "loadingList" is true', () => { - (useExceptionListItems as jest.Mock).mockReturnValue([ - true, - [], - [], - { - page: 1, - perPage: 20, - total: 0, - }, - jest.fn(), - ]); - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="loadingPanelAllRulesTable"]').exists()).toBeTruthy(); - }); - - it('it renders empty prompt if no "exceptionListMeta" passed in', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); - }); - - it('it renders empty prompt if no exception items exist', () => { - (useExceptionListItems as jest.Mock).mockReturnValue([ - false, - [getExceptionListSchemaMock()], - [], - { - page: 1, - perPage: 20, - total: 0, - }, - jest.fn(), - ]); - - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx deleted file mode 100644 index e724a546f8054..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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, useReducer, useState } from 'react'; -import { EuiSpacer } from '@elastic/eui'; -import uuid from 'uuid'; - -import type { - ExceptionListTypeEnum, - ExceptionListItemSchema, - ExceptionListIdentifiers, - UseExceptionListItemsSuccess, -} from '@kbn/securitysolution-io-ts-list-types'; -import { useApi, useExceptionListItems } from '@kbn/securitysolution-list-hooks'; -import * as i18n from '../translations'; -import { useStateToaster } from '../../toasters'; -import { useUserData } from '../../../../detections/components/user_info'; -import { useKibana } from '../../../lib/kibana'; -import { Panel } from '../../panel'; -import { Loader } from '../../loader'; -import { ExceptionsViewerHeader } from './exceptions_viewer_header'; -import type { ExceptionListItemIdentifiers, Filter } from '../types'; -import type { State, ViewerFlyoutName } from './reducer'; -import { allExceptionItemsReducer } from './reducer'; - -import { ExceptionsViewerPagination } from './exceptions_pagination'; -import { ExceptionsViewerUtility } from './exceptions_utility'; -import { ExceptionsViewerItems } from './exceptions_viewer_items'; -import { EditExceptionFlyout } from '../edit_exception_flyout'; -import { AddExceptionFlyout } from '../add_exception_flyout'; - -const initialState: State = { - filterOptions: { filter: '', tags: [] }, - pagination: { - pageIndex: 0, - pageSize: 20, - totalItemCount: 0, - pageSizeOptions: [5, 10, 20, 50, 100, 200, 300], - }, - exceptions: [], - exceptionToEdit: null, - loadingItemIds: [], - isInitLoading: true, - currentModal: null, - exceptionListTypeToEdit: null, - totalEndpointItems: 0, - totalDetectionsItems: 0, - showEndpointListsOnly: false, - showDetectionsListsOnly: false, -}; - -interface ExceptionsViewerProps { - ruleId: string; - ruleName: string; - ruleIndices: string[]; - dataViewId?: string; - exceptionListsMeta: ExceptionListIdentifiers[]; - availableListTypes: ExceptionListTypeEnum[]; - commentsAccordionId: string; - onRuleChange?: () => void; -} - -const ExceptionsViewerComponent = ({ - ruleId, - ruleName, - ruleIndices, - dataViewId, - exceptionListsMeta, - availableListTypes, - commentsAccordionId, - onRuleChange, -}: ExceptionsViewerProps): JSX.Element => { - const { services } = useKibana(); - const [, dispatchToaster] = useStateToaster(); - const onDispatchToaster = useCallback( - ({ title, color, iconType }) => - (): void => { - dispatchToaster({ - type: 'addToaster', - toast: { - id: uuid.v4(), - title, - color, - iconType, - }, - }); - }, - [dispatchToaster] - ); - const [ - { - exceptions, - filterOptions, - pagination, - loadingItemIds, - isInitLoading, - currentModal, - exceptionToEdit, - exceptionListTypeToEdit, - totalEndpointItems, - totalDetectionsItems, - showDetectionsListsOnly, - showEndpointListsOnly, - }, - dispatch, - ] = useReducer(allExceptionItemsReducer(), { ...initialState }); - const { deleteExceptionItem, getExceptionListsItems } = useApi(services.http); - const [supportedListTypes, setSupportedListTypes] = useState([]); - - const [{ canUserCRUD, hasIndexWrite }] = useUserData(); - - useEffect((): void => { - if (!canUserCRUD || !hasIndexWrite) { - setSupportedListTypes([]); - } else { - setSupportedListTypes(availableListTypes); - } - }, [availableListTypes, canUserCRUD, hasIndexWrite]); - - const setExceptions = useCallback( - ({ - exceptions: newExceptions, - pagination: newPagination, - }: UseExceptionListItemsSuccess): void => { - dispatch({ - type: 'setExceptions', - lists: exceptionListsMeta, - exceptions: newExceptions, - pagination: newPagination, - }); - }, - [dispatch, exceptionListsMeta] - ); - const [loadingList, , , fetchListItems] = useExceptionListItems({ - http: services.http, - lists: exceptionListsMeta, - filterOptions: - filterOptions.filter !== '' || filterOptions.tags.length > 0 ? [filterOptions] : [], - pagination: { - page: pagination.pageIndex + 1, - perPage: pagination.pageSize, - total: pagination.totalItemCount, - }, - showDetectionsListsOnly, - showEndpointListsOnly, - matchFilters: true, - onSuccess: setExceptions, - onError: onDispatchToaster({ - color: 'danger', - title: i18n.FETCH_LIST_ERROR, - iconType: 'alert', - }), - }); - - const setCurrentModal = useCallback( - (modalName: ViewerFlyoutName): void => { - dispatch({ - type: 'updateModalOpen', - modalName, - }); - }, - [dispatch] - ); - - const setExceptionItemTotals = useCallback( - (endpointItemTotals: number | null, detectionItemTotals: number | null): void => { - dispatch({ - type: 'setExceptionItemTotals', - totalEndpointItems: endpointItemTotals, - totalDetectionsItems: detectionItemTotals, - }); - }, - [dispatch] - ); - - const handleGetTotals = useCallback(async (): Promise => { - await getExceptionListsItems({ - lists: exceptionListsMeta, - filterOptions: [], - pagination: { - page: 0, - perPage: 1, - total: 0, - }, - showDetectionsListsOnly: true, - showEndpointListsOnly: false, - onSuccess: ({ pagination: detectionPagination }) => { - setExceptionItemTotals(null, detectionPagination.total ?? 0); - }, - onError: () => { - const dispatchToasterError = onDispatchToaster({ - color: 'danger', - title: i18n.TOTAL_ITEMS_FETCH_ERROR, - iconType: 'alert', - }); - - dispatchToasterError(); - }, - }); - await getExceptionListsItems({ - lists: exceptionListsMeta, - filterOptions: [], - pagination: { - page: 0, - perPage: 1, - total: 0, - }, - showDetectionsListsOnly: false, - showEndpointListsOnly: true, - onSuccess: ({ pagination: endpointPagination }) => { - setExceptionItemTotals(endpointPagination.total ?? 0, null); - }, - onError: () => { - const dispatchToasterError = onDispatchToaster({ - color: 'danger', - title: i18n.TOTAL_ITEMS_FETCH_ERROR, - iconType: 'alert', - }); - - dispatchToasterError(); - }, - }); - }, [setExceptionItemTotals, exceptionListsMeta, getExceptionListsItems, onDispatchToaster]); - - const handleFetchList = useCallback((): void => { - if (fetchListItems != null) { - fetchListItems(); - handleGetTotals(); - } - }, [fetchListItems, handleGetTotals]); - - const handleFilterChange = useCallback( - (filters: Partial): void => { - dispatch({ - type: 'updateFilterOptions', - filters, - }); - }, - [dispatch] - ); - - const handleAddException = useCallback( - (type: ExceptionListTypeEnum): void => { - dispatch({ - type: 'updateExceptionListTypeToEdit', - exceptionListType: type, - }); - setCurrentModal('addException'); - }, - [setCurrentModal] - ); - - const handleEditException = useCallback( - (exception: ExceptionListItemSchema): void => { - dispatch({ - type: 'updateExceptionToEdit', - lists: exceptionListsMeta, - exception, - }); - setCurrentModal('editException'); - }, - [setCurrentModal, exceptionListsMeta] - ); - - const handleOnCancelExceptionModal = useCallback((): void => { - setCurrentModal(null); - handleFetchList(); - }, [setCurrentModal, handleFetchList]); - - const handleOnConfirmExceptionModal = useCallback((): void => { - setCurrentModal(null); - handleFetchList(); - }, [setCurrentModal, handleFetchList]); - - const setLoadingItemIds = useCallback( - (items: ExceptionListItemIdentifiers[]): void => { - dispatch({ - type: 'updateLoadingItemIds', - items, - }); - }, - [dispatch] - ); - - const handleDeleteException = useCallback( - ({ id: itemId, namespaceType }: ExceptionListItemIdentifiers) => { - setLoadingItemIds([{ id: itemId, namespaceType }]); - - deleteExceptionItem({ - id: itemId, - namespaceType, - onSuccess: () => { - setLoadingItemIds(loadingItemIds.filter(({ id }) => id !== itemId)); - handleFetchList(); - }, - onError: () => { - const dispatchToasterError = onDispatchToaster({ - color: 'danger', - title: i18n.DELETE_EXCEPTION_ERROR, - iconType: 'alert', - }); - - dispatchToasterError(); - setLoadingItemIds(loadingItemIds.filter(({ id }) => id !== itemId)); - }, - }); - }, - [setLoadingItemIds, deleteExceptionItem, loadingItemIds, handleFetchList, onDispatchToaster] - ); - - // Logic for initial render - useEffect((): void => { - if (isInitLoading && !loadingList && (exceptions.length === 0 || exceptions != null)) { - handleGetTotals(); - dispatch({ - type: 'updateIsInitLoading', - loading: false, - }); - } - }, [handleGetTotals, isInitLoading, exceptions, loadingList, dispatch]); - - // Used in utility bar info text - const ruleSettingsUrl = services.application.getUrlForApp( - `security/detections/rules/id/${encodeURI(ruleId)}/edit` - ); - - const showEmpty: boolean = !isInitLoading && !loadingList && exceptions.length === 0; - - const showNoResults: boolean = - exceptions.length === 0 && (totalEndpointItems > 0 || totalDetectionsItems > 0); - - return ( - <> - {currentModal === 'editException' && - exceptionToEdit != null && - exceptionListTypeToEdit != null && ( - - )} - - {currentModal === 'addException' && exceptionListTypeToEdit != null && ( - - )} - - - {(isInitLoading || loadingList) && ( - - )} - - - - - - - - - - - - - ); -}; - -ExceptionsViewerComponent.displayName = 'ExceptionsViewerComponent'; - -export const ExceptionsViewer = React.memo(ExceptionsViewerComponent); - -ExceptionsViewer.displayName = 'ExceptionsViewer'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/reducer.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/reducer.ts deleted file mode 100644 index 1d91f45fd0c9f..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/reducer.ts +++ /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 type { - ExceptionListType, - ExceptionListItemSchema, - ExceptionListIdentifiers, - Pagination, -} from '@kbn/securitysolution-io-ts-list-types'; -import type { - FilterOptions, - ExceptionsPagination, - ExceptionListItemIdentifiers, - Filter, -} from '../types'; - -export type ViewerFlyoutName = 'addException' | 'editException' | null; - -export interface State { - filterOptions: FilterOptions; - pagination: ExceptionsPagination; - exceptions: ExceptionListItemSchema[]; - exceptionToEdit: ExceptionListItemSchema | null; - loadingItemIds: ExceptionListItemIdentifiers[]; - isInitLoading: boolean; - currentModal: ViewerFlyoutName; - exceptionListTypeToEdit: ExceptionListType | null; - totalEndpointItems: number; - totalDetectionsItems: number; - showEndpointListsOnly: boolean; - showDetectionsListsOnly: boolean; -} - -export type Action = - | { - type: 'setExceptions'; - lists: ExceptionListIdentifiers[]; - exceptions: ExceptionListItemSchema[]; - pagination: Pagination; - } - | { - type: 'updateFilterOptions'; - filters: Partial; - } - | { type: 'updateIsInitLoading'; loading: boolean } - | { type: 'updateModalOpen'; modalName: ViewerFlyoutName } - | { - type: 'updateExceptionToEdit'; - lists: ExceptionListIdentifiers[]; - exception: ExceptionListItemSchema; - } - | { type: 'updateLoadingItemIds'; items: ExceptionListItemIdentifiers[] } - | { type: 'updateExceptionListTypeToEdit'; exceptionListType: ExceptionListType | null } - | { - type: 'setExceptionItemTotals'; - totalEndpointItems: number | null; - totalDetectionsItems: number | null; - }; - -export const allExceptionItemsReducer = - () => - (state: State, action: Action): State => { - switch (action.type) { - case 'setExceptions': { - const { exceptions, pagination } = action; - - return { - ...state, - pagination: { - ...state.pagination, - pageIndex: pagination.page - 1, - pageSize: pagination.perPage, - totalItemCount: pagination.total ?? 0, - }, - exceptions, - }; - } - case 'updateFilterOptions': { - const { filter, pagination, showEndpointListsOnly, showDetectionsListsOnly } = - action.filters; - return { - ...state, - filterOptions: { - ...state.filterOptions, - ...filter, - }, - pagination: { - ...state.pagination, - ...pagination, - }, - showEndpointListsOnly: showEndpointListsOnly ?? state.showEndpointListsOnly, - showDetectionsListsOnly: showDetectionsListsOnly ?? state.showDetectionsListsOnly, - }; - } - case 'setExceptionItemTotals': { - return { - ...state, - totalEndpointItems: - action.totalEndpointItems == null - ? state.totalEndpointItems - : action.totalEndpointItems, - totalDetectionsItems: - action.totalDetectionsItems == null - ? state.totalDetectionsItems - : action.totalDetectionsItems, - }; - } - case 'updateIsInitLoading': { - return { - ...state, - isInitLoading: action.loading, - }; - } - case 'updateLoadingItemIds': { - return { - ...state, - loadingItemIds: [...state.loadingItemIds, ...action.items], - }; - } - case 'updateExceptionToEdit': { - const { exception, lists } = action; - const exceptionListToEdit = lists.find((list) => { - return list !== null && exception.list_id === list.listId; - }); - return { - ...state, - exceptionToEdit: exception, - exceptionListTypeToEdit: exceptionListToEdit ? exceptionListToEdit.type : null, - }; - } - case 'updateModalOpen': { - return { - ...state, - currentModal: action.modalName, - }; - } - case 'updateExceptionListTypeToEdit': { - return { - ...state, - exceptionListTypeToEdit: action.exceptionListType, - }; - } - default: - return state; - } - }; diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts index c7f8481c36247..494ecb0c6b4d0 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts @@ -5,37 +5,27 @@ * 2.0. */ -import type { EuiLinkAnchorProps } from '@elastic/eui'; import { getDefaultEuiMarkdownParsingPlugins, getDefaultEuiMarkdownProcessingPlugins, getDefaultEuiMarkdownUiPlugins, } from '@elastic/eui'; -// Remove after this issue is resolved: https://github.com/elastic/eui/issues/4688 -import type { Options as Remark2RehypeOptions } from 'mdast-util-to-hast'; -import type { FunctionComponent } from 'react'; -import type rehype2react from 'rehype-react'; -import type { Plugin, PluggableList } from 'unified'; + import * as timelineMarkdownPlugin from './timeline'; +import * as osqueryMarkdownPlugin from './osquery'; export const { uiPlugins, parsingPlugins, processingPlugins } = { uiPlugins: getDefaultEuiMarkdownUiPlugins(), parsingPlugins: getDefaultEuiMarkdownParsingPlugins(), - processingPlugins: getDefaultEuiMarkdownProcessingPlugins() as [ - [Plugin, Remark2RehypeOptions], - [ - typeof rehype2react, - Parameters[0] & { - components: { a: FunctionComponent; timeline: unknown }; - } - ], - ...PluggableList - ], + processingPlugins: getDefaultEuiMarkdownProcessingPlugins(), }; uiPlugins.push(timelineMarkdownPlugin.plugin); +uiPlugins.push(osqueryMarkdownPlugin.plugin); parsingPlugins.push(timelineMarkdownPlugin.parser); +parsingPlugins.push(osqueryMarkdownPlugin.parser); // This line of code is TS-compatible and it will break if [1][1] change in the future. processingPlugins[1][1].components.timeline = timelineMarkdownPlugin.renderer; +processingPlugins[1][1].components.osquery = osqueryMarkdownPlugin.renderer; diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx new file mode 100644 index 0000000000000..7b96f3886159c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pickBy, isEmpty } from 'lodash'; +import type { Plugin } from 'unified'; +import React, { useContext, useMemo, useState, useCallback } from 'react'; +import type { RemarkTokenizer } from '@elastic/eui'; +import { + EuiSpacer, + EuiCodeBlock, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiModalFooter, + EuiButton, + EuiButtonEmpty, +} from '@elastic/eui'; +import { useForm, FormProvider } from 'react-hook-form'; +import styled from 'styled-components'; +import type { EuiMarkdownEditorUiPluginEditorProps } from '@elastic/eui/src/components/markdown_editor/markdown_types'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '../../../../lib/kibana'; +import { LabelField } from './label_field'; +import OsqueryLogo from './osquery_icon/osquery.svg'; +import { OsqueryFlyout } from '../../../../../detections/components/osquery/osquery_flyout'; +import { BasicAlertDataContext } from '../../../event_details/investigation_guide_view'; +import { convertECSMappingToObject } from './utils'; + +const StyledEuiButton = styled(EuiButton)` + > span > img { + margin-block-end: 0; + } +`; + +const OsqueryEditorComponent = ({ + node, + onSave, + onCancel, +}: EuiMarkdownEditorUiPluginEditorProps<{ + configuration: { + label?: string; + query: string; + ecs_mapping: { [key: string]: {} }; + }; +}>) => { + const isEditMode = node != null; + const { osquery } = useKibana().services; + const formMethods = useForm<{ + label: string; + query: string; + ecs_mapping: Record; + }>({ + defaultValues: { + label: node?.configuration?.label, + query: node?.configuration?.query, + ecs_mapping: node?.configuration?.ecs_mapping, + }, + }); + + const onSubmit = useCallback( + (data) => { + onSave( + `!{osquery${JSON.stringify( + pickBy( + { + query: data.query, + label: data.label, + ecs_mapping: convertECSMappingToObject(data.ecs_mapping), + }, + (value) => !isEmpty(value) + ) + )}}`, + { + block: true, + } + ); + }, + [onSave] + ); + + const OsqueryActionForm = useMemo(() => { + if (osquery?.LiveQueryField) { + const { LiveQueryField } = osquery; + + return ( + + + + + + ); + } + return null; + }, [formMethods, osquery]); + + return ( + <> + + + {isEditMode ? ( + + ) : ( + + )} + + + + + <>{OsqueryActionForm} + + + + + {i18n.translate('xpack.securitySolution.markdown.osquery.modalCancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + {isEditMode ? ( + + ) : ( + + )} + + + + ); +}; + +const OsqueryEditor = React.memo(OsqueryEditorComponent); + +export const plugin = { + name: 'osquery', + button: { + label: 'Osquery', + iconType: 'logoOsquery', + }, + helpText: ( +
+ + {'!{osquery{options}}'} + + +
+ ), + editor: OsqueryEditor, +}; + +export const parser: Plugin = function () { + const Parser = this.Parser; + const tokenizers = Parser.prototype.blockTokenizers; + const methods = Parser.prototype.blockMethods; + + const tokenizeOsquery: RemarkTokenizer = function (eat, value, silent) { + if (value.startsWith('!{osquery') === false) return false; + + const nextChar = value[9]; + + if (nextChar !== '{' && nextChar !== '}') return false; // this isn't actually a osquery + + if (silent) { + return true; + } + + // is there a configuration? + const hasConfiguration = nextChar === '{'; + + let match = '!{osquery'; + let configuration = {}; + + if (hasConfiguration) { + let configurationString = ''; + + let openObjects = 0; + + for (let i = 9; i < value.length; i++) { + const char = value[i]; + if (char === '{') { + openObjects++; + configurationString += char; + } else if (char === '}') { + openObjects--; + if (openObjects === -1) { + break; + } + configurationString += char; + } else { + configurationString += char; + } + } + + match += configurationString; + try { + configuration = JSON.parse(configurationString); + } catch (e) { + const now = eat.now(); + this.file.fail(`Unable to parse osquery JSON configuration: ${e}`, { + line: now.line, + column: now.column + 9, + }); + } + } + + match += '}'; + + return eat(match)({ + type: 'osquery', + configuration, + }); + }; + + tokenizers.osquery = tokenizeOsquery; + methods.splice(methods.indexOf('text'), 0, 'osquery'); +}; + +// receives the configuration from the parser and renders +const RunOsqueryButtonRenderer = ({ + configuration, +}: { + configuration: { + label?: string; + query: string; + ecs_mapping: { [key: string]: {} }; + }; +}) => { + const [showFlyout, setShowFlyout] = useState(false); + const { agentId } = useContext(BasicAlertDataContext); + + const handleOpen = useCallback(() => setShowFlyout(true), [setShowFlyout]); + + const handleClose = useCallback(() => setShowFlyout(false), [setShowFlyout]); + + return ( + <> + + {configuration.label ?? + i18n.translate('xpack.securitySolution.markdown.osquery.runOsqueryButtonLabel', { + defaultMessage: 'Run Osquery', + })} + + {showFlyout && ( + + )} + + ); +}; + +export { RunOsqueryButtonRenderer as renderer }; diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/label_field.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/label_field.tsx new file mode 100644 index 0000000000000..3517bbf7643d3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/label_field.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { useController } from 'react-hook-form'; +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface QueryDescriptionFieldProps { + euiFieldProps?: Record; +} + +const LabelFieldComponent = ({ euiFieldProps }: QueryDescriptionFieldProps) => { + const { + field: { onChange, value, name: fieldName }, + fieldState: { error }, + } = useController({ + name: 'label', + defaultValue: '', + }); + + const hasError = useMemo(() => !!error?.message, [error?.message]); + + return ( + + + + ); +}; + +export const LabelField = React.memo(LabelFieldComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/index.tsx new file mode 100644 index 0000000000000..fe7b811bd70fd --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/index.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 type { EuiIconProps } from '@elastic/eui'; +import { EuiIcon } from '@elastic/eui'; +import OsqueryLogo from './osquery.svg'; + +export type OsqueryIconProps = Omit; + +const OsqueryIconComponent: React.FC = (props) => ( + +); + +export const OsqueryIcon = React.memo(OsqueryIconComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/osquery.svg b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/osquery.svg new file mode 100755 index 0000000000000..32305a5916c04 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/osquery_icon/osquery.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts new file mode 100644 index 0000000000000..77e2f14c51420 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.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 { isEmpty, reduce } from 'lodash'; + +export const convertECSMappingToObject = ( + ecsMapping: Array<{ + key: string; + result: { + type: string; + value: string; + }; + }> +) => + 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/security_solution/public/common/components/navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts index 99ce1198f30d7..5a4c346be2e12 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts @@ -65,6 +65,7 @@ export interface NavTab { } export const securityNavKeys = [ SecurityPageName.alerts, + SecurityPageName.actionHistory, SecurityPageName.blocklist, SecurityPageName.detectionAndResponse, SecurityPageName.case, @@ -77,7 +78,6 @@ export const securityNavKeys = [ SecurityPageName.hosts, SecurityPageName.network, SecurityPageName.overview, - SecurityPageName.responseActions, SecurityPageName.rules, SecurityPageName.timelines, SecurityPageName.trustedApps, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap index 3b87593d9f483..a7f54ccf701b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap @@ -240,6 +240,16 @@ Object { "name": "Blocklist", "onClick": [Function], }, + Object { + "data-href": "securitySolutionUI/action_history", + "data-test-subj": "navigation-action_history", + "disabled": false, + "href": "securitySolutionUI/action_history", + "id": "action_history", + "isSelected": false, + "name": "Action history", + "onClick": [Function], + }, Object { "data-href": "securitySolutionUI/cloud_security_posture-benchmarks", "data-test-subj": "navigation-cloud_security_posture-benchmarks", diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx index ea1448e57398b..2a8d977760cbf 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx @@ -147,6 +147,7 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record) { ? [navTabs[SecurityPageName.hostIsolationExceptions]] : []), navTabs[SecurityPageName.blocklist], + navTabs[SecurityPageName.actionHistory], navTabs[SecurityPageName.cloudSecurityPostureBenchmarks], ], }, diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score_over_time/index.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score_over_time/index.tsx index 5c6979fbd4a03..d17621ade7956 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score_over_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score_over_time/index.tsx @@ -27,13 +27,14 @@ import { HeaderSection } from '../header_section'; import { InspectButton, InspectButtonContainer } from '../inspect'; import * as i18n from './translations'; import { PreferenceFormattedDate } from '../formatted_date'; -import type { RiskScore } from '../../../../common/search_strategy'; +import type { HostRiskScore, UserRiskScore } from '../../../../common/search_strategy'; +import { isUserRiskScore } from '../../../../common/search_strategy'; export interface RiskScoreOverTimeProps { from: string; to: string; loading: boolean; - riskScore?: RiskScore[]; + riskScore?: Array; queryId: string; title: string; toggleStatus: boolean; @@ -81,7 +82,7 @@ const RiskScoreOverTimeComponent: React.FC = ({ riskScore ?.map((data) => ({ x: data['@timestamp'], - y: data.risk_stats.risk_score, + y: (isUserRiskScore(data) ? data.user : data.host).risk.calculated_score_norm, })) .reverse() ?? [], [riskScore] diff --git a/x-pack/plugins/security_solution/public/common/images/illustration_product_no_results_magnifying_glass.svg b/x-pack/plugins/security_solution/public/common/images/illustration_product_no_results_magnifying_glass.svg new file mode 100644 index 0000000000000..b9a0df1630b20 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/images/illustration_product_no_results_magnifying_glass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/security_solution/public/common/lib/apm/user_actions.ts b/x-pack/plugins/security_solution/public/common/lib/apm/user_actions.ts index ba2a3fa77e637..44703b4f5707c 100644 --- a/x-pack/plugins/security_solution/public/common/lib/apm/user_actions.ts +++ b/x-pack/plugins/security_solution/public/common/lib/apm/user_actions.ts @@ -33,3 +33,21 @@ export const RULES_TABLE_ACTIONS = { PREVIEW_ON: `${APP_UI_ID} rulesTable technicalPreview on`, PREVIEW_OFF: `${APP_UI_ID} rulesTable technicalPreview off`, }; + +export const TIMELINE_ACTIONS = { + SAVE: `${APP_UI_ID} timeline save`, + DUPLICATE: `${APP_UI_ID} timeline duplicate`, // it includes duplicate template, create template from timeline and create timeline from template + DELETE: `${APP_UI_ID} timeline delete`, + BULK_DELETE: `${APP_UI_ID} timeline bulkDelete`, +}; + +export const ALERTS_ACTIONS = { + OPEN_ANALYZER: `${APP_UI_ID} alerts openAnalyzer`, + OPEN_SESSION_VIEW: `${APP_UI_ID} alerts openSessionView`, + INVESTIGATE_IN_TIMELINE: `${APP_UI_ID} alerts investigateInTimeline`, +}; + +export const FIELD_BROWSER_ACTIONS = { + FIELD_SAVED: `${APP_UI_ID} fieldBrowser fieldSaved`, + FIELD_DELETED: `${APP_UI_ID} fieldBrowser fieldDeleted`, +}; diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index 00ca0e0a5852c..d0d5fb0cd2cc5 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -82,7 +82,7 @@ export const mockGlobalState: State = { hostRisk: { activePage: 0, limit: 10, - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, severitySelection: [], }, sessions: { activePage: 0, limit: 10 }, @@ -106,7 +106,7 @@ export const mockGlobalState: State = { hostRisk: { activePage: 0, limit: 10, - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, severitySelection: [], }, sessions: { activePage: 0, limit: 10 }, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx index 5945b1a0111c0..de5eca78aaffb 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx @@ -14,30 +14,30 @@ import { AddExceptionFlyout } from '.'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import { useAsync } from '@kbn/securitysolution-hook-utils'; import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; -import { useFetchIndex } from '../../../containers/source'; +import { useFetchIndex } from '../../../../common/containers/source'; import { createStubIndexPattern, stubIndexPattern } from '@kbn/data-plugin/common/stubs'; -import { useAddOrUpdateException } from '../use_add_exception'; -import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list'; +import { useAddOrUpdateException } from '../../logic/use_add_exception'; +import { useFetchOrCreateRuleExceptionList } from '../../logic/use_fetch_or_create_rule_exception_list'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; -import * as helpers from '../helpers'; +import * as helpers from '../../utils/helpers'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import type { EntriesArray } from '@kbn/securitysolution-io-ts-list-types'; -import { TestProviders } from '../../../mock'; +import { TestProviders } from '../../../../common/mock'; import { getRulesEqlSchemaMock, getRulesSchemaMock, } from '../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; -import type { AlertData } from '../types'; +import type { AlertData } from '../../utils/types'; jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); -jest.mock('../../../lib/kibana'); -jest.mock('../../../containers/source'); +jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/containers/source'); jest.mock('../../../../detections/containers/detection_engine/rules'); -jest.mock('../use_add_exception'); -jest.mock('../use_fetch_or_create_rule_exception_list'); +jest.mock('../../logic/use_add_exception'); +jest.mock('../../logic/use_fetch_or_create_rule_exception_list'); jest.mock('@kbn/securitysolution-hook-utils', () => ({ ...jest.requireActual('@kbn/securitysolution-hook-utils'), useAsync: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index e5ae187d79d34..1a547b6e62d60 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -46,17 +46,17 @@ import { isThresholdRule, } from '../../../../../common/detection_engine/utils'; import type { Status } from '../../../../../common/detection_engine/schemas/common/schemas'; -import * as i18nCommon from '../../../translations'; +import * as i18nCommon from '../../../../common/translations'; import * as i18n from './translations'; -import * as sharedI18n from '../translations'; -import { useAppToasts } from '../../../hooks/use_app_toasts'; -import { useKibana } from '../../../lib/kibana'; -import { Loader } from '../../loader'; -import { useAddOrUpdateException } from '../use_add_exception'; +import * as sharedI18n from '../../utils/translations'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useKibana } from '../../../../common/lib/kibana'; +import { Loader } from '../../../../common/components/loader'; +import { useAddOrUpdateException } from '../../logic/use_add_exception'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; -import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list'; -import { AddExceptionComments } from '../add_exception_comments'; +import { useFetchOrCreateRuleExceptionList } from '../../logic/use_fetch_or_create_rule_exception_list'; +import { ExceptionItemComments } from '../item_comments'; import { enrichNewExceptionItemsWithComments, enrichExceptionItemsWithOS, @@ -66,11 +66,11 @@ import { entryHasNonEcsType, retrieveAlertOsTypes, filterIndexPatterns, -} from '../helpers'; +} from '../../utils/helpers'; import type { ErrorInfo } from '../error_callout'; import { ErrorCallout } from '../error_callout'; -import type { AlertData } from '../types'; -import { useFetchIndex } from '../../../containers/source'; +import type { AlertData } from '../../utils/types'; +import { useFetchIndex } from '../../../../common/containers/source'; export interface AddExceptionFlyoutProps { ruleName: string; @@ -549,7 +549,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ - diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/translations.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.test.tsx new file mode 100644 index 0000000000000..0df9fad55a14d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.test.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; + +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionsViewerItems } from './all_items'; +import { getMockTheme } from '../../../../common/lib/kibana/kibana_react.mock'; +import { TestProviders } from '../../../../common/mock'; + +const mockTheme = getMockTheme({ + eui: { + euiSize: '10px', + euiColorPrimary: '#ece', + euiColorDanger: '#ece', + }, +}); + +describe('ExceptionsViewerItems', () => { + it('it renders empty prompt if "viewerState" is "empty"', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-empty-detection"]').exists() + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); + }); + + it('it renders no search results found prompt if "viewerState" is "empty_search"', () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-emptySearch"]').exists() + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); + }); + + it('it renders exceptions if "viewerState" and "null"', () => { + const wrapper = mount( + + + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.tsx new file mode 100644 index 0000000000000..fdffa134dd96f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/all_items.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import type { + ExceptionListItemSchema, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionItemCard } from '../exception_item_card'; +import type { ExceptionListItemIdentifiers } from '../../utils/types'; +import type { RuleReferences } from '../../logic/use_find_references'; +import type { ViewerState } from './reducer'; +import { ExeptionItemsViewerEmptyPrompts } from './empty_viewer_state'; + +const MyFlexItem = styled(EuiFlexItem)` + margin: ${({ theme }) => `${theme.eui.euiSize} 0`}; + &:first-child { + margin: ${({ theme }) => `${theme.eui.euiSizeXS} 0 ${theme.eui.euiSize}`}; + } +`; + +interface ExceptionItemsViewerProps { + isReadOnly: boolean; + disableActions: boolean; + exceptions: ExceptionListItemSchema[]; + listType: ExceptionListTypeEnum; + ruleReferences: RuleReferences | null; + viewerState: ViewerState; + onCreateExceptionListItem: () => void; + onDeleteException: (arg: ExceptionListItemIdentifiers) => void; + onEditExceptionItem: (item: ExceptionListItemSchema) => void; +} + +const ExceptionItemsViewerComponent: React.FC = ({ + isReadOnly, + exceptions, + listType, + disableActions, + ruleReferences, + viewerState, + onCreateExceptionListItem, + onDeleteException, + onEditExceptionItem, +}): JSX.Element => { + return ( + <> + {viewerState != null && viewerState !== 'deleting' ? ( + + ) : ( + + + + {exceptions.map((exception) => ( + + + + ))} + + + + )} + + ); +}; + +ExceptionItemsViewerComponent.displayName = 'ExceptionItemsViewerComponent'; + +export const ExceptionsViewerItems = React.memo(ExceptionItemsViewerComponent); + +ExceptionsViewerItems.displayName = 'ExceptionsViewerItems'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.test.tsx new file mode 100644 index 0000000000000..66892e31031be --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExeptionItemsViewerEmptyPrompts } from './empty_viewer_state'; +import * as i18n from './translations'; + +describe('ExeptionItemsViewerEmptyPrompts', () => { + it('it renders loading screen when "currentState" is "loading"', () => { + const wrapper = mount( + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-loading"]').exists() + ).toBeTruthy(); + }); + + it('it renders empty search screen when "currentState" is "empty_search"', () => { + const wrapper = mount( + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-emptySearch"]').exists() + ).toBeTruthy(); + }); + + it('it renders no endpoint items screen when "currentState" is "empty" and "listType" is "endpoint"', () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_ENDPOINT_PROMPT_BODY + ); + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptButton"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_ENDPOINT_BUTTON + ); + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-empty-endpoint"]').exists() + ).toBeTruthy(); + }); + + it('it renders no exception items screen when "currentState" is "empty" and "listType" is "detection"', () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_BODY + ); + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptButton"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_BUTTON + ); + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-empty-detection"]').exists() + ).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.tsx new file mode 100644 index 0000000000000..2be1860f138d3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/empty_viewer_state.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 { + EuiLoadingContent, + EuiImage, + EuiEmptyPrompt, + EuiButton, + useEuiTheme, + EuiPanel, +} from '@elastic/eui'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from './translations'; +import type { ViewerState } from './reducer'; +import illustration from '../../../../common/images/illustration_product_no_results_magnifying_glass.svg'; + +interface ExeptionItemsViewerEmptyPromptsComponentProps { + isReadOnly: boolean; + listType: ExceptionListTypeEnum; + currentState: ViewerState; + onCreateExceptionListItem: () => void; +} + +const ExeptionItemsViewerEmptyPromptsComponent = ({ + isReadOnly, + listType, + currentState, + onCreateExceptionListItem, +}: ExeptionItemsViewerEmptyPromptsComponentProps): JSX.Element => { + const { euiTheme } = useEuiTheme(); + + const content = useMemo(() => { + switch (currentState) { + case 'error': + return ( + {i18n.EXCEPTION_ERROR_TITLE}} + body={

{i18n.EXCEPTION_ERROR_DESCRIPTION}

} + data-test-subj={'exceptionItemViewerEmptyPrompts-error'} + /> + ); + case 'empty': + return ( + + {i18n.EXCEPTION_EMPTY_PROMPT_TITLE} + + } + body={ +

+ {listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.EXCEPTION_EMPTY_ENDPOINT_PROMPT_BODY + : i18n.EXCEPTION_EMPTY_PROMPT_BODY} +

+ } + actions={[ + + {listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.EXCEPTION_EMPTY_PROMPT_ENDPOINT_BUTTON + : i18n.EXCEPTION_EMPTY_PROMPT_BUTTON} + , + ]} + data-test-subj={`exceptionItemViewerEmptyPrompts-empty-${listType}`} + /> + ); + case 'empty_search': + return ( + } + title={

{i18n.EXCEPTION_NO_SEARCH_RESULTS_PROMPT_TITLE}

} + body={

{i18n.EXCEPTION_NO_SEARCH_RESULTS_PROMPT_BODY}

} + data-test-subj="exceptionItemViewerEmptyPrompts-emptySearch" + /> + ); + default: + return ( + + ); + } + }, [currentState, euiTheme.colors.darkestShade, isReadOnly, listType, onCreateExceptionListItem]); + + return ( + + {content} + + ); +}; + +export const ExeptionItemsViewerEmptyPrompts = React.memo(ExeptionItemsViewerEmptyPromptsComponent); + +ExeptionItemsViewerEmptyPrompts.displayName = 'ExeptionItemsViewerEmptyPrompts'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx new file mode 100644 index 0000000000000..d34cf07aa9146 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx @@ -0,0 +1,309 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useReducer } from 'react'; +import { mount, shallow } from 'enzyme'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionsViewer } from '.'; +import { useKibana } from '../../../../common/lib/kibana'; +import { TestProviders } from '../../../../common/mock'; +import type { Rule } from '../../../../detections/containers/detection_engine/rules/types'; +import { mockRule } from '../../../../detections/pages/detection_engine/rules/all/__mocks__/mock'; +import { useFindExceptionListReferences } from '../../logic/use_find_references'; +import * as i18n from './translations'; + +jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/securitysolution-list-hooks'); +jest.mock('../../logic/use_find_references'); +jest.mock('react', () => { + const r = jest.requireActual('react'); + return { ...r, useReducer: jest.fn() }; +}); + +const sampleExceptionItem = { + _version: 'WzEwMjM4MSwxXQ==', + comments: [], + created_at: '2022-08-18T17:38:09.018Z', + created_by: 'elastic', + description: 'Index - exception list item', + entries: [ + { + field: 'Endpoint.policy.applied.artifacts.global.identifiers.name', + operator: 'included', + type: 'match', + value: 'sdf', + id: '6a62a5fb-a7d7-44bf-942c-a44b69baba63', + }, + ], + id: '863f3cb0-1f1c-11ed-8a48-9982ed15e50b', + item_id: '74eacd42-7617-4d32-9363-3c074a8892fe', + list_id: '9633e7f2-b92c-4a51-ad56-3e69e5e5f517', + name: 'Index - exception list item', + namespace_type: 'single', + os_types: [], + tags: [], + tie_breaker_id: '5ed24b1f-e717-4798-92ac-9eefd33bb9c0', + type: 'simple', + updated_at: '2022-08-18T17:38:09.020Z', + updated_by: 'elastic', + meta: undefined, +}; + +const getMockRule = (): Rule => ({ + ...mockRule('123'), + exceptions_list: [ + { + id: '5b543420', + list_id: 'list_id', + type: 'endpoint', + namespace_type: 'single', + }, + ], +}); + +describe('ExceptionsViewer', () => { + beforeEach(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + http: {}, + application: { + getUrlForApp: () => 'some/url', + }, + }, + }); + + (useFindExceptionListReferences as jest.Mock).mockReturnValue([false, null]); + }); + + it('it renders loading screen when "currentState" is "loading"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: null, + exceptionToEdit: null, + viewerState: 'loading', + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-loading"]').exists() + ).toBeTruthy(); + }); + + it('it renders empty search screen when "currentState" is "empty_search"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: null, + exceptionToEdit: null, + viewerState: 'empty_search', + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-emptySearch"]').exists() + ).toBeTruthy(); + }); + + it('it renders no endpoint items screen when "currentState" is "empty" and "listType" is "endpoint"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: null, + exceptionToEdit: null, + viewerState: 'empty', + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_ENDPOINT_PROMPT_BODY + ); + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptButton"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_ENDPOINT_BUTTON + ); + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-empty-endpoint"]').exists() + ).toBeTruthy(); + }); + + it('it renders no exception items screen when "currentState" is "empty" and "listType" is "detection"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: null, + exceptionToEdit: null, + viewerState: 'empty', + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptBody"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_BODY + ); + expect(wrapper.find('[data-test-subj="exceptionsEmptyPromptButton"]').at(0).text()).toEqual( + i18n.EXCEPTION_EMPTY_PROMPT_BUTTON + ); + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-empty-detection"]').exists() + ).toBeTruthy(); + }); + + it('it renders add exception flyout if "currentFlyout" is "addException"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: 'addException', + exceptionToEdit: null, + viewerState: null, + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="addExceptionItemFlyout"]').exists()).toBeTruthy(); + }); + + it('it renders edit exception flyout if "currentFlyout" is "editException"', () => { + (useReducer as jest.Mock).mockReturnValue([ + { + exceptions: [sampleExceptionItem], + pagination: { pageIndex: 0, pageSize: 25, totalItemCount: 0, pageSizeOptions: [25, 50] }, + currenFlyout: 'editException', + exceptionToEdit: sampleExceptionItem, + viewerState: null, + exceptionLists: [], + }, + jest.fn(), + ]); + + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="editExceptionItemFlyout"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx new file mode 100644 index 0000000000000..e6168bb39edbd --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx @@ -0,0 +1,393 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, useEffect, useReducer, useState } from 'react'; +import { EuiPanel, EuiSpacer } from '@elastic/eui'; + +import type { + ExceptionListItemSchema, + UseExceptionListItemsSuccess, + Pagination, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; +import { transformInput } from '@kbn/securitysolution-list-hooks'; + +import { + deleteExceptionListItemById, + fetchExceptionListsItemsByListIds, +} from '@kbn/securitysolution-list-api'; +import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; +import { useUserData } from '../../../../detections/components/user_info'; +import { useKibana, useToasts } from '../../../../common/lib/kibana'; +import { ExceptionsViewerSearchBar } from './search_bar'; +import type { ExceptionListItemIdentifiers } from '../../utils/types'; +import type { State, ViewerFlyoutName, ViewerState } from './reducer'; +import { allExceptionItemsReducer } from './reducer'; + +import { ExceptionsViewerPagination } from './pagination'; +import { ExceptionsViewerUtility } from './utility_bar'; +import { ExceptionsViewerItems } from './all_items'; +import { EditExceptionFlyout } from '../edit_exception_flyout'; +import { AddExceptionFlyout } from '../add_exception_flyout'; +import * as i18n from './translations'; +import { useFindExceptionListReferences } from '../../logic/use_find_references'; +import type { Rule } from '../../../../detections/containers/detection_engine/rules/types'; + +const STATES_SEARCH_HIDDEN: ViewerState[] = ['error', 'empty']; +const STATES_PAGINATION_UTILITY_HIDDEN: ViewerState[] = [ + 'loading', + 'empty_search', + 'empty', + 'error', + 'searching', +]; + +const initialState: State = { + pagination: { + pageIndex: 0, + pageSize: 25, + totalItemCount: 0, + pageSizeOptions: [1, 5, 10, 25, 50, 100, 200, 300], + }, + exceptions: [], + exceptionToEdit: null, + currenFlyout: null, + viewerState: 'loading', +}; + +export interface GetExceptionItemProps { + pagination?: Partial; + search?: string; + filters?: string; +} + +interface ExceptionsViewerProps { + rule: Rule | null; + listType: ExceptionListTypeEnum; + onRuleChange?: () => void; +} + +const ExceptionsViewerComponent = ({ + rule, + listType, + onRuleChange, +}: ExceptionsViewerProps): JSX.Element => { + const { services } = useKibana(); + const toasts = useToasts(); + const [{ canUserCRUD, hasIndexWrite }] = useUserData(); + const [isReadOnly, setReadOnly] = useState(true); + const [lastUpdated, setLastUpdated] = useState(null); + const exceptionListsToQuery = useMemo( + () => + rule != null && rule.exceptions_list != null + ? rule.exceptions_list.filter((list) => list.type === listType) + : [], + [listType, rule] + ); + + // Reducer state + const [{ exceptions, pagination, currenFlyout, exceptionToEdit, viewerState }, dispatch] = + useReducer(allExceptionItemsReducer(), { + ...initialState, + }); + + // Reducer actions + const setExceptions = useCallback( + ({ + exceptions: newExceptions, + pagination: newPagination, + }: UseExceptionListItemsSuccess): void => { + setLastUpdated(Date.now()); + + dispatch({ + type: 'setExceptions', + exceptions: newExceptions, + pagination: newPagination, + }); + }, + [dispatch] + ); + + const setViewerState = useCallback( + (state: ViewerState): void => { + dispatch({ + type: 'setViewerState', + state, + }); + }, + [dispatch] + ); + + const setFlyoutType = useCallback( + (flyoutType: ViewerFlyoutName): void => { + dispatch({ + type: 'updateFlyoutOpen', + flyoutType, + }); + }, + [dispatch] + ); + + const [_, allReferences] = useFindExceptionListReferences(exceptionListsToQuery); + + const handleFetchItems = useCallback( + async (options?: GetExceptionItemProps) => { + const abortCtrl = new AbortController(); + + const newPagination = + options?.pagination != null + ? { + page: (options.pagination.page ?? 0) + 1, + perPage: options.pagination.perPage, + } + : { + page: pagination.pageIndex + 1, + perPage: pagination.pageSize, + }; + + if (exceptionListsToQuery.length === 0) { + return { + data: [], + pageIndex: pagination.pageIndex, + itemsPerPage: pagination.pageSize, + total: 0, + }; + } + + const { + page: pageIndex, + per_page: itemsPerPage, + total, + data, + } = await fetchExceptionListsItemsByListIds({ + filter: undefined, + http: services.http, + listIds: exceptionListsToQuery.map((list) => list.list_id), + namespaceTypes: exceptionListsToQuery.map((list) => list.namespace_type), + search: options?.search, + pagination: newPagination, + signal: abortCtrl.signal, + }); + + // Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes + // for context around the temporary `id` + const transformedData = data.map((item) => transformInput(item)); + + return { + data: transformedData, + pageIndex, + itemsPerPage, + total, + }; + }, + [pagination.pageIndex, pagination.pageSize, exceptionListsToQuery, services.http] + ); + + const handleGetExceptionListItems = useCallback( + async (options?: GetExceptionItemProps) => { + try { + setViewerState('loading'); + + const { pageIndex, itemsPerPage, total, data } = await handleFetchItems(options); + + setViewerState(total > 0 ? null : 'empty'); + + setExceptions({ + exceptions: data, + pagination: { + page: pageIndex, + perPage: itemsPerPage, + total, + }, + }); + } catch (e) { + setViewerState('error'); + + toasts.addError(e, { + title: i18n.EXCEPTION_ERROR_TITLE, + toastMessage: i18n.EXCEPTION_ERROR_DESCRIPTION, + }); + } + }, + [handleFetchItems, setExceptions, setViewerState, toasts] + ); + + const handleSearch = useCallback( + async (options?: GetExceptionItemProps) => { + try { + setViewerState('searching'); + + const { pageIndex, itemsPerPage, total, data } = await handleFetchItems(options); + + setViewerState(total > 0 ? null : 'empty_search'); + + setExceptions({ + exceptions: data, + pagination: { + page: pageIndex, + perPage: itemsPerPage, + total, + }, + }); + } catch (e) { + toasts.addError(e, { + title: i18n.EXCEPTION_SEARCH_ERROR_TITLE, + toastMessage: i18n.EXCEPTION_SEARCH_ERROR_BODY, + }); + } + }, + [handleFetchItems, setExceptions, setViewerState, toasts] + ); + + const handleAddException = useCallback((): void => { + setFlyoutType('addException'); + }, [setFlyoutType]); + + const handleEditException = useCallback( + (exception: ExceptionListItemSchema): void => { + dispatch({ + type: 'updateExceptionToEdit', + exception, + }); + setFlyoutType('editException'); + }, + [setFlyoutType] + ); + + const handleCancelExceptionItemFlyout = useCallback((): void => { + setFlyoutType(null); + handleGetExceptionListItems(); + }, [setFlyoutType, handleGetExceptionListItems]); + + const handleConfirmExceptionFlyout = useCallback((): void => { + setFlyoutType(null); + handleGetExceptionListItems(); + }, [setFlyoutType, handleGetExceptionListItems]); + + const handleDeleteException = useCallback( + async ({ id: itemId, name, namespaceType }: ExceptionListItemIdentifiers) => { + const abortCtrl = new AbortController(); + + try { + setViewerState('deleting'); + + await deleteExceptionListItemById({ + http: services.http, + id: itemId, + namespaceType, + signal: abortCtrl.signal, + }); + + toasts.addSuccess({ + title: i18n.EXCEPTION_ITEM_DELETE_TITLE, + text: i18n.EXCEPTION_ITEM_DELETE_TEXT(name), + }); + + await handleGetExceptionListItems(); + } catch (e) { + setViewerState('error'); + + toasts.addError(e, { + title: i18n.EXCEPTION_DELETE_ERROR_TITLE, + }); + } + }, + [handleGetExceptionListItems, services.http, setViewerState, toasts] + ); + + // User privileges checks + useEffect((): void => { + setReadOnly(!canUserCRUD || !hasIndexWrite); + }, [setReadOnly, canUserCRUD, hasIndexWrite]); + + useEffect(() => { + if (exceptionListsToQuery.length > 0) { + handleGetExceptionListItems(); + } else { + setViewerState('empty'); + } + }, [exceptionListsToQuery.length, handleGetExceptionListItems, setViewerState]); + + return ( + <> + {currenFlyout === 'editException' && exceptionToEdit != null && rule != null && ( + + )} + + {currenFlyout === 'addException' && rule != null && ( + + )} + + + <> + {!STATES_SEARCH_HIDDEN.includes(viewerState) && ( + + )} + {!STATES_PAGINATION_UTILITY_HIDDEN.includes(viewerState) && ( + <> + + + + + )} + + + + {!STATES_PAGINATION_UTILITY_HIDDEN.includes(viewerState) && ( + + )} + + + + ); +}; + +ExceptionsViewerComponent.displayName = 'ExceptionsViewerComponent'; + +export const ExceptionsViewer = React.memo(ExceptionsViewerComponent); + +ExceptionsViewer.displayName = 'ExceptionsViewer'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.test.tsx new file mode 100644 index 0000000000000..ccca3542b2520 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { ExceptionsViewerPagination } from './pagination'; + +describe('ExceptionsViewerPagination', () => { + it('it invokes "onPaginationChange" when per page item is clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = mount( + + ); + + wrapper.find('button[data-test-subj="tablePaginationPopoverButton"]').at(0).simulate('click'); + wrapper.find('button[data-test-subj="tablePagination-50-rows"]').at(0).simulate('click'); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ pagination: { page: 0, perPage: 50 } }); + }); + + it('it invokes "onPaginationChange" when next clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = mount( + + ); + + wrapper.find('button[data-test-subj="pagination-button-next"]').at(0).simulate('click'); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ pagination: { page: 1, perPage: 5 } }); + }); + + it('it invokes "onPaginationChange" when page clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = mount( + + ); + + wrapper.find('button[data-test-subj="pagination-button-2"]').simulate('click'); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ pagination: { page: 2, perPage: 50 } }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.tsx new file mode 100644 index 0000000000000..4310462164950 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/pagination.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 React, { useCallback } from 'react'; +import { EuiTablePagination } from '@elastic/eui'; + +import type { ExceptionsPagination } from '../../utils/types'; +import * as i18n from './translations'; +import type { GetExceptionItemProps } from '.'; + +interface ExceptionsViewerPaginationProps { + pagination: ExceptionsPagination; + onPaginationChange: (arg: GetExceptionItemProps) => void; +} + +const ExceptionsViewerPaginationComponent = ({ + pagination, + onPaginationChange, +}: ExceptionsViewerPaginationProps): JSX.Element => { + const handleItemsPerPageChange = useCallback( + (pageSize: number) => { + onPaginationChange({ + pagination: { + page: pagination.pageIndex, + perPage: pageSize, + }, + }); + }, + [onPaginationChange, pagination.pageIndex] + ); + + const handlePageIndexChange = useCallback( + (pageIndex: number) => { + onPaginationChange({ + pagination: { + page: pageIndex, + perPage: pagination.pageSize, + }, + }); + }, + [onPaginationChange, pagination.pageSize] + ); + + return ( + + ); +}; + +ExceptionsViewerPaginationComponent.displayName = 'ExceptionsViewerPaginationComponent'; + +export const ExceptionsViewerPagination = React.memo(ExceptionsViewerPaginationComponent); + +ExceptionsViewerPagination.displayName = 'ExceptionsViewerPagination'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/reducer.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/reducer.ts new file mode 100644 index 0000000000000..dbf617e55f66f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/reducer.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExceptionListItemSchema, Pagination } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionsPagination } from '../../utils/types'; + +export type ViewerFlyoutName = 'addException' | 'editException' | null; +export type ViewerState = + | 'error' + | 'empty' + | 'empty_search' + | 'loading' + | 'searching' + | 'deleting' + | null; + +export interface State { + pagination: ExceptionsPagination; + // Individual exception items + exceptions: ExceptionListItemSchema[]; + // Exception item selected to update + exceptionToEdit: ExceptionListItemSchema | null; + // Flyout to be opened (edit vs add vs none) + currenFlyout: ViewerFlyoutName; + viewerState: ViewerState; +} + +export type Action = + | { + type: 'setExceptions'; + exceptions: ExceptionListItemSchema[]; + pagination: Pagination; + } + | { type: 'updateFlyoutOpen'; flyoutType: ViewerFlyoutName } + | { + type: 'updateExceptionToEdit'; + exception: ExceptionListItemSchema; + } + | { + type: 'setViewerState'; + state: ViewerState; + }; + +export const allExceptionItemsReducer = + () => + (state: State, action: Action): State => { + switch (action.type) { + case 'setExceptions': { + const { exceptions, pagination } = action; + + return { + ...state, + pagination: { + ...state.pagination, + pageIndex: pagination.page - 1, + pageSize: pagination.perPage, + totalItemCount: pagination.total ?? 0, + }, + exceptions, + }; + } + case 'updateExceptionToEdit': { + const { exception } = action; + return { + ...state, + exceptionToEdit: exception, + }; + } + case 'updateFlyoutOpen': { + return { + ...state, + currenFlyout: action.flyoutType, + }; + } + case 'setViewerState': { + return { + ...state, + viewerState: action.state, + }; + } + default: + return state; + } + }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.test.tsx new file mode 100644 index 0000000000000..454036b3f3036 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionsViewerSearchBar } from './search_bar'; + +describe('ExceptionsViewerSearchBar', () => { + it('it does not display add exception button if user is read only', () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"]').exists()).toBeFalsy(); + }); + + it('it invokes "onAddExceptionClick" when user selects to add an exception item', () => { + const mockOnAddExceptionClick = jest.fn(); + const wrapper = mount( + + ); + + wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"] button').simulate('click'); + + expect(wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"]').at(0).text()).toEqual( + 'Add rule exception' + ); + expect(mockOnAddExceptionClick).toHaveBeenCalledWith('detection'); + }); + + it('it invokes "onAddExceptionClick" when user selects to add an endpoint exception item', () => { + const mockOnAddExceptionClick = jest.fn(); + const wrapper = mount( + + ); + + wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"] button').simulate('click'); + + expect(wrapper.find('[data-test-subj="exceptionsHeaderAddExceptionBtn"]').at(0).text()).toEqual( + 'Add endpoint exception' + ); + expect(mockOnAddExceptionClick).toHaveBeenCalledWith('endpoint'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.tsx new file mode 100644 index 0000000000000..1cc371c440dba --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/search_bar.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSearchBar } from '@elastic/eui'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from '../../utils/translations'; +import type { GetExceptionItemProps } from '.'; + +const ITEMS_SCHEMA = { + strict: true, + fields: { + created_by: { + type: 'string', + }, + description: { + type: 'string', + }, + id: { + type: 'string', + }, + item_id: { + type: 'string', + }, + list_id: { + type: 'string', + }, + name: { + type: 'string', + }, + os_types: { + type: 'string', + }, + tags: { + type: 'string', + }, + }, +}; + +interface ExceptionsViewerSearchBarProps { + canAddException: boolean; + // Exception list type used to determine what type of item is + // being created when "onAddExceptionClick" is invoked + listType: ExceptionListTypeEnum; + isSearching: boolean; + onSearch: (arg: GetExceptionItemProps) => void; + onAddExceptionClick: (type: ExceptionListTypeEnum) => void; +} + +/** + * Search exception items and take actions (to creat an item) + */ +const ExceptionsViewerSearchBarComponent = ({ + canAddException, + listType, + isSearching, + onSearch, + onAddExceptionClick, +}: ExceptionsViewerSearchBarProps): JSX.Element => { + const handleOnSearch = useCallback( + ({ queryText }): void => { + onSearch({ search: queryText }); + }, + [onSearch] + ); + + const handleAddException = useCallback(() => { + onAddExceptionClick(listType); + }, [onAddExceptionClick, listType]); + + const addExceptionButtonText = useMemo(() => { + return listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ADD_TO_ENDPOINT_LIST + : i18n.ADD_TO_DETECTIONS_LIST; + }, [listType]); + + return ( + + + + + {!canAddException && ( + + + {addExceptionButtonText} + + + )} + + ); +}; + +ExceptionsViewerSearchBarComponent.displayName = 'ExceptionsViewerSearchBarComponent'; + +export const ExceptionsViewerSearchBar = React.memo(ExceptionsViewerSearchBarComponent); + +ExceptionsViewerSearchBar.displayName = 'ExceptionsViewerSearchBar'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/translations.ts new file mode 100644 index 0000000000000..727fb78bede2a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/translations.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const EXCEPTION_NO_SEARCH_RESULTS_PROMPT_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.noSearchResultsPromptTitle', + { + defaultMessage: 'No results match your search criteria', + } +); + +export const EXCEPTION_NO_SEARCH_RESULTS_PROMPT_BODY = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.noSearchResultsPromptBody', + { + defaultMessage: 'Try modifying your search.', + } +); + +export const EXCEPTION_EMPTY_PROMPT_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.addExceptionsEmptyPromptTitle', + { + defaultMessage: 'Add exceptions to this rule', + } +); + +export const EXCEPTION_EMPTY_PROMPT_BODY = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.emptyPromptBody', + { + defaultMessage: 'There are no exceptions for this rule. Create your first rule exception.', + } +); + +export const EXCEPTION_EMPTY_ENDPOINT_PROMPT_BODY = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.endpoint.emptyPromptBody', + { + defaultMessage: + 'There are no endpoint exceptions. Endpoint exceptions are applied to the endpoint and the detection rule. Create your first endpoint exception.', + } +); + +export const EXCEPTION_EMPTY_PROMPT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.emptyPromptButtonLabel', + { + defaultMessage: 'Add rule exception', + } +); + +export const EXCEPTION_EMPTY_PROMPT_ENDPOINT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.endpoint.emptyPromptButtonLabel', + { + defaultMessage: 'Add endpoint exception', + } +); + +export const EXCEPTION_ERROR_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionItemsFetchError', + { + defaultMessage: 'Unable to load exception items', + } +); + +export const EXCEPTION_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionItemsFetchErrorDescription', + { + defaultMessage: + 'There was an error loading the exception items. Contact your administrator for help.', + } +); + +export const EXCEPTION_SEARCH_ERROR_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionItemSearchErrorTitle', + { + defaultMessage: 'Error searching', + } +); + +export const EXCEPTION_SEARCH_ERROR_BODY = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionItemSearchErrorBody', + { + defaultMessage: 'An error occurred searching for exception items. Please try again.', + } +); + +export const EXCEPTION_DELETE_ERROR_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionDeleteErrorTitle', + { + defaultMessage: 'Error deleting exception item', + } +); + +export const EXCEPTION_ITEMS_PAGINATION_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.paginationAriaLabel', + { + defaultMessage: 'Exception item table pagination', + } +); + +export const EXCEPTION_ITEM_DELETE_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.allItems.exceptionItemDeleteSuccessTitle', + { + defaultMessage: 'Exception deleted', + } +); + +export const EXCEPTION_ITEM_DELETE_TEXT = (itemName: string) => + i18n.translate('xpack.securitySolution.exceptions.allItems.exceptionItemDeleteSuccessText', { + values: { itemName }, + defaultMessage: '"{itemName}" deleted successfully.', + }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx new file mode 100644 index 0000000000000..aa604dbfbf001 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +import { ExceptionsViewerUtility } from './utility_bar'; +import { TestProviders } from '../../../../common/mock'; + +describe('ExceptionsViewerUtility', () => { + it('it renders correct item counts', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsShowing"]').at(0).text()).toEqual( + 'Showing 1-50 of 105' + ); + }); + + it('it renders last updated message', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsViewerLastUpdated"]').at(0).text()).toEqual( + 'Updated now' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.tsx new file mode 100644 index 0000000000000..a68930a9a40b8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import type { ExceptionsPagination } from '../../utils/types'; +import { + UtilityBar, + UtilityBarSection, + UtilityBarGroup, + UtilityBarText, +} from '../../../../common/components/utility_bar'; +import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; + +const StyledText = styled.span` + font-weight: bold; +`; + +const MyUtilities = styled(EuiFlexGroup)` + height: 50px; +`; + +const StyledCondition = styled.span` + display: inline-block !important; + vertical-align: middle !important; +`; + +interface ExceptionsViewerUtilityProps { + pagination: ExceptionsPagination; + // Corresponds to last time exception items were fetched + lastUpdated: string | number | null; +} + +/** + * Utilities include exception item counts and group by options + */ +const ExceptionsViewerUtilityComponent: React.FC = ({ + pagination, + lastUpdated, +}): JSX.Element => ( + + + + + + + {`1-${Math.min( + pagination.pageSize, + pagination.totalItemCount + )}`} + ), + partTwo: {`${pagination.totalItemCount}`}, + }} + /> + + + + + + + + + + + ), + }} + /> + + + +); + +ExceptionsViewerUtilityComponent.displayName = 'ExceptionsViewerUtilityComponent'; + +export const ExceptionsViewerUtility = React.memo(ExceptionsViewerUtilityComponent); + +ExceptionsViewerUtility.displayName = 'ExceptionsViewerUtility'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx index 2885920222b5d..d5d5c3fc37316 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx @@ -12,10 +12,10 @@ import type { ReactWrapper } from 'enzyme'; import { mount } from 'enzyme'; import { EditExceptionFlyout } from '.'; -import { useCurrentUser } from '../../../lib/kibana'; -import { useFetchIndex } from '../../../containers/source'; +import { useCurrentUser } from '../../../../common/lib/kibana'; +import { useFetchIndex } from '../../../../common/containers/source'; import { stubIndexPattern, createStubIndexPattern } from '@kbn/data-plugin/common/stubs'; -import { useAddOrUpdateException } from '../use_add_exception'; +import { useAddOrUpdateException } from '../../logic/use_add_exception'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import type { EntriesArray } from '@kbn/securitysolution-io-ts-list-types'; @@ -24,7 +24,7 @@ import { getRulesSchemaMock, } from '../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; -import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; +import { getMockTheme } from '../../../../common/lib/kibana/kibana_react.mock'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; const mockTheme = getMockTheme({ @@ -36,11 +36,11 @@ const mockTheme = getMockTheme({ }, }); -jest.mock('../../../lib/kibana'); +jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../detections/containers/detection_engine/rules'); -jest.mock('../use_add_exception'); -jest.mock('../../../containers/source'); -jest.mock('../use_fetch_or_create_rule_exception_list'); +jest.mock('../../logic/use_add_exception'); +jest.mock('../../../../common/containers/source'); +jest.mock('../../logic/use_fetch_or_create_rule_exception_list'); jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../detections/containers/detection_engine/rules/use_rule_async'); jest.mock('@kbn/lists-plugin/public'); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx index 73095d11decaf..78e86dd4f1fa0 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx @@ -45,16 +45,16 @@ import { isNewTermsRule, isThresholdRule, } from '../../../../../common/detection_engine/utils'; -import { useFetchIndex } from '../../../containers/source'; +import { useFetchIndex } from '../../../../common/containers/source'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; import * as i18n from './translations'; -import * as sharedI18n from '../translations'; -import { useKibana } from '../../../lib/kibana'; -import { useAppToasts } from '../../../hooks/use_app_toasts'; -import { useAddOrUpdateException } from '../use_add_exception'; -import { AddExceptionComments } from '../add_exception_comments'; +import * as sharedI18n from '../../utils/translations'; +import { useKibana } from '../../../../common/lib/kibana'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useAddOrUpdateException } from '../../logic/use_add_exception'; +import { ExceptionItemComments } from '../item_comments'; import { enrichExistingExceptionItemWithComments, enrichExceptionItemsWithOS, @@ -62,8 +62,8 @@ import { entryHasNonEcsType, lowercaseHashValues, filterIndexPatterns, -} from '../helpers'; -import { Loader } from '../../loader'; +} from '../../utils/helpers'; +import { Loader } from '../../../../common/components/loader'; import type { ErrorInfo } from '../error_callout'; import { ErrorCallout } from '../error_callout'; @@ -410,7 +410,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ - (({ comments }) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + {i18n.exceptionItemCommentsAccordion(comments.length)} + + } + arrowDisplay="none" + data-test-subj="exceptionsViewerCommentAccordion" + > + + + + + + ); +}); + +ExceptionItemCardComments.displayName = 'ExceptionItemCardComments'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.test.tsx similarity index 98% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.test.tsx index e31f049e55e13..33fbb2150323c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { mount } from 'enzyme'; -import { TestProviders } from '../../../../mock'; -import { ExceptionItemCardConditions } from './exception_item_card_conditions'; +import { TestProviders } from '../../../../common/mock'; +import { ExceptionItemCardConditions } from './conditions'; describe('ExceptionItemCardConditions', () => { it('it includes os condition if one exists', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.tsx similarity index 94% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.tsx index 451cb3a4ecf31..95e3f7c211888 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/conditions.tsx @@ -6,7 +6,14 @@ */ import React, { memo, useMemo, useCallback } from 'react'; -import { EuiExpression, EuiToken, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; +import { + EuiExpression, + EuiToken, + EuiFlexGroup, + EuiFlexItem, + EuiBadge, + EuiPanel, +} from '@elastic/eui'; import styled from 'styled-components'; import type { EntryExists, @@ -21,7 +28,7 @@ import type { import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import * as i18n from './translations'; -import { ValueWithSpaceWarning } from '../value_with_space_warning/value_with_space_warning'; +import { ValueWithSpaceWarning } from '../value_with_space_warning'; const OS_LABELS = Object.freeze({ linux: i18n.OS_LINUX, @@ -60,6 +67,12 @@ const StyledCondition = styled('span')` margin-right: 6px; `; +const StyledConditionContent = styled(EuiPanel)` + border: 1px; + border-color: #d3dae6; + border-style: solid; +`; + export interface CriteriaConditionsProps { entries: ExceptionListItemSchema['entries']; dataTestSubj: string; @@ -148,7 +161,12 @@ export const ExceptionItemCardConditions = memo( ); return ( -
+ {osLabel != null && (
@@ -183,7 +201,7 @@ export const ExceptionItemCardConditions = memo(
); })} -
+ ); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/header.test.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/header.test.tsx index fe8811152e2e1..2a60e81375745 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/header.test.tsx @@ -11,8 +11,8 @@ import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas import { ThemeProvider } from 'styled-components'; import * as i18n from './translations'; -import { ExceptionItemCardHeader } from './exception_item_card_header'; -import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; +import { ExceptionItemCardHeader } from './header'; +import { getMockTheme } from '../../../../common/lib/kibana/kibana_react.mock'; const mockTheme = getMockTheme({ eui: { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/header.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/header.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx similarity index 52% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx index 46a0f74642c08..5219f5d72d847 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx @@ -6,40 +6,52 @@ */ import React from 'react'; -import { ThemeProvider } from 'styled-components'; import { mount } from 'enzyme'; import { ExceptionItemCard } from '.'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; -import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { TestProviders } from '../../../../common/mock'; -jest.mock('../../../../lib/kibana'); - -const mockTheme = getMockTheme({ - eui: { - euiColorDanger: '#ece', - euiColorLightestShade: '#ece', - euiColorPrimary: '#ece', - euiFontWeightSemiBold: 1, - }, -}); +jest.mock('../../../../common/lib/kibana'); describe('ExceptionItemCard', () => { it('it renders header, item meta information and conditions', () => { const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: [] }; const wrapper = mount( - + - + ); expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); @@ -54,16 +66,37 @@ describe('ExceptionItemCard', () => { const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: getCommentsArrayMock() }; const wrapper = mount( - + - + ); expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); @@ -78,16 +111,37 @@ describe('ExceptionItemCard', () => { const exceptionItem = getExceptionListItemSchemaMock(); const wrapper = mount( - + - + ); expect(wrapper.find('button[data-test-subj="item-actionButton"]').exists()).toBeFalsy(); @@ -98,16 +152,37 @@ describe('ExceptionItemCard', () => { const exceptionItem = getExceptionListItemSchemaMock(); const wrapper = mount( - + - + ); // click on popover @@ -127,16 +202,37 @@ describe('ExceptionItemCard', () => { const exceptionItem = getExceptionListItemSchemaMock(); const wrapper = mount( - + - + ); // click on popover @@ -150,6 +246,7 @@ describe('ExceptionItemCard', () => { expect(mockOnDeleteException).toHaveBeenCalledWith({ id: '1', + name: 'some name', namespaceType: 'single', }); }); @@ -158,16 +255,37 @@ describe('ExceptionItemCard', () => { const exceptionItem = getExceptionListItemSchemaMock(); exceptionItem.comments = getCommentsArrayMock(); const wrapper = mount( - + - + ); expect(wrapper.find('.euiAccordion-isOpen')).toHaveLength(0); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx similarity index 60% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx index 322050e27e4b8..cae3136dc6ad4 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx @@ -6,50 +6,46 @@ */ import type { EuiCommentProps } from '@elastic/eui'; -import { - EuiPanel, - EuiFlexGroup, - EuiCommentList, - EuiAccordion, - EuiFlexItem, - EuiText, - useEuiTheme, -} from '@elastic/eui'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useMemo, useCallback } from 'react'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { getFormattedComments } from '../../helpers'; -import type { ExceptionListItemIdentifiers } from '../../types'; +import { getFormattedComments } from '../../utils/helpers'; +import type { ExceptionListItemIdentifiers } from '../../utils/types'; import * as i18n from './translations'; -import { ExceptionItemCardHeader } from './exception_item_card_header'; -import { ExceptionItemCardConditions } from './exception_item_card_conditions'; -import { ExceptionItemCardMetaInfo } from './exception_item_card_meta'; +import { ExceptionItemCardHeader } from './header'; +import { ExceptionItemCardConditions } from './conditions'; +import { ExceptionItemCardMetaInfo } from './meta'; +import type { RuleReferenceSchema } from '../../../../../common/detection_engine/schemas/response'; +import { ExceptionItemCardComments } from './comments'; export interface ExceptionItemProps { - loadingItemIds: ExceptionListItemIdentifiers[]; exceptionItem: ExceptionListItemSchema; + listType: ExceptionListTypeEnum; + disableActions: boolean; + ruleReferences: RuleReferenceSchema[]; onDeleteException: (arg: ExceptionListItemIdentifiers) => void; onEditException: (item: ExceptionListItemSchema) => void; - disableActions: boolean; dataTestSubj: string; } const ExceptionItemCardComponent = ({ disableActions, - loadingItemIds, exceptionItem, + listType, + ruleReferences, onDeleteException, onEditException, dataTestSubj, }: ExceptionItemProps): JSX.Element => { - const { euiTheme } = useEuiTheme(); - const handleDelete = useCallback((): void => { onDeleteException({ id: exceptionItem.id, + name: exceptionItem.name, namespaceType: exceptionItem.namespace_type, }); - }, [onDeleteException, exceptionItem.id, exceptionItem.namespace_type]); + }, [onDeleteException, exceptionItem.id, exceptionItem.name, exceptionItem.namespace_type]); const handleEdit = useCallback((): void => { onEditException(exceptionItem); @@ -59,11 +55,6 @@ const ExceptionItemCardComponent = ({ return getFormattedComments(exceptionItem.comments); }, [exceptionItem.comments]); - const disableItemActions = useMemo((): boolean => { - const foundItems = loadingItemIds.some(({ id }) => id === exceptionItem.id); - return disableActions || foundItems; - }, [loadingItemIds, exceptionItem.id, disableActions]); - return ( @@ -73,24 +64,31 @@ const ExceptionItemCardComponent = ({ actions={[ { key: 'edit', - icon: 'pencil', - label: i18n.EXCEPTION_ITEM_EDIT_BUTTON, + icon: 'controlsHorizontal', + label: + listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ENDPOINT_EXCEPTION_ITEM_EDIT_BUTTON + : i18n.EXCEPTION_ITEM_EDIT_BUTTON, onClick: handleEdit, }, { key: 'delete', icon: 'trash', - label: i18n.EXCEPTION_ITEM_DELETE_BUTTON, + label: + listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ENDPOINT_EXCEPTION_ITEM_DELETE_BUTTON + : i18n.EXCEPTION_ITEM_DELETE_BUTTON, onClick: handleDelete, }, ]} - disableActions={disableItemActions} + disableActions={disableActions} dataTestSubj="exceptionItemCardHeader" />
@@ -101,24 +99,7 @@ const ExceptionItemCardComponent = ({ dataTestSubj="exceptionItemCardConditions" />
- {formattedComments.length > 0 && ( - - - {i18n.exceptionItemCommentsAccordion(formattedComments.length)} - - } - arrowDisplay="none" - data-test-subj="exceptionsViewerCommentAccordion" - > - - - - - - )} + {formattedComments.length > 0 && } ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.test.tsx new file mode 100644 index 0000000000000..c755321f5f4ea --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.test.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 from 'react'; +import { mount } from 'enzyme'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; + +import { ExceptionItemCardMetaInfo } from './meta'; +import { TestProviders } from '../../../../common/mock'; + +describe('ExceptionItemCardMetaInfo', () => { + it('it renders item creation info', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value1"]').at(0).text() + ).toEqual('Apr 20, 2020 @ 15:25:31.830'); + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value2"]').at(0).text() + ).toEqual('some user'); + }); + + it('it renders item update info', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value1"]').at(0).text() + ).toEqual('Apr 20, 2020 @ 15:25:31.830'); + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value2"]').at(0).text() + ).toEqual('some user'); + }); + + it('it renders references info', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-affectedRulesButton"]').at(0).text() + ).toEqual('Affects 1 rule'); + }); + + it('it renders references info when multiple references exist', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-affectedRulesButton"]').at(0).text() + ).toEqual('Affects 2 rules'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx new file mode 100644 index 0000000000000..a24526dd04e3a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo, useState } from 'react'; +import type { EuiContextMenuPanelProps } from '@elastic/eui'; +import { + EuiBadge, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, + EuiText, + EuiButtonEmpty, + EuiPopover, +} from '@elastic/eui'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import styled from 'styled-components'; + +import * as i18n from './translations'; +import { FormattedDate } from '../../../../common/components/formatted_date'; +import { SecurityPageName } from '../../../../../common/constants'; +import type { RuleReferenceSchema } from '../../../../../common/detection_engine/schemas/response'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; +import { RuleDetailTabs } from '../../../../detections/pages/detection_engine/rules/details'; +import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; + +const StyledFlexItem = styled(EuiFlexItem)` + border-right: 1px solid #d3dae6; + padding: 4px 12px 4px 0; +`; + +export interface ExceptionItemCardMetaInfoProps { + item: ExceptionListItemSchema; + references: RuleReferenceSchema[]; + dataTestSubj: string; +} + +export const ExceptionItemCardMetaInfo = memo( + ({ item, references, dataTestSubj }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onAffectedRulesClick = () => setIsPopoverOpen((isOpen) => !isOpen); + const onClosePopover = () => setIsPopoverOpen(false); + + const itemActions = useMemo((): EuiContextMenuPanelProps['items'] => { + if (references == null) { + return []; + } + return references.map((reference) => ( + + + + {reference.name} + + + + )); + }, [references, dataTestSubj]); + + return ( + + + } + value2={item.created_by} + dataTestSubj={`${dataTestSubj}-createdBy`} + /> + + + } + value2={item.updated_by} + dataTestSubj={`${dataTestSubj}-updatedBy`} + /> + + + + {i18n.AFFECTED_RULES(references?.length ?? 0)} + + } + panelPaddingSize="none" + isOpen={isPopoverOpen} + closePopover={onClosePopover} + data-test-subj={`${dataTestSubj}-items`} + > + + + + + ); + } +); +ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; + +interface MetaInfoDetailsProps { + fieldName: string; + label: string; + value1: JSX.Element | string; + value2: string; + dataTestSubj: string; +} + +const MetaInfoDetails = memo(({ label, value1, value2, dataTestSubj }) => { + return ( + + + + {label} + + + + + {value1} + + + + + {i18n.EXCEPTION_ITEM_META_BY} + + + + + + + {value2} + + + + + + ); +}); + +MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/translations.ts similarity index 84% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/translations.ts index 5d46243697355..ccdd5eebf3b8c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/translations.ts @@ -10,14 +10,28 @@ import { i18n } from '@kbn/i18n'; export const EXCEPTION_ITEM_EDIT_BUTTON = i18n.translate( 'xpack.securitySolution.exceptions.exceptionItem.editItemButton', { - defaultMessage: 'Edit item', + defaultMessage: 'Edit rule exception', } ); export const EXCEPTION_ITEM_DELETE_BUTTON = i18n.translate( 'xpack.securitySolution.exceptions.exceptionItem.deleteItemButton', { - defaultMessage: 'Delete item', + defaultMessage: 'Delete rule exception', + } +); + +export const ENDPOINT_EXCEPTION_ITEM_EDIT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.endpoint.editItemButton', + { + defaultMessage: 'Edit endpoint exception', + } +); + +export const ENDPOINT_EXCEPTION_ITEM_DELETE_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.endpoint.deleteItemButton', + { + defaultMessage: 'Delete endpoint exception', } ); @@ -159,3 +173,9 @@ export const OS_MAC = i18n.translate( defaultMessage: 'Mac', } ); + +export const AFFECTED_RULES = (numRules: number) => + i18n.translate('xpack.securitySolution.exceptions.exceptionItem.affectedRules', { + values: { numRules }, + defaultMessage: 'Affects {numRules} {numRules, plural, =1 {rule} other {rules}}', + }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx index aa927eef34d76..c83e729669813 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_comments.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx @@ -18,11 +18,11 @@ import { EuiText, } from '@elastic/eui'; import type { Comment } from '@kbn/securitysolution-io-ts-list-types'; -import * as i18n from './translations'; -import { useCurrentUser } from '../../lib/kibana'; -import { getFormattedComments } from './helpers'; +import * as i18n from '../../utils/translations'; +import { useCurrentUser } from '../../../../common/lib/kibana'; +import { getFormattedComments } from '../../utils/helpers'; -interface AddExceptionCommentsProps { +interface ExceptionItemCommentsProps { exceptionItemComments?: Comment[]; newCommentValue: string; newCommentOnChange: (value: string) => void; @@ -45,11 +45,11 @@ const CommentAccordion = styled(EuiAccordion)` `} `; -export const AddExceptionComments = memo(function AddExceptionComments({ +export const ExceptionItemComments = memo(function ExceptionItemComments({ exceptionItemComments, newCommentValue, newCommentOnChange, -}: AddExceptionCommentsProps) { +}: ExceptionItemCommentsProps) { const [shouldShowComments, setShouldShowComments] = useState(false); const currentUser = useCurrentUser(); @@ -71,7 +71,7 @@ export const AddExceptionComments = memo(function AddExceptionComments({ const commentsAccordionTitle = useMemo(() => { if (exceptionItemComments && exceptionItemComments.length > 0) { return ( - + {!shouldShowComments ? i18n.COMMENTS_SHOW(exceptionItemComments.length) : i18n.COMMENTS_HIDE(exceptionItemComments.length)} @@ -97,7 +97,7 @@ export const AddExceptionComments = memo(function AddExceptionComments({ id={'add-exception-comments-accordion'} buttonClassName={COMMENT_ACCORDION_BUTTON_CLASS_NAME} buttonContent={commentsAccordionTitle} - data-test-subj="addExceptionCommentsAccordion" + data-test-subj="ExceptionItemCommentsAccordion" onToggle={(isOpen) => handleTriggerOnClick(isOpen)} > diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/__snapshots__/value_with_space_warning.test.tsx.snap b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/__snapshots__/value_with_space_warning.test.tsx.snap similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/__snapshots__/value_with_space_warning.test.tsx.snap rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/__snapshots__/value_with_space_warning.test.tsx.snap diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/use_value_with_space_warning.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/use_value_with_space_warning.test.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/use_value_with_space_warning.test.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/use_value_with_space_warning.test.ts diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/value_with_space_warning.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/value_with_space_warning.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/__tests__/value_with_space_warning.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/__tests__/value_with_space_warning.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/index.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/index.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/index.ts diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/use_value_with_space_warning.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/use_value_with_space_warning.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/use_value_with_space_warning.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/use_value_with_space_warning.ts diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/value_with_space_warning.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/value_with_space_warning.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/value_with_space_warning/value_with_space_warning.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/value_with_space_warning/value_with_space_warning.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.test.tsx similarity index 98% rename from x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.test.tsx index a451ee9e4963f..36446f9ca2a4b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.test.tsx @@ -9,7 +9,7 @@ import type { RenderHookResult } from '@testing-library/react-hooks'; import { act, renderHook } from '@testing-library/react-hooks'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { coreMock } from '@kbn/core/public/mocks'; -import { KibanaServices } from '../../lib/kibana'; +import { KibanaServices } from '../../../common/lib/kibana'; import * as alertsApi from '../../../detections/containers/detection_engine/alerts/api'; import * as listsApi from '@kbn/securitysolution-list-api'; @@ -23,7 +23,7 @@ import type { CreateExceptionListItemSchema, UpdateExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import { TestProviders } from '../../mock'; +import { TestProviders } from '../../../common/mock'; import type { UseAddOrUpdateExceptionProps, ReturnUseAddOrUpdateException, @@ -33,7 +33,7 @@ import { useAddOrUpdateException } from './use_add_exception'; const mockKibanaHttpService = coreMock.createStart().http; const mockKibanaServices = KibanaServices.get as jest.Mock; -jest.mock('../../lib/kibana'); +jest.mock('../../../common/lib/kibana'); jest.mock('@kbn/securitysolution-list-api'); const fetchMock = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.tsx similarity index 98% rename from x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.tsx index c06f4928ad894..88556d305bb03 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_add_exception.tsx @@ -22,8 +22,8 @@ import { } from '../../../detections/components/alerts_table/default_config'; import { getQueryFilter } from '../../../../common/detection_engine/get_query_filter'; import type { Index } from '../../../../common/detection_engine/schemas/common/schemas'; -import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from './helpers'; -import { useKibana } from '../../lib/kibana'; +import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from '../utils/helpers'; +import { useKibana } from '../../../common/lib/kibana'; /** * Adds exception items to the list. Also optionally closes alerts. diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_fetch_or_create_rule_exception_list.tsx diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx new file mode 100644 index 0000000000000..f30d5aeb598a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.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 { useEffect, useMemo, useState } from 'react'; +import type { ListArray } from '@kbn/securitysolution-io-ts-list-types'; + +import type { RuleReferenceSchema } from '../../../../common/detection_engine/schemas/response'; +import { findRuleExceptionReferences } from '../../../detections/containers/detection_engine/rules/api'; +import { useToasts } from '../../../common/lib/kibana'; +import type { FindRulesReferencedByExceptionsListProp } from '../../../detections/containers/detection_engine/rules/types'; +import * as i18n from '../utils/translations'; + +export type ReturnUseFindExceptionListReferences = [boolean, RuleReferences | null]; + +export interface RuleReferences { + [key: string]: RuleReferenceSchema[]; +} +/** + * Hook for finding what rules are referenced by a set of exception lists + * @param ruleExceptionLists array of exception list info stored on a rule + */ +export const useFindExceptionListReferences = ( + ruleExceptionLists: ListArray +): ReturnUseFindExceptionListReferences => { + const toasts = useToasts(); + const [isLoading, setIsLoading] = useState(false); + const [references, setReferences] = useState(null); + const listRefs = useMemo((): FindRulesReferencedByExceptionsListProp[] => { + return ruleExceptionLists.map((list) => { + return { + id: list.id, + listId: list.list_id, + namespaceType: list.namespace_type, + }; + }); + }, [ruleExceptionLists]); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + + const findReferences = async () => { + try { + setIsLoading(true); + + const { references: referencesResults } = await findRuleExceptionReferences({ + lists: listRefs, + signal: abortCtrl.signal, + }); + + const results = referencesResults.reduce((acc, result) => { + const [[key, value]] = Object.entries(result); + + acc[key] = value; + + return acc; + }, {}); + + if (isSubscribed) { + setIsLoading(false); + setReferences(results); + } + } catch (error) { + if (isSubscribed) { + setIsLoading(false); + toasts.addError(error, { title: i18n.ERROR_FETCHING_REFERENCES_TITLE }); + } + } + }; + + if (listRefs.length === 0 && isSubscribed) { + setIsLoading(false); + setReferences(null); + } else { + findReferences(); + } + + return (): void => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [ruleExceptionLists, listRefs, toasts]); + + return [isLoading, references]; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_endpoint_fields.json similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_endpoint_fields.json diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_linux_fields.json b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_linux_fields.json similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_linux_fields.json rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_linux_fields.json diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_windows_mac_fields.json b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_windows_mac_fields.json similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_windows_mac_fields.json rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/exceptionable_windows_mac_fields.json diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx similarity index 99% rename from x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx index c7890f99dc44b..e4087567de39c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx @@ -42,7 +42,7 @@ import type { AlertData, Flattened } from './types'; import type { Ecs } from '../../../../common/ecs'; import type { CodeSignature } from '../../../../common/ecs/file'; -import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard'; +import { WithCopyToClipboard } from '../../../common/lib/clipboard/with_copy_to_clipboard'; import exceptionableLinuxFields from './exceptionable_linux_fields.json'; import exceptionableWindowsMacFields from './exceptionable_windows_mac_fields.json'; import exceptionableEndpointFields from './exceptionable_endpoint_fields.json'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/translations.ts new file mode 100644 index 0000000000000..8f189c7aaf7db --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/translations.ts @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 COMMENTS_SHOW = (comments: number) => + i18n.translate('xpack.securitySolution.exceptions.showCommentsLabel', { + values: { comments }, + defaultMessage: 'Show ({comments}) {comments, plural, =1 {Comment} other {Comments}}', + }); + +export const COMMENTS_HIDE = (comments: number) => + i18n.translate('xpack.securitySolution.exceptions.hideCommentsLabel', { + values: { comments }, + defaultMessage: 'Hide ({comments}) {comments, plural, =1 {Comment} other {Comments}}', + }); + +export const COMMENT_EVENT = i18n.translate('xpack.securitySolution.exceptions.commentEventLabel', { + defaultMessage: 'added a comment', +}); + +export const OPERATING_SYSTEM_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.operatingSystemFullLabel', + { + defaultMessage: 'Operating System', + } +); + +export const ADD_TO_ENDPOINT_LIST = i18n.translate( + 'xpack.securitySolution.exceptions.viewer.addToEndpointListLabel', + { + defaultMessage: 'Add endpoint exception', + } +); + +export const ADD_TO_DETECTIONS_LIST = i18n.translate( + 'xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel', + { + defaultMessage: 'Add rule exception', + } +); + +export const ADD_COMMENT_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.exceptions.viewer.addCommentPlaceholder', + { + defaultMessage: 'Add a new comment...', + } +); + +export const ADD_TO_CLIPBOARD = i18n.translate( + 'xpack.securitySolution.exceptions.viewer.addToClipboard', + { + defaultMessage: 'Comment', + } +); + +export const CLEAR_EXCEPTIONS_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.clearExceptionsLabel', + { + defaultMessage: 'Remove Exception List', + } +); + +export const ADD_EXCEPTION_FETCH_404_ERROR = (listId: string) => + i18n.translate('xpack.securitySolution.exceptions.fetch404Error', { + values: { listId }, + defaultMessage: + 'The associated exception list ({listId}) no longer exists. Please remove the missing exception list to add additional exceptions to the detection rule.', + }); + +export const ADD_EXCEPTION_FETCH_ERROR = i18n.translate( + 'xpack.securitySolution.exceptions.fetchError', + { + defaultMessage: 'Error fetching exception list', + } +); + +export const ERROR = i18n.translate('xpack.securitySolution.exceptions.errorLabel', { + defaultMessage: 'Error', +}); + +export const CANCEL = i18n.translate('xpack.securitySolution.exceptions.cancelLabel', { + defaultMessage: 'Cancel', +}); + +export const MODAL_ERROR_ACCORDION_TEXT = i18n.translate( + 'xpack.securitySolution.exceptions.modalErrorAccordionText', + { + defaultMessage: 'Show rule reference information:', + } +); + +export const DISSASOCIATE_LIST_SUCCESS = (id: string) => + i18n.translate('xpack.securitySolution.exceptions.dissasociateListSuccessText', { + values: { id }, + defaultMessage: 'Exception list ({id}) has successfully been removed', + }); + +export const DISSASOCIATE_EXCEPTION_LIST_ERROR = i18n.translate( + 'xpack.securitySolution.exceptions.dissasociateExceptionListError', + { + defaultMessage: 'Failed to remove exception list', + } +); + +export const OPERATING_SYSTEM_WINDOWS = i18n.translate( + 'xpack.securitySolution.exceptions.operatingSystemWindows', + { + defaultMessage: 'Windows', + } +); + +export const OPERATING_SYSTEM_MAC = i18n.translate( + 'xpack.securitySolution.exceptions.operatingSystemMac', + { + defaultMessage: 'macOS', + } +); + +export const OPERATING_SYSTEM_WINDOWS_AND_MAC = i18n.translate( + 'xpack.securitySolution.exceptions.operatingSystemWindowsAndMac', + { + defaultMessage: 'Windows and macOS', + } +); + +export const OPERATING_SYSTEM_LINUX = i18n.translate( + 'xpack.securitySolution.exceptions.operatingSystemLinux', + { + defaultMessage: 'Linux', + } +); + +export const ERROR_FETCHING_REFERENCES_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle', + { + defaultMessage: 'Error fetching exception references', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/types.ts similarity index 83% rename from x-pack/plugins/security_solution/public/common/components/exceptions/types.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/types.ts index fe0f137800d26..56db2ad8257f8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/types.ts @@ -11,21 +11,10 @@ import type { CodeSignature } from '../../../../common/ecs/file'; export interface ExceptionListItemIdentifiers { id: string; + name: string; namespaceType: NamespaceType; } -export interface FilterOptions { - filter: string; - tags: string[]; -} - -export interface Filter { - filter: Partial; - pagination: Partial; - showDetectionsListsOnly: boolean; - showEndpointListsOnly: boolean; -} - export interface ExceptionsPagination { pageIndex: number; pageSize: number; 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 436c4f63ac77c..0127fe63efea7 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 @@ -17,17 +17,17 @@ import { DEFAULT_ACTION_BUTTON_WIDTH } from '@kbn/timelines-plugin/public'; import { useOsqueryContextActionItem } from '../../osquery/use_osquery_context_action_item'; import { OsqueryFlyout } from '../../osquery/osquery_flyout'; import { useRouteSpy } from '../../../../common/utils/route/use_route_spy'; -import { buildGetAlertByIdQuery } from '../../../../common/components/exceptions/helpers'; +import { buildGetAlertByIdQuery } from '../../../../detection_engine/rule_exceptions/utils/helpers'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { EventsTdContent } from '../../../../timelines/components/timeline/styles'; import type { Ecs } from '../../../../../common/ecs'; -import type { AddExceptionFlyoutProps } from '../../../../common/components/exceptions/add_exception_flyout'; -import { AddExceptionFlyout } from '../../../../common/components/exceptions/add_exception_flyout'; +import type { AddExceptionFlyoutProps } from '../../../../detection_engine/rule_exceptions/components/add_exception_flyout'; +import { AddExceptionFlyout } from '../../../../detection_engine/rule_exceptions/components/add_exception_flyout'; import * as i18n from '../translations'; import type { inputsModel, State } from '../../../../common/store'; import { inputsSelectors } from '../../../../common/store'; import { TimelineId } from '../../../../../common/types'; -import type { AlertData, EcsHit } from '../../../../common/components/exceptions/types'; +import type { AlertData, EcsHit } from '../../../../detection_engine/rule_exceptions/utils/types'; import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query'; import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants'; import { useSignalIndex } from '../../../containers/detection_engine/alerts/use_signal_index'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.test.tsx index 5eebdd18acfd4..f4b581060e1ef 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.test.tsx @@ -11,7 +11,6 @@ import { KibanaServices, useKibana } from '../../../../common/lib/kibana'; import type { Ecs } from '../../../../../common/ecs'; import * as actions from '../actions'; import { coreMock } from '@kbn/core/public/mocks'; -import type { SendAlertToTimelineActionProps } from '../types'; import { InvestigateInTimelineAction } from './investigate_in_timeline_action'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; @@ -30,9 +29,26 @@ const ecsRowData: Ecs = { }; jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/lib/apm/use_start_transaction'); jest.mock('../../../../common/hooks/use_app_toasts'); jest.mock('../actions'); +(KibanaServices.get as jest.Mock).mockReturnValue(coreMock.createStart()); +const mockSendAlertToTimeline = jest.spyOn(actions, 'sendAlertToTimelineAction'); +(useKibana as jest.Mock).mockReturnValue({ + services: { + data: { + search: { + searchStrategyClient: jest.fn(), + }, + query: jest.fn(), + }, + }, +}); +(useAppToasts as jest.Mock).mockReturnValue({ + addError: jest.fn(), +}); + const props = { ecsRowData, onInvestigateInTimelineAlertClick: () => {}, @@ -40,28 +56,8 @@ const props = { }; describe('use investigate in timeline hook', () => { - let mockSendAlertToTimeline: jest.SpyInstance, [SendAlertToTimelineActionProps]>; - - beforeEach(() => { - const coreStartMock = coreMock.createStart(); - (KibanaServices.get as jest.Mock).mockReturnValue(coreStartMock); - mockSendAlertToTimeline = jest.spyOn(actions, 'sendAlertToTimelineAction'); - (useKibana as jest.Mock).mockReturnValue({ - services: { - data: { - search: { - searchStrategyClient: jest.fn(), - }, - query: jest.fn(), - }, - }, - }); - (useAppToasts as jest.Mock).mockReturnValue({ - addError: jest.fn(), - }); - }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); test('it creates a component and click handler', () => { const wrapper = render( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx index 4fd5ebc48e49b..eaef9169925ae 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx @@ -12,7 +12,6 @@ import type { Ecs } from '../../../../../common/ecs'; import { useInvestigateInTimeline } from './use_investigate_in_timeline'; import * as actions from '../actions'; import { coreMock } from '@kbn/core/public/mocks'; -import type { SendAlertToTimelineActionProps } from '../types'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; const ecsRowData: Ecs = { @@ -30,37 +29,34 @@ const ecsRowData: Ecs = { }; jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/lib/apm/use_start_transaction'); jest.mock('../../../../common/hooks/use_app_toasts'); jest.mock('../actions'); +(KibanaServices.get as jest.Mock).mockReturnValue(coreMock.createStart()); +const mockSendAlertToTimeline = jest.spyOn(actions, 'sendAlertToTimelineAction'); +(useKibana as jest.Mock).mockReturnValue({ + services: { + data: { + search: { + searchStrategyClient: jest.fn(), + }, + query: jest.fn(), + }, + }, +}); +(useAppToasts as jest.Mock).mockReturnValue({ + addError: jest.fn(), +}); + const props = { ecsRowData, onInvestigateInTimelineAlertClick: () => {}, }; describe('use investigate in timeline hook', () => { - let mockSendAlertToTimeline: jest.SpyInstance, [SendAlertToTimelineActionProps]>; - - beforeEach(() => { - const coreStartMock = coreMock.createStart(); - (KibanaServices.get as jest.Mock).mockReturnValue(coreStartMock); - mockSendAlertToTimeline = jest.spyOn(actions, 'sendAlertToTimelineAction'); - (useKibana as jest.Mock).mockReturnValue({ - services: { - data: { - search: { - searchStrategyClient: jest.fn(), - }, - query: jest.fn(), - }, - }, - }); - (useAppToasts as jest.Mock).mockReturnValue({ - addError: jest.fn(), - }); - }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); test('it creates a component and click handler', () => { const { result } = renderHook(() => useInvestigateInTimeline(props), { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index 577ecca52c59e..8d5eb34fe580f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -30,6 +30,8 @@ import { ACTION_INVESTIGATE_IN_TIMELINE } from '../translations'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { getField } from '../../../../helpers'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; +import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions'; interface UseInvestigateInTimelineActionProps { ecsRowData?: Ecs | Ecs[] | null; @@ -45,6 +47,7 @@ export const useInvestigateInTimeline = ({ data: { search: searchStrategyClient, query }, } = useKibana().services; const dispatch = useDispatch(); + const { startTransaction } = useStartTransaction(); const { services } = useKibana(); const { getExceptionListsItems } = useApi(services.http); @@ -73,7 +76,6 @@ export const useInvestigateInTimeline = ({ if (exceptionsLists.length > 0) { await getExceptionListsItems({ lists: exceptionsLists, - filterOptions: [], pagination: { page: 0, perPage: 10000, @@ -142,6 +144,7 @@ export const useInvestigateInTimeline = ({ ); const investigateInTimelineAlertClick = useCallback(async () => { + startTransaction({ name: ALERTS_ACTIONS.INVESTIGATE_IN_TIMELINE }); if (onInvestigateInTimelineAlertClick) { onInvestigateInTimelineAlertClick(); } @@ -155,6 +158,7 @@ export const useInvestigateInTimeline = ({ }); } }, [ + startTransaction, createTimeline, ecsRowData, onInvestigateInTimelineAlertClick, 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 126f057742901..4999d757cd047 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 @@ -25,16 +25,19 @@ const OsqueryActionWrapper = styled.div` `; export interface OsqueryFlyoutProps { - agentId: string; + agentId?: string; + defaultValues?: {}; onClose: () => void; } -const TimelineComponent = React.memo((props) => { - return ; -}); +const TimelineComponent = React.memo((props) => ); TimelineComponent.displayName = 'TimelineComponent'; -export const OsqueryFlyoutComponent: React.FC = ({ agentId, onClose }) => { +export const OsqueryFlyoutComponent: React.FC = ({ + agentId, + defaultValues, + onClose, +}) => { const { services: { osquery, timelines }, } = useKibana(); @@ -70,30 +73,38 @@ export const OsqueryFlyoutComponent: React.FC = ({ agentId, }, [getAddToTimelineButton] ); - // @ts-expect-error - const { OsqueryAction } = osquery; - return ( - - - -

{ACTION_OSQUERY}

-
-
- - - - - - - - -
- ); + + if (osquery?.OsqueryAction) { + return ( + + + +

{ACTION_OSQUERY}

+
+
+ + + + + + + + +
+ ); + } + + return null; }; export const OsqueryFlyout = React.memo(OsqueryFlyoutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index 48fa12f03565f..2511e8da834f0 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -20,6 +20,7 @@ import { fetchTags, getPrePackagedRulesStatus, previewRule, + findRuleExceptionReferences, } from './api'; import { getRulesSchemaMock } from '../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; import { @@ -28,6 +29,8 @@ import { } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getPatchRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/patch_rules_schema.mock'; import { rulesMock } from './mock'; +import type { FindRulesReferencedByExceptionsListProp } from './types'; +import { DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL } from '../../../../../common/constants'; const abortCtrl = new AbortController(); const mockKibanaServices = KibanaServices.get as jest.Mock; @@ -664,4 +667,36 @@ describe('Detections Rules API', () => { expect(resp).toEqual(prePackagedRulesStatus); }); }); + + describe('findRuleExceptionReferences', () => { + beforeEach(() => { + fetchMock.mockClear(); + fetchMock.mockResolvedValue(getRulesSchemaMock()); + }); + + test('GETs exception references', async () => { + const payload: FindRulesReferencedByExceptionsListProp[] = [ + { + id: '123', + listId: 'list_id_1', + namespaceType: 'single', + }, + { + id: '456', + listId: 'list_id_2', + namespaceType: 'single', + }, + ]; + await findRuleExceptionReferences({ lists: payload, signal: abortCtrl.signal }); + expect(fetchMock).toHaveBeenCalledWith(DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, { + query: { + ids: '123,456', + list_ids: 'list_id_1,list_id_2', + namespace_types: 'single,single', + }, + method: 'GET', + signal: abortCtrl.signal, + }); + }); + }); }); 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 876a3a0a469a8..f2e78eeee99ef 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 @@ -17,6 +17,7 @@ import { DETECTION_ENGINE_RULES_PREVIEW, DETECTION_ENGINE_INSTALLED_INTEGRATIONS_URL, DETECTION_ENGINE_RULES_URL_FIND, + DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, } from '../../../../../common/constants'; import type { BulkAction } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import type { @@ -26,6 +27,7 @@ import type { import type { RulesSchema, GetInstalledIntegrationsResponse, + RulesReferencedByExceptionListsSchema, } from '../../../../../common/detection_engine/schemas/response'; import type { @@ -43,6 +45,7 @@ import type { BulkActionProps, BulkActionResponseMap, PreviewRulesProps, + FindRulesReferencedByExceptionsProps, } from './types'; import { KibanaServices } from '../../../../common/lib/kibana'; import * as i18n from '../../../pages/detection_engine/rules/translations'; @@ -369,3 +372,28 @@ export const fetchInstalledIntegrations = async ({ signal, } ); + +/** + * Fetch info on what exceptions lists are referenced by what rules + * + * @param lists exception list information needed for making request + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +export const findRuleExceptionReferences = async ({ + lists, + signal, +}: FindRulesReferencedByExceptionsProps): Promise => + KibanaServices.get().http.fetch( + DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + { + method: 'GET', + query: { + ids: lists.map(({ id }) => id).join(','), + list_ids: lists.map(({ listId }) => listId).join(','), + namespace_types: lists.map(({ namespaceType }) => namespaceType).join(','), + }, + signal, + } + ); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts index 45c89c307de4e..77811b3d8aa2d 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts @@ -7,6 +7,7 @@ import * as t from 'io-ts'; +import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; import { listArray } from '@kbn/securitysolution-io-ts-list-types'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { @@ -341,3 +342,14 @@ export interface PrePackagedRulesStatusResponse { timelines_not_installed: number; timelines_not_updated: number; } + +export interface FindRulesReferencedByExceptionsListProp { + id: string; + listId: string; + namespaceType: NamespaceType; +} + +export interface FindRulesReferencedByExceptionsProps { + lists: FindRulesReferencedByExceptionsListProp[]; + signal?: AbortSignal; +} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx index 66892235a0f91..6c5ff0ab2851e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx @@ -294,4 +294,70 @@ describe('RuleDetailsPageComponent', () => { }); }); }); + + it('renders exceptions tab', async () => { + await setup(); + (useRuleWithFallback as jest.Mock).mockReturnValue({ + error: null, + loading: false, + isExistingRule: true, + refresh: jest.fn(), + rule: { + ...mockRule, + outcome: 'conflict', + alias_target_id: 'aliased_rule_id', + alias_purpose: 'savedObjectConversion', + }, + }); + const wrapper = mount( + + + + + + ); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="navigation-rule_exceptions"]').exists()).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="navigation-endpoint_exceptions"]').exists() + ).toBeFalsy(); + }); + }); + + it('renders endpoint exeptions tab when rule includes endpoint list', async () => { + await setup(); + (useRuleWithFallback as jest.Mock).mockReturnValue({ + error: null, + loading: false, + isExistingRule: true, + refresh: jest.fn(), + rule: { + ...mockRule, + outcome: 'conflict', + alias_target_id: 'aliased_rule_id', + alias_purpose: 'savedObjectConversion', + exceptions_list: [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + type: 'endpoint', + namespace_type: 'agnostic', + }, + ], + }, + }); + const wrapper = mount( + + + + + + ); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="navigation-rule_exceptions"]').exists()).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="navigation-endpoint_exceptions"]').exists() + ).toBeTruthy(); + }); + }); }); 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 0660df924f56a..cc36e6f75da4b 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 @@ -26,7 +26,6 @@ import { Switch, useParams } from 'react-router-dom'; import type { ConnectedProps } from 'react-redux'; import { connect, useDispatch } from 'react-redux'; import styled from 'styled-components'; -import type { ExceptionListIdentifiers } from '@kbn/securitysolution-io-ts-list-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { Dispatch } from 'redux'; @@ -79,8 +78,7 @@ import { hasMlLicense } from '../../../../../../common/machine_learning/has_ml_l import { SecurityPageName } from '../../../../../app/types'; import { LinkButton } from '../../../../../common/components/links'; import { useFormatUrl } from '../../../../../common/components/link_to'; -import { ExceptionsViewer } from '../../../../../common/components/exceptions/viewer'; -import { APP_UI_ID, DEFAULT_INDEX_PATTERN } from '../../../../../../common/constants'; +import { APP_UI_ID } from '../../../../../../common/constants'; import { useGlobalFullScreen } from '../../../../../common/containers/use_full_screen'; import { Display } from '../../../../../hosts/pages/display'; @@ -127,6 +125,7 @@ import { } from '../../../../components/alerts_table/alerts_filter_group'; import { useSignalHelpers } from '../../../../../common/containers/sourcerer/use_signal_helpers'; import { HeaderPage } from '../../../../../common/components/header_page'; +import { ExceptionsViewer } from '../../../../../detection_engine/rule_exceptions/components/all_exception_items_table'; import type { NavTab } from '../../../../../common/components/navigation/types'; /** @@ -148,6 +147,7 @@ const StyledMinHeightTabContainer = styled.div` export enum RuleDetailTabs { alerts = 'alerts', exceptions = 'rule_exceptions', + endpointExceptions = 'endpoint_exceptions', executionResults = 'execution_results', executionEvents = 'execution_events', } @@ -155,6 +155,7 @@ export enum RuleDetailTabs { export const RULE_DETAILS_TAB_NAME: Record = { [RuleDetailTabs.alerts]: detectionI18n.ALERT, [RuleDetailTabs.exceptions]: i18n.EXCEPTIONS_TAB, + [RuleDetailTabs.endpointExceptions]: i18n.ENDPOINT_EXCEPTIONS_TAB, [RuleDetailTabs.executionResults]: i18n.EXECUTION_RESULTS_TAB, [RuleDetailTabs.executionEvents]: i18n.EXECUTION_EVENTS_TAB, }; @@ -220,7 +221,6 @@ const RuleDetailsPageComponent: React.FC = ({ runtimeMappings, loading: isLoadingIndexPattern, } = useSourcererDataView(SourcererScopeName.detections); - const loading = userInfoLoading || listsConfigLoading; const { detailName: ruleId } = useParams<{ detailName: string; @@ -247,9 +247,15 @@ const RuleDetailsPageComponent: React.FC = ({ [RuleDetailTabs.exceptions]: { id: RuleDetailTabs.exceptions, name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.exceptions], - disabled: false, + disabled: rule == null, href: `/rules/id/${ruleId}/${RuleDetailTabs.exceptions}`, }, + [RuleDetailTabs.endpointExceptions]: { + id: RuleDetailTabs.endpointExceptions, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.endpointExceptions], + disabled: rule == null, + href: `/rules/id/${ruleId}/${RuleDetailTabs.endpointExceptions}`, + }, [RuleDetailTabs.executionResults]: { id: RuleDetailTabs.executionResults, name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.executionResults], @@ -263,10 +269,11 @@ const RuleDetailsPageComponent: React.FC = ({ href: `/rules/id/${ruleId}/${RuleDetailTabs.executionEvents}`, }, }), - [isExistingRule, ruleId] + [isExistingRule, rule, ruleId] ); const [pageTabs, setTabs] = useState>>(ruleDetailTabs); + const { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData } = rule != null ? getStepsData({ rule, detailsView: true }) @@ -389,11 +396,19 @@ const RuleDetailsPageComponent: React.FC = ({ if (!ruleExecutionSettings.extendedLogging.isEnabled) { hiddenTabs.push(RuleDetailTabs.executionEvents); } + if (rule != null) { + const hasEndpointList = (rule.exceptions_list ?? []).some( + (list) => list.type === ExceptionListTypeEnum.ENDPOINT + ); + if (!hasEndpointList) { + hiddenTabs.push(RuleDetailTabs.endpointExceptions); + } + } const tabs = omit>(hiddenTabs, ruleDetailTabs); setTabs(tabs); - }, [hasIndexRead, ruleDetailTabs, ruleExecutionSettings]); + }, [hasIndexRead, rule, ruleDetailTabs, ruleExecutionSettings]); const showUpdating = useMemo( () => isLoadingIndexPattern || isAlertsLoading || loading, @@ -611,34 +626,6 @@ const RuleDetailsPageComponent: React.FC = ({ [setShowOnlyThreatIndicatorAlerts] ); - const exceptionLists = useMemo((): { - lists: ExceptionListIdentifiers[]; - allowedExceptionListTypes: ExceptionListTypeEnum[]; - } => { - if (rule != null && rule.exceptions_list != null) { - return rule.exceptions_list.reduce<{ - lists: ExceptionListIdentifiers[]; - allowedExceptionListTypes: ExceptionListTypeEnum[]; - }>( - (acc, { id, list_id: listId, namespace_type: namespaceType, type }) => { - const { allowedExceptionListTypes, lists } = acc; - const shouldAddEndpoint = - type === ExceptionListTypeEnum.ENDPOINT && - !allowedExceptionListTypes.includes(ExceptionListTypeEnum.ENDPOINT); - return { - lists: [...lists, { id, listId, namespaceType, type }], - allowedExceptionListTypes: shouldAddEndpoint - ? [...allowedExceptionListTypes, ExceptionListTypeEnum.ENDPOINT] - : allowedExceptionListTypes, - }; - }, - { lists: [], allowedExceptionListTypes: [ExceptionListTypeEnum.DETECTION] } - ); - } else { - return { lists: [], allowedExceptionListTypes: [ExceptionListTypeEnum.DETECTION] }; - } - }, [rule]); - const onSkipFocusBeforeEventsTable = useCallback(() => { focusUtilityBarAction(containerElement.current); }, [containerElement]); @@ -858,14 +845,20 @@ const RuleDetailsPageComponent: React.FC = ({ + + + diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts index 02e8e01a281e9..1a50d570ea179 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts @@ -42,6 +42,13 @@ export const EXCEPTIONS_TAB = i18n.translate( } ); +export const ENDPOINT_EXCEPTIONS_TAB = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.endpointExceptionsTab', + { + defaultMessage: 'Endpoint exceptions', + } +); + export const EXECUTION_RESULTS_TAB = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionResultsTab', { diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx index 5d95d6e7f9446..ea1ecf8c9d652 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx @@ -27,12 +27,11 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; - 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_HOSTS_DOC_LINK } from '../../../../common/constants'; const tableColumns: Array> = [ { @@ -129,9 +128,9 @@ const HostRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => voi <> diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx index 38daf27402c54..9a2138786b3a8 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx @@ -16,7 +16,7 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { hostsActions, hostsModel, hostsSelectors } from '../../store'; import { getHostRiskScoreColumns } from './columns'; import type { - HostsRiskScore, + HostRiskScore, RiskScoreItem, RiskScoreSortField, RiskSeverity, @@ -50,7 +50,7 @@ const IconWrapper = styled.span` const tableType = hostsModel.HostsTableType.risk; interface HostRiskScoreTableProps { - data: HostsRiskScore[]; + data: HostRiskScore[]; id: string; isInspect: boolean; loading: boolean; @@ -63,8 +63,8 @@ interface HostRiskScoreTableProps { export type HostRiskScoreColumns = [ Columns, - Columns, - Columns + Columns, + Columns ]; const HostRiskScoreTableComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index 7c73cb4f24508..f7c9352f3a951 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -12,9 +12,9 @@ import { HostsKpiHosts } from './hosts'; import { HostsKpiUniqueIps } from './unique_ips'; import type { HostsKpiProps } from './types'; import { CallOutSwitcher } from '../../../common/components/callouts'; -import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; import * as i18n from './translations'; import { useHostRiskScore } from '../../../risk_score/containers'; +import { RISKY_HOSTS_DOC_LINK } from '../../../../common/constants'; export const HostsKpiComponent = React.memo( ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index b8e62a9bed97c..145b9ec56a344 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -28,7 +28,7 @@ import { SecuritySolutionPageWrapper } from '../../common/components/page_wrappe import { useGlobalFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; import { TimelineId } from '../../../common/types/timeline'; -import { LastEventIndexKey } from '../../../common/search_strategy'; +import { LastEventIndexKey, RiskScoreEntity } from '../../../common/search_strategy'; import { useKibana } from '../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../common/lib/keury'; import type { State } from '../../common/store'; @@ -103,7 +103,7 @@ const HostsComponent = () => { } if (tabName === HostsTableType.risk) { - const severityFilter = generateSeverityFilter(severitySelection); + const severityFilter = generateSeverityFilter(severitySelection, RiskScoreEntity.host); return [...severityFilter, ...hostNameExistsFilter, ...filters]; } 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 67c9bb761be94..33565cd9a34e1 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 @@ -9,6 +9,7 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; +import { last } from 'lodash/fp'; import type { HostsComponentsQueryProps } from './types'; import * as i18n from '../translations'; import { HostRiskInformationButtonEmpty } from '../../components/host_risk_information'; @@ -86,7 +87,7 @@ const HostRiskTabBodyComponent: React.FC< [setOverTimeToggleStatus] ); - const rules = data && data.length > 0 ? data[data.length - 1].risk_stats.rule_risks : []; + const lastHostRiskItem = last(data); return ( <> @@ -110,7 +111,7 @@ const HostRiskTabBodyComponent: React.FC< queryId={QUERY_ID} toggleStatus={contributorsToggleStatus} toggleQuery={toggleContributorsQuery} - rules={rules} + rules={lastHostRiskItem ? lastHostRiskItem.host.risk.rule_risks : []} /> diff --git a/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts b/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts index fd4830f93159f..daaa2e54ca300 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts @@ -40,7 +40,7 @@ export const mockHostsState: HostsModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: RiskScoreFields.riskScore, + field: RiskScoreFields.hostRiskScore, direction: Direction.desc, }, severitySelection: [], @@ -79,7 +79,7 @@ export const mockHostsState: HostsModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: RiskScoreFields.riskScore, + field: RiskScoreFields.hostRiskScore, direction: Direction.desc, }, severitySelection: [], @@ -124,7 +124,7 @@ describe('Hosts redux store', () => { severitySelection: [], sort: { direction: 'desc', - field: 'risk_stats.risk_score', + field: RiskScoreFields.hostRiskScore, }, }, [HostsTableType.sessions]: { @@ -164,7 +164,7 @@ describe('Hosts redux store', () => { severitySelection: [], sort: { direction: 'desc', - field: 'risk_stats.risk_score', + field: RiskScoreFields.hostRiskScore, }, }, [HostsTableType.sessions]: { diff --git a/x-pack/plugins/security_solution/public/hosts/store/helpers.ts b/x-pack/plugins/security_solution/public/hosts/store/helpers.ts index 6093a2c72a3a9..eaf1bb5d7c5aa 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/helpers.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RiskScoreEntity, RiskScoreFields } from '../../../common/search_strategy'; import type { RiskSeverity } from '../../../common/search_strategy'; import { DEFAULT_TABLE_ACTIVE_PAGE } from '../../common/store/constants'; @@ -60,7 +61,10 @@ export const setHostsQueriesActivePageToZero = (state: HostsModel, type: HostsTy throw new Error(`HostsType ${type} is unknown`); }; -export const generateSeverityFilter = (severitySelection: RiskSeverity[]) => +export const generateSeverityFilter = ( + severitySelection: RiskSeverity[], + entity: RiskScoreEntity +) => severitySelection.length > 0 ? [ { @@ -68,7 +72,9 @@ export const generateSeverityFilter = (severitySelection: RiskSeverity[]) => bool: { should: severitySelection.map((query) => ({ match_phrase: { - 'risk.keyword': { + [entity === RiskScoreEntity.user + ? RiskScoreFields.userRisk + : RiskScoreFields.hostRisk]: { query, }, }, diff --git a/x-pack/plugins/security_solution/public/hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/hosts/store/reducer.ts index 15f4d979a7267..f549b07b3850b 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/reducer.ts @@ -59,7 +59,7 @@ export const initialHostsState: HostsState = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: RiskScoreFields.riskScore, + field: RiskScoreFields.hostRiskScore, direction: Direction.desc, }, severitySelection: [], @@ -98,7 +98,7 @@ export const initialHostsState: HostsState = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: RiskScoreFields.riskScore, + field: RiskScoreFields.hostRiskScore, direction: Direction.desc, }, severitySelection: [], diff --git a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts index bfeccafd2e977..12dfa0f28208a 100644 --- a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts +++ b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts @@ -9,7 +9,7 @@ import type { ChromeBreadcrumb } from '@kbn/core/public'; import { AdministrationSubTab } from '../types'; import { ENDPOINTS_TAB, EVENT_FILTERS_TAB, POLICIES_TAB, TRUSTED_APPS_TAB } from './translations'; import type { AdministrationRouteSpyState } from '../../common/utils/route/types'; -import { HOST_ISOLATION_EXCEPTIONS, BLOCKLIST, RESPONSE_ACTIONS } from '../../app/translations'; +import { HOST_ISOLATION_EXCEPTIONS, BLOCKLIST, ACTION_HISTORY } from '../../app/translations'; const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, @@ -18,7 +18,7 @@ const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.eventFilters]: EVENT_FILTERS_TAB, [AdministrationSubTab.hostIsolationExceptions]: HOST_ISOLATION_EXCEPTIONS, [AdministrationSubTab.blocklist]: BLOCKLIST, - [AdministrationSubTab.responseActions]: RESPONSE_ACTIONS, + [AdministrationSubTab.actionHistory]: ACTION_HISTORY, }; export function getTrailingBreadcrumbs(params: AdministrationRouteSpyState): ChromeBreadcrumb[] { diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index a46a9d8a9397f..afad5b78e9f4e 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -23,7 +23,7 @@ export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/:tabName export const MANAGEMENT_ROUTING_EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.eventFilters})`; export const MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.hostIsolationExceptions})`; export const MANAGEMENT_ROUTING_BLOCKLIST_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.blocklist})`; -export const MANAGEMENT_ROUTING_RESPONSE_ACTIONS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.responseActions})`; +export const MANAGEMENT_ROUTING_ACTION_HISTORY_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.actionHistory})`; // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx index a75d2a76ae704..0d4c8caf892b2 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/utils/get_formatted_comments.tsx @@ -10,7 +10,7 @@ import type { EuiCommentProps } from '@elastic/eui'; import { EuiAvatar, EuiText } from '@elastic/eui'; import styled from 'styled-components'; import type { CommentsArray } from '@kbn/securitysolution-io-ts-list-types'; -import { COMMENT_EVENT } from '../../../../common/components/exceptions/translations'; +import { COMMENT_EVENT } from '../../../../detection_engine/rule_exceptions/utils/translations'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; const CustomEuiAvatar = styled(EuiAvatar)` diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx index df23ac288806b..86ec7431bd801 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx @@ -16,8 +16,7 @@ import { getDeferred } from '../mocks'; jest.mock('../../../common/components/user_privileges'); -// FLAKY: https://github.com/elastic/kibana/issues/129837 -describe.skip('When using the ArtifactListPage component', () => { +describe('When using the ArtifactListPage component', () => { let render: ( props?: Partial ) => ReturnType; @@ -156,7 +155,8 @@ describe.skip('When using the ArtifactListPage component', () => { expect(getByTestId('testPage-flyout')).toBeTruthy(); }); - it('should display the Delete modal when delete action is clicked', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/129837 + it.skip('should display the Delete modal when delete action is clicked', async () => { const { getByTestId } = await renderWithListData(); await clickCardAction('delete'); @@ -227,7 +227,8 @@ describe.skip('When using the ArtifactListPage component', () => { }); }); - it('should persist policy filter to the URL params', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/129837 + it.skip('should persist policy filter to the URL params', async () => { const policyId = mockedApi.responseProvider.endpointPackagePolicyList().items[0].id; const firstPolicyTestId = `policiesSelector-popover-items-${policyId}`; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts index 497baa999cf2e..d0fb3e3c59dfa 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts @@ -77,12 +77,14 @@ describe('When displaying the Delete artifact modal in the Artifact List Page', 10000 ); - it('should show Cancel and Delete buttons enabled', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139527 + it.skip('should show Cancel and Delete buttons enabled', async () => { expect(cancelButton).toBeEnabled(); expect(submitButton).toBeEnabled(); }); - it('should close modal if Cancel/Close buttons are clicked', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139528 + it.skip('should close modal if Cancel/Close buttons are clicked', async () => { userEvent.click(cancelButton); expect(renderResult.queryByTestId('testPage-deleteModal')).toBeNull(); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_input/command_input.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_input/command_input.test.tsx index 04a57ad6ec9f6..4cb233ee0c480 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_input/command_input.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_input/command_input.test.tsx @@ -59,6 +59,20 @@ describe('When entering data into the Console input', () => { expect(getUserInputText()).toEqual('cm'); }); + it('should repeat letters if the user holds letter key down on the keyboard', () => { + render(); + enterCommand('{a>5/}', { inputOnly: true, useKeyboard: true }); + expect(getUserInputText()).toEqual('aaaaa'); + }); + + it('should not display command key names in the input, when command keys are used', () => { + render(); + enterCommand('{Meta>}', { inputOnly: true, useKeyboard: true }); + expect(getUserInputText()).toEqual(''); + enterCommand('{Shift>}A{/Shift}', { inputOnly: true, useKeyboard: true }); + expect(getUserInputText()).toEqual('A'); + }); + it('should display placeholder text when input area is blank', () => { render(); @@ -201,6 +215,11 @@ describe('When entering data into the Console input', () => { expect(getRightOfCursorText()).toEqual(''); }); + it('should clear the input if the user holds down the delete/backspace key', () => { + typeKeyboardKey('{backspace>7/}'); + expect(getUserInputText()).toEqual(''); + }); + it('should move cursor to the left', () => { typeKeyboardKey('{ArrowLeft}'); typeKeyboardKey('{ArrowLeft}'); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_input/key_capture.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_input/key_capture.tsx index a88cffed733a6..b5c999427e1d4 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_input/key_capture.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_input/key_capture.tsx @@ -5,7 +5,12 @@ * 2.0. */ -import type { FormEventHandler, KeyboardEventHandler, MutableRefObject } from 'react'; +import type { + ClipboardEventHandler, + FormEventHandler, + KeyboardEventHandler, + MutableRefObject, +} from 'react'; import React, { memo, useCallback, useMemo, useRef, useState } from 'react'; import { pick } from 'lodash'; import styled from 'styled-components'; @@ -65,12 +70,11 @@ export const KeyCapture = memo(({ onCapture, focusRef, onStateC // We don't need the actual value that was last input in this component, because // `setLastInput()` is used with a function that returns the typed character. // This state is used like this: - // 1. user presses a keyboard key - // 2. `input` event is triggered - we store the letter typed - // 3. the next event to be triggered (after `input`) that we listen for is `keyup`, - // and when that is triggered, we take the input letter (already stored) and - // call `onCapture()` with it and then set the lastInput state back to an empty string - const [, setLastInput] = useState(''); + // 1. User presses a keyboard key down + // 2. We store the key that was pressed + // 3. When the 'keyup' event is triggered, we call `onCapture()` + // with all of the character that were entered + // 4. We set the last input back to an empty string const getTestId = useTestIdGenerator(useDataTestSubj()); const inputRef = useRef(null); const blurInputRef = useRef(null); @@ -96,15 +100,36 @@ export const KeyCapture = memo(({ onCapture, focusRef, onStateC [onStateChange] ); - const handleOnKeyUp = useCallback>( + const handleInputOnPaste = useCallback( (ev) => { - // There is a condition (still not clear how it is actually happening) where the `Enter` key - // event from the EuiSelectable component gets captured here by the Input. Its likely due to - // the sequence of events between keyup, focus and the Focus trap component having the - // `returnFocus` on by default. - // To avoid having that key Event from actually being processed, we check for this custom - // property on the event and skip processing it if we find it. This property is currently - // set by the CommandInputHistory (using EuiSelectable). + const value = ev.clipboardData.getData('text'); + ev.stopPropagation(); + + // hard-coded for use in onCapture and future keyboard functions + const metaKey = { + altKey: false, + ctrlKey: false, + key: 'Meta', + keyCode: 91, + metaKey: true, + repeat: false, + shiftKey: false, + }; + + onCapture({ + value, + eventDetails: metaKey, + }); + }, + [onCapture] + ); + + // 1. Determine if the key press is one that we need to store ex) letters, digits, values that we see + // 2. If the user clicks a key we don't need to store as text, but we need to do logic with ex) backspace, delete, l/r arrows, we must call onCapture + const handleOnKeyDown = useCallback( + (ev) => { + // checking to ensure that the key is not a control character + const newValue = /^[\w\d]{2}/.test(ev.key) ? '' : ev.key; // @ts-expect-error if (!isCapturing || ev._CONSOLE_IGNORE_KEY) { @@ -119,6 +144,11 @@ export const KeyCapture = memo(({ onCapture, focusRef, onStateC ev.stopPropagation(); + // allows for clipboard events to be captured via onPaste event handler + if (ev.metaKey || ev.ctrlKey) { + return; + } + const eventDetails = pick(ev, [ 'key', 'altKey', @@ -129,26 +159,14 @@ export const KeyCapture = memo(({ onCapture, focusRef, onStateC 'shiftKey', ]); - setLastInput((value) => { - onCapture({ - value, - eventDetails, - }); - - return ''; + onCapture({ + value: newValue, + eventDetails, }); }, [isCapturing, onCapture] ); - const handleOnInput = useCallback>((ev) => { - const newValue = ev.currentTarget.value; - - setLastInput((prevState) => { - return `${prevState || ''}${newValue}`; - }); - }, []); - const keyCaptureFocusMethods = useMemo(() => { return { focus: (force: boolean = false) => { @@ -183,10 +201,10 @@ export const KeyCapture = memo(({ onCapture, focusRef, onStateC spellCheck="false" value="" tabIndex={-1} - onInput={handleOnInput} - onKeyUp={handleOnKeyUp} + onKeyDown={handleOnKeyDown} onBlur={handleInputOnBlur} onFocus={handleInputOnFocus} + onPaste={handleInputOnPaste} onChange={NOOP} // this just silences Jest output warnings ref={inputRef} /> diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx index bb065a9392d43..29b6fd0446577 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx @@ -194,7 +194,8 @@ describe('When using processes action from response actions console', () => { }); }); - it('should display completion output if done (no additional API calls)', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139707 + it.skip('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx index 167c3feb554a7..827a4d6191754 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx @@ -283,7 +283,8 @@ describe('When using the kill-process action from response actions console', () }); }); - it('should display completion output if done (no additional API calls)', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139962 + it.skip('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx index e729185b220cc..19e3be94469eb 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx @@ -20,8 +20,7 @@ import { getDeferred } from '../mocks'; import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; -// FLAKY: https://github.com/elastic/kibana/issues/139641 -describe.skip('When using the release action from response actions console', () => { +describe('When using the release action from response actions console', () => { let render: ( capabilities?: ResponderCapabilities[] ) => Promise>; @@ -205,7 +204,8 @@ describe.skip('When using the release action from response actions console', () }); }); - it('should display completion output if done (no additional API calls)', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139641 + it.skip('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx index 4d12af721a02f..9446fb5dcba6a 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx @@ -274,7 +274,8 @@ describe('When using the suspend-process action from response actions console', }); }); - it('should display completion output if done (no additional API calls)', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/140119 + it.skip('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index 4bf28276d1651..323c46a6cbbda 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -10,7 +10,10 @@ import type { DurationRange, OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; -import type { ResponseActionStatus } from '../../../../../common/endpoint/service/response_actions/constants'; +import type { + ResponseActions, + ResponseActionStatus, +} from '../../../../../common/endpoint/service/response_actions/constants'; import { RESPONSE_ACTION_COMMANDS, RESPONSE_ACTION_STATUS, @@ -111,6 +114,11 @@ export const getActionStatus = (status: ResponseActionStatus): string => { return ''; }; +export const getCommand = ( + command: ResponseActions +): Exclude | 'release' | 'processes' => + command === 'unisolate' ? 'release' : command === 'running-processes' ? 'processes' : command; + // TODO: add more filter names here export type FilterName = keyof typeof FILTER_NAMES; export const useActionsLogFilter = ( @@ -139,7 +147,7 @@ export const useActionsLogFilter = ( })) : RESPONSE_ACTION_COMMANDS.map((filter) => ({ key: filter, - label: filter === 'unisolate' ? 'release' : filter, + label: getCommand(filter), checked: undefined, })) ); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 1b97987d4a131..1f5e39c532a6f 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -488,7 +488,7 @@ describe('Response Actions Log', () => { expect(filterList.querySelectorAll('ul>li').length).toEqual(5); expect( Array.from(filterList.querySelectorAll('ul>li')).map((option) => option.textContent) - ).toEqual(['isolate', 'release', 'kill-process', 'suspend-process', 'running-processes']); + ).toEqual(['isolate', 'release', 'kill-process', 'suspend-process', 'processes']); }); it('should have `clear all` button `disabled` when no selected values', () => { diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx index 8f873da6d9232..d12fce4efcb95 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -41,14 +41,11 @@ import { OUTPUT_MESSAGES, TABLE_COLUMN_NAMES, UX_MESSAGES } from './translations import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../common/constants'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; import { ActionsLogFilters } from './components/actions_log_filters'; -import { getActionStatus, useDateRangePicker } from './components/hooks'; +import { getActionStatus, getCommand, useDateRangePicker } from './components/hooks'; import { StatusBadge } from './components/status_badge'; const emptyValue = getEmptyValue(); -const getCommand = (command: ResponseActions): Exclude | 'release' => - command === 'unisolate' ? 'release' : command; - // Truncated usernames const StyledFacetButton = euiStyled(EuiFacetButton)` .euiText { @@ -300,11 +297,13 @@ export const ResponseActionsLog = memo< const command = getCommand(_command); return ( - + + {command} + ); }, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index fc6d3f6e7349e..f16feaeb94455 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -95,8 +95,8 @@ export const UX_MESSAGES = Object.freeze({ defaultMessage: `Actions log : {hostname}`, values: { hostname }, }), - pageTitle: i18n.translate('xpack.securitySolution.responseActionsList.list.title', { - defaultMessage: 'Response actions', + pageSubTitle: i18n.translate('xpack.securitySolution.responseActionsList.list.pageSubTitle', { + defaultMessage: 'View the history of response actions performed on hosts.', }), fetchError: i18n.translate('xpack.securitySolution.responseActionsList.list.errorMessage', { defaultMessage: 'Error while retrieving response actions', diff --git a/x-pack/plugins/security_solution/public/management/icons/action_history.tsx b/x-pack/plugins/security_solution/public/management/icons/action_history.tsx new file mode 100644 index 0000000000000..9a2763a2f338f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/icons/action_history.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 type { SVGProps } from 'react'; +import React from 'react'; +export const IconActionHistory: React.FC> = ({ ...props }) => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts index 4f41f95d3a556..659fb7a8216a5 100644 --- a/x-pack/plugins/security_solution/public/management/links.ts +++ b/x-pack/plugins/security_solution/public/management/links.ts @@ -16,6 +16,7 @@ import { HOST_ISOLATION_EXCEPTIONS_PATH, MANAGE_PATH, POLICIES_PATH, + ACTION_HISTORY_PATH, RULES_CREATE_PATH, RULES_PATH, SecurityPageName, @@ -31,6 +32,7 @@ import { HOST_ISOLATION_EXCEPTIONS, MANAGE, POLICIES, + ACTION_HISTORY, RULES, TRUSTED_APPLICATIONS, } from '../app/translations'; @@ -41,6 +43,7 @@ import { manageCategories as cloudSecurityPostureCategories, manageLinks as cloudSecurityPostureLinks, } from '../cloud_security_posture/links'; +import { IconActionHistory } from './icons/action_history'; import { IconBlocklist } from './icons/blocklist'; import { IconEndpoints } from './icons/endpoints'; import { IconEndpointPolicies } from './icons/endpoint_policies'; @@ -69,6 +72,7 @@ const categories = [ SecurityPageName.eventFilters, SecurityPageName.hostIsolationExceptions, SecurityPageName.blocklist, + SecurityPageName.actionHistory, ], }, ...cloudSecurityPostureCategories, @@ -202,6 +206,17 @@ export const links: LinkItem = { skipUrlState: true, hideTimeline: true, }, + { + id: SecurityPageName.actionHistory, + title: ACTION_HISTORY, + description: i18n.translate('xpack.securitySolution.appLinks.actionHistoryDescription', { + defaultMessage: 'View the history of response actions performed on hosts.', + }), + landingIcon: IconActionHistory, + path: ACTION_HISTORY_PATH, + skipUrlState: true, + hideTimeline: true, + }, cloudSecurityPostureLinks, ], }; 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 7d1fd0a3d77fe..c2cecdba29b3d 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 @@ -255,15 +255,15 @@ describe('endpoint list middleware', () => { query: { agent_ids: [ '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', + '34634c58-24b4-4448-80f4-107fb9918494', + '5a1298e3-e607-4bc0-8ef6-6d6a811312f2', + '78c54b13-596d-4891-95f4-80092d04454b', + '445f1fd2-5f81-4ddd-bdb6-f0d1bf2efe90', + 'd77a3fc6-3096-4852-a6ee-f6b09278fbc6', + '892fcccf-1bd8-45a2-a9cc-9a7860a3cb81', + '693a3110-5ba0-4284-a264-5d78301db08c', + '554db084-64fa-4e4a-ba47-2ba713f9932b', + 'c217deb6-674d-4f97-bb1d-a3a04238e6d7', ], }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx index a2b7a8ad2ce2f..eb651d8aedd12 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx @@ -16,8 +16,7 @@ import { fireEvent } from '@testing-library/dom'; import { uiQueryParams } from '../../store/selectors'; import type { EndpointIndexUIQueryParams } from '../../types'; -// FLAKY: https://github.com/elastic/kibana/issues/132398 -describe.skip('when rendering the endpoint list `AdminSearchBar`', () => { +describe('when rendering the endpoint list `AdminSearchBar`', () => { let render: ( urlParams?: EndpointIndexUIQueryParams ) => Promise>; @@ -85,7 +84,8 @@ describe.skip('when rendering the endpoint list `AdminSearchBar`', () => { expect(getQueryParamsFromStore().admin_query).toBe("(language:kuery,query:'host.name: foo')"); }); - it.each([ + // FLAKY: https://github.com/elastic/kibana/issues/132398 + it.skip.each([ ['nothing', ''], ['spaces', ' '], ])( diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx index f157a7e4d804e..f3431de72d7ce 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx @@ -29,13 +29,11 @@ import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { OnChangeProps } from '@kbn/lists-plugin/public'; import { useTestIdGenerator } from '../../../../hooks/use_test_id_generator'; import type { PolicyData } from '../../../../../../common/endpoint/types'; -import { AddExceptionComments } from '../../../../../common/components/exceptions/add_exception_comments'; import { useFetchIndex } from '../../../../../common/containers/source'; import { Loader } from '../../../../../common/components/loader'; import { useLicense } from '../../../../../common/hooks/use_license'; import { useKibana } from '../../../../../common/lib/kibana'; import type { ArtifactFormComponentProps } from '../../../../components/artifact_list_page'; -import { filterIndexPatterns } from '../../../../../common/components/exceptions/helpers'; import { isArtifactGlobal, getPolicyIdsFromArtifact, @@ -57,6 +55,8 @@ import { ENDPOINT_EVENT_FILTERS_LIST_ID, EVENT_FILTER_LIST_TYPE } from '../../co import type { EffectedPolicySelection } from '../../../../components/effected_policy_select'; import { EffectedPolicySelect } from '../../../../components/effected_policy_select'; import { isGlobalPolicyEffected } from '../../../../components/effected_policy_select/utils'; +import { ExceptionItemComments } from '../../../../../detection_engine/rule_exceptions/components/item_comments'; +import { filterIndexPatterns } from '../../../../../detection_engine/rule_exceptions/utils/helpers'; const OPERATING_SYSTEMS: readonly OperatingSystem[] = [ OperatingSystem.MAC, @@ -316,7 +316,7 @@ export const EventFiltersForm: React.FC ( - { await render(); }); - it('should render the form with empty inputs', () => { + // FLAKY: https://github.com/elastic/kibana/issues/140140 + it.skip('should render the form with empty inputs', () => { expect(renderResult.getByTestId('hostIsolationExceptions-form-name-input')).toHaveValue(''); expect(renderResult.getByTestId('hostIsolationExceptions-form-ip-input')).toHaveValue(''); expect( @@ -144,14 +145,16 @@ describe('When on the host isolation exceptions entry form', () => { ).toBe(true); }); - it('should show policy as selected when user clicks on it', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139776 + it.skip('should show policy as selected when user clicks on it', async () => { userEvent.click(renderResult.getByTestId('perPolicy')); await clickOnEffectedPolicy(renderResult); await expect(isEffectedPolicySelected(renderResult)).resolves.toBe(true); }); - it('should retain the previous policy selection when switching from per-policy to global', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/139899 + it.skip('should retain the previous policy selection when switching from per-policy to global', async () => { // move to per-policy and select the first userEvent.click(renderResult.getByTestId('perPolicy')); await clickOnEffectedPolicy(renderResult); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx index ba830b859d004..8fb3f683e6eb5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx @@ -22,8 +22,7 @@ import { getFirstCard } from '../../../components/artifact_list_page/mocks'; jest.mock('../../../../common/components/user_privileges'); const useUserPrivilegesMock = _useUserPrivileges as jest.Mock; -// FLAKY: https://github.com/elastic/kibana/issues/135587 -describe.skip('When on the host isolation exceptions page', () => { +describe('When on the host isolation exceptions page', () => { let render: () => ReturnType; let renderResult: ReturnType; let history: AppContextTestRender['history']; @@ -78,7 +77,8 @@ describe.skip('When on the host isolation exceptions page', () => { ); }); - it('should hide the Create and Edit actions when host isolation authz is not allowed', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/135587 + it.skip('should hide the Create and Edit actions when host isolation authz is not allowed', async () => { // Use case: license downgrade scenario, where user still has entries defined, but no longer // able to create or edit them (only Delete them) const existingPrivileges = useUserPrivilegesMock(); diff --git a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx index 7d2778d602c79..1df471633c3c2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx @@ -16,8 +16,7 @@ import { endpointPageHttpMock } from './endpoint_hosts/mocks'; jest.mock('../../common/components/user_privileges'); -// FLAKY: https://github.com/elastic/kibana/issues/135166 -describe.skip('when in the Administration tab', () => { +describe('when in the Administration tab', () => { let render: () => ReturnType; beforeEach(() => { @@ -35,7 +34,8 @@ describe.skip('when in the Administration tab', () => { expect(await render().findByTestId('noIngestPermissions')).not.toBeNull(); }); - it('should display the Management view if user has privileges', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/135166 + it.skip('should display the Management view if user has privileges', async () => { (useUserPrivileges as jest.Mock).mockReturnValue({ endpointPrivileges: { loading: false, canAccessEndpointManagement: true }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index b78ad462ae8a1..2a54557b0095b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -17,7 +17,7 @@ import { MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, MANAGEMENT_ROUTING_BLOCKLIST_PATH, - MANAGEMENT_ROUTING_RESPONSE_ACTIONS_PATH, + MANAGEMENT_ROUTING_ACTION_HISTORY_PATH, } from '../common/constants'; import { NotFoundPage } from '../../app/404'; import { EndpointsContainer } from './endpoint_hosts'; @@ -69,9 +69,9 @@ const HostIsolationExceptionsTelemetry = () => ( ); const ResponseActionsTelemetry = () => ( - + - + ); @@ -103,7 +103,7 @@ export const ManagementContainer = memo(() => { component={HostIsolationExceptionsTelemetry} /> - + 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 c5f6c62be00c1..5127f0605648c 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 @@ -118,11 +118,15 @@ describe('When on the policy list page', () => { expect(updatedByCells[0].textContent).toEqual(expectedAvatarName.charAt(0)); expect(firstUpdatedByName.textContent).toEqual(expectedAvatarName); }); - it('should show the correct endpoint count', async () => { + + // FLAKY: https://github.com/elastic/kibana/issues/139778 + it.skip('should show the correct endpoint count', async () => { const endpointCount = renderResult.getAllByTestId('policyEndpointCountLink'); expect(endpointCount[0].textContent).toBe('4'); }); - it('endpoint count link should navigate to the endpoint list filtered by policy', () => { + + // FLAKY: https://github.com/elastic/kibana/issues/140153 + it.skip('endpoint count link should navigate to the endpoint list filtered by policy', () => { const policyId = policies.items[0].id; const filterByPolicyQuery = `?admin_query=(language:kuery,query:'united.endpoint.Endpoint.policy.applied.id : "${policyId}"')`; const backLink = { @@ -185,7 +189,9 @@ describe('When on the policy list page', () => { perPage: 10, }); }); - it('should pass the correct pageSize value to the api', async () => { + + // FLAKY: https://github.com/elastic/kibana/issues/139196 + it.skip('should pass the correct pageSize value to the api', async () => { await waitFor(() => { expect(renderResult.getByTestId('tablePaginationPopoverButton')).toBeTruthy(); }); @@ -205,7 +211,9 @@ describe('When on the policy list page', () => { perPage: 20, }); }); - it('should call the api with the initial pagination values taken from the url', async () => { + + // FLAKY: https://github.com/elastic/kibana/issues/139207 + it.skip('should call the api with the initial pagination values taken from the url', async () => { act(() => { history.push('/administration/policies?page=3&pageSize=50'); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx index 7449a0c81afb4..b9a7e1cda040e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx @@ -336,7 +336,7 @@ export const PolicyTabs = React.memo(() => { // show loader for privileges validation if ( isInHostIsolationExceptionsTab && - (privileges.loading || allPolicyHostIsolationExceptionsListRequest.isLoading) + (privileges.loading || allPolicyHostIsolationExceptionsListRequest.isFetching) ) { return ; } diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx index f759830f555fe..0d3f029cc34ce 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx @@ -7,7 +7,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/kibana-react-plugin/public'; import React, { memo } from 'react'; -import { MANAGEMENT_ROUTING_RESPONSE_ACTIONS_PATH } from '../../common/constants'; +import { MANAGEMENT_ROUTING_ACTION_HISTORY_PATH } from '../../common/constants'; import { NotFoundPage } from '../../../app/404'; import { ResponseActionsListPage } from './view/response_actions_list_page'; @@ -15,7 +15,7 @@ export const ResponseActionsContainer = memo(() => { return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx index 044632a3c3984..23b3da831ddac 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx @@ -6,13 +6,18 @@ */ import React from 'react'; +import { ACTION_HISTORY } from '../../../../app/translations'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { ResponseActionsLog } from '../../../components/endpoint_response_actions_list/response_actions_log'; import { UX_MESSAGES } from '../../../components/endpoint_response_actions_list/translations'; export const ResponseActionsListPage = () => { return ( - + ); diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index 2658bd7a58b22..96c1983c8f254 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -31,7 +31,7 @@ export enum AdministrationSubTab { eventFilters = 'event_filters', hostIsolationExceptions = 'host_isolation_exceptions', blocklist = 'blocklist', - responseActions = 'response_actions', + actionHistory = 'action_history', } /** diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx index b382e6905a60f..feed7baf8819a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx @@ -84,7 +84,7 @@ describe('RiskScoreDonutChart', () => { expect(mockDispatch).toHaveBeenCalledWith( usersActions.updateTableSorting({ - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.userRiskScore, direction: Direction.desc }, tableType: UsersTableType.risk, }) ); @@ -110,7 +110,7 @@ describe('RiskScoreDonutChart', () => { expect(mockDispatch).toHaveBeenCalledWith( hostsActions.updateHostRiskScoreSort({ - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, hostsType: HostsType.page, }) ); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx index dd22104bf39ad..4ee2bab00f1db 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx @@ -52,7 +52,7 @@ export const EntityAnalyticsHeader = () => { dispatch( hostsActions.updateHostRiskScoreSort({ - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, hostsType: HostsType.page, }) ); @@ -74,7 +74,7 @@ export const EntityAnalyticsHeader = () => { dispatch( usersActions.updateTableSorting({ - sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.userRiskScore, direction: Direction.desc }, tableType: UsersTableType.risk, }) ); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx index affbd9e3357e6..998a356bf4f73 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx @@ -12,10 +12,11 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { HostDetailsLink } from '../../../../common/components/links'; import { HostsTableType } from '../../../../hosts/store/model'; import { RiskScore } from '../../../../common/components/severity/common'; -import type { HostsRiskScore, RiskSeverity } from '../../../../../common/search_strategy'; +import type { HostRiskScore, RiskSeverity } from '../../../../../common/search_strategy'; +import { RiskScoreFields } from '../../../../../common/search_strategy'; import * as i18n from './translations'; -type HostRiskScoreColumns = Array>; +type HostRiskScoreColumns = Array>; export const getHostRiskScoreColumns = (): HostRiskScoreColumns => [ { @@ -31,7 +32,7 @@ export const getHostRiskScoreColumns = (): HostRiskScoreColumns => [ }, }, { - field: 'risk_stats.risk_score', + field: RiskScoreFields.hostRiskScore, name: i18n.HOST_RISK_SCORE, truncateText: true, mobileOptions: { show: true }, @@ -47,7 +48,7 @@ export const getHostRiskScoreColumns = (): HostRiskScoreColumns => [ }, }, { - field: 'risk', + field: RiskScoreFields.hostRisk, name: ( <> diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx index 9e44561e8b4f5..fa3cda0921c83 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx @@ -27,6 +27,7 @@ import { HeaderSection } from '../../../../common/components/header_section'; import { useHostRiskScore, useHostRiskScoreKpi } from '../../../../risk_score/containers'; import type { RiskSeverity } from '../../../../../common/search_strategy'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { SecurityPageName } from '../../../../app/types'; import * as i18n from './translations'; import { generateSeverityFilter } from '../../../../hosts/store/helpers'; @@ -58,7 +59,7 @@ export const EntityAnalyticsHostRiskScores = () => { const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); const severityFilter = useMemo(() => { - const [filter] = generateSeverityFilter(selectedSeverity); + const [filter] = generateSeverityFilter(selectedSeverity, RiskScoreEntity.host); return filter ? JSON.stringify(filter.query) : undefined; }, [selectedSeverity]); @@ -127,7 +128,7 @@ export const EntityAnalyticsHostRiskScores = () => { return null; } - if (!isModuleEnabled) { + if (!isModuleEnabled && !isTableLoading) { return ; } diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx index c32c2282f367e..05f532617d5cc 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx @@ -12,10 +12,11 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { RiskScore } from '../../../../common/components/severity/common'; import * as i18n from './translations'; import { UsersTableType } from '../../../../users/store/model'; -import type { RiskSeverity, UsersRiskScore } from '../../../../../common/search_strategy'; +import type { RiskSeverity, UserRiskScore } from '../../../../../common/search_strategy'; +import { RiskScoreFields } from '../../../../../common/search_strategy'; import { UserDetailsLink } from '../../../../common/components/links'; -type UserRiskScoreColumns = Array>; +type UserRiskScoreColumns = Array>; export const getUserRiskScoreColumns = (): UserRiskScoreColumns => [ { @@ -31,7 +32,7 @@ export const getUserRiskScoreColumns = (): UserRiskScoreColumns => [ }, }, { - field: 'risk_stats.risk_score', + field: RiskScoreFields.userRiskScore, name: i18n.USER_RISK_SCORE, truncateText: true, mobileOptions: { show: true }, @@ -47,7 +48,7 @@ export const getUserRiskScoreColumns = (): UserRiskScoreColumns => [ }, }, { - field: 'risk', + field: RiskScoreFields.userRisk, name: ( <> diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx index 34c41ee2a0024..68ed1082f4c05 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx @@ -20,6 +20,7 @@ import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../../common/ import { LastUpdatedAt } from '../../detection_response/utils'; import { HeaderSection } from '../../../../common/components/header_section'; import type { RiskSeverity } from '../../../../../common/search_strategy'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { SecurityPageName } from '../../../../app/types'; import * as i18n from './translations'; import { generateSeverityFilter } from '../../../../hosts/store/helpers'; @@ -55,7 +56,7 @@ export const EntityAnalyticsUserRiskScores = () => { const riskyUsersFeatureEnabled = useIsExperimentalFeatureEnabled('riskyUsersEnabled'); const severityFilter = useMemo(() => { - const [filter] = generateSeverityFilter(selectedSeverity); + const [filter] = generateSeverityFilter(selectedSeverity, RiskScoreEntity.user); return filter ? JSON.stringify(filter.query) : undefined; }, [selectedSeverity]); @@ -123,7 +124,7 @@ export const EntityAnalyticsUserRiskScores = () => { return null; } - if (!isModuleEnabled) { + if (!isModuleEnabled && !isTableLoading) { return ; } diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx index 48dea1e5d4b90..721cd5c73f285 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx @@ -82,11 +82,11 @@ describe('Host Summary Component', () => { { host: { name: 'testHostmame', - }, - risk, - risk_stats: { - rule_risks: [], - risk_score: riskScore, + risk: { + rule_risks: [], + calculated_score_norm: riskScore, + calculated_level: risk, + }, }, }, ], diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index c6aad526117cd..d3a1f601445fd 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -10,7 +10,7 @@ import { euiLightVars as lightTheme, euiDarkVars as darkTheme } from '@kbn/ui-th import { getOr } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; -import type { HostItem, RiskSeverity } from '../../../../common/search_strategy'; +import type { HostItem } from '../../../../common/search_strategy'; import { buildHostNamesFilter } from '../../../../common/search_strategy'; import { DEFAULT_DARK_MODE } from '../../../../common/constants'; import type { DescriptionList } from '../../../../common/utility_types'; @@ -108,7 +108,9 @@ export const HostOverview = React.memo( title: i18n.HOST_RISK_SCORE, description: ( <> - {hostRiskData ? Math.round(hostRiskData.risk_stats.risk_score) : getEmptyTagValue()} + {hostRiskData + ? Math.round(hostRiskData.host.risk.calculated_score_norm) + : getEmptyTagValue()} ), }, @@ -118,7 +120,10 @@ export const HostOverview = React.memo( description: ( <> {hostRiskData ? ( - + ) : ( getEmptyTagValue() )} diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/inner_link_panel.tsx b/x-pack/plugins/security_solution/public/overview/components/link_panel/inner_link_panel.tsx index c4f234b43efd0..f76b446ac72e8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/link_panel/inner_link_panel.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/inner_link_panel.tsx @@ -8,7 +8,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiSplitPanel, EuiText } from '@elastic/eui'; -import { LEARN_MORE } from '../overview_risky_host_links/translations'; +import * as i18n from './translations'; const ButtonContainer = styled(EuiFlexGroup)` padding: ${({ theme }) => theme.eui.euiSizeS}; @@ -66,7 +66,7 @@ export const InnerLinkPanel = ({ data-test-subj={`${dataTestSubj}-learn-more`} external > - {LEARN_MORE} + {i18n.LEARN_MORE} )}

diff --git a/x-pack/plugins/security_solution/public/overview/components/link_panel/translations.ts b/x-pack/plugins/security_solution/public/overview/components/link_panel/translations.ts new file mode 100644 index 0000000000000..edbfa06477ba5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/link_panel/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const LEARN_MORE = i18n.translate( + 'xpack.securitySolution.overview.linkPanelLearnMoreButton', + { + defaultMessage: 'Learn More', + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx index 371f9a1e79f20..c6a623f19681f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx @@ -16,7 +16,6 @@ import type { LinkPanelViewProps } from '../link_panel/types'; import { shortenCountIntoString } from '../../../common/utils/shorten_count_into_string'; import { Link } from '../link_panel/link'; import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_ti_data_sources'; -import { LINK_COPY } from '../overview_risky_host_links/translations'; const columns: Array> = [ { name: 'Name', field: 'title', sortable: true, truncateText: true, width: '100%' }, @@ -34,7 +33,7 @@ const columns: Array> = [ field: 'path', truncateText: true, width: '80px', - render: (path: string) => , + render: (path: string) => , }, ]; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts index 775dab6721da1..ef7f1f6540ee5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts @@ -42,3 +42,7 @@ export const OTHER_DATA_SOURCE_TITLE = i18n.translate( defaultMessage: 'Others', } ); + +export const LINK_COPY = i18n.translate('xpack.securitySolution.overview.ctiLinkSource', { + defaultMessage: 'Source', +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx index 6e35d801c75d9..c985d5a7af655 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx @@ -11,18 +11,17 @@ import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo, useCallback, useState, useEffect } from 'react'; -import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants'; +import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import type { ESQuery } from '../../../../common/typed_json'; import { ID as OverviewHostQueryId, useHostOverview } from '../../containers/overview_host'; import { HeaderSection } from '../../../common/components/header_section'; -import { useUiSetting$, useKibana } from '../../../common/lib/kibana'; -import { getHostDetailsUrl, useFormatUrl } from '../../../common/components/link_to'; +import { useUiSetting$ } from '../../../common/lib/kibana'; import { getOverviewHostStats, OverviewHostStats } from '../overview_host_stats'; import { manageQuery } from '../../../common/components/page/manage_query'; import { InspectButtonContainer } from '../../../common/components/inspect'; +import { SecuritySolutionLinkButton } from '../../../common/components/links'; import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import { SecurityPageName } from '../../../app/types'; -import { LinkButton } from '../../../common/components/links'; import { useQueryToggle } from '../../../common/containers/query_toggle'; export interface OwnProps { @@ -43,8 +42,6 @@ const OverviewHostComponent: React.FC = ({ startDate, setQuery, }) => { - const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts); - const { navigateToApp } = useKibana().services.application; const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const { toggleStatus, setToggleStatus } = useQueryToggle(OverviewHostQueryId); @@ -69,17 +66,6 @@ const OverviewHostComponent: React.FC = ({ skip: querySkip, }); - const goToHost = useCallback( - (ev) => { - ev.preventDefault(); - navigateToApp(APP_UI_ID, { - deepLinkId: SecurityPageName.hosts, - path: getHostDetailsUrl('allHosts', urlSearch), - }); - }, - [navigateToApp, urlSearch] - ); - const hostEventsCount = useMemo( () => getOverviewHostStats(overviewHost).reduce((total, stat) => total + stat.count, 0), [overviewHost] @@ -90,18 +76,6 @@ const OverviewHostComponent: React.FC = ({ [defaultNumberFormat, hostEventsCount] ); - const hostPageButton = useMemo( - () => ( - - - - ), - [goToHost, formatUrl] - ); - const title = useMemo( () => ( = ({ title={title} isInspectDisabled={filterQuery === undefined} > - <>{hostPageButton} + + + {toggleStatus && ( { - const state: State = mockGlobalState; - - const { storage } = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - - it('renders enabled module view if module is enabled', () => { - useHostRiskScoreMock.mockReturnValueOnce([ - false, - { - data: [], - isModuleEnabled: true, - }, - ]); - - render( - - - - - - - - ); - - expect(screen.queryByTestId('risky-hosts-enable-module-button')).not.toBeInTheDocument(); - }); - - it('renders disabled module view if module is disabled', () => { - useHostRiskScoreMock.mockReturnValueOnce([ - false, - { - data: [], - isModuleEnabled: false, - }, - ]); - - render( - - - - - - - - ); - - expect(screen.getByTestId('disabled-open-in-console-button-with-tooltip')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx deleted file mode 100644 index df6286647e82e..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { RiskyHostsEnabledModule } from './risky_hosts_enabled_module'; -import { RiskyHostsDisabledModule } from './risky_hosts_disabled_module'; -import { useQueryInspector } from '../../../common/components/page/manage_query'; -import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'; -import { useHostRiskScore, HostRiskScoreQueryId } from '../../../risk_score/containers'; -export interface RiskyHostLinksProps extends Pick { - timerange: { to: string; from: string }; -} - -const QUERY_ID = HostRiskScoreQueryId.OVERVIEW_RISKY_HOSTS; - -const RiskyHostLinksComponent: React.FC = ({ - timerange, - deleteQuery, - setQuery, -}) => { - const [loading, { data, isModuleEnabled, inspect, refetch }] = useHostRiskScore({ - timerange, - }); - - useQueryInspector({ - queryId: QUERY_ID, - loading, - refetch, - setQuery, - deleteQuery, - inspect, - }); - - switch (isModuleEnabled) { - case true: - return ( - - ); - case false: - return ; - case undefined: - default: - return null; - } -}; - -export const RiskyHostLinks = React.memo(RiskyHostLinksComponent); -RiskyHostLinks.displayName = 'RiskyHostLinks'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx deleted file mode 100644 index afa0cfe7e9ae8..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback } from 'react'; -import { EuiButtonEmpty, EuiText, EuiToolTip } from '@elastic/eui'; -import { APP_UI_ID, SecurityPageName } from '../../../../common/constants'; -import { useKibana } from '../../../common/lib/kibana'; - -export const NavigateToHost: React.FC<{ name: string }> = ({ name }): JSX.Element => { - const { navigateToApp } = useKibana().services.application; - const { filterManager } = useKibana().services.data.query; - - const goToHostPage = useCallback( - (e) => { - e.preventDefault(); - filterManager.addFilters([ - { - meta: { - alias: null, - disabled: false, - negate: false, - }, - query: { match_phrase: { 'host.name': name } }, - }, - ]); - navigateToApp(APP_UI_ID, { - deepLinkId: SecurityPageName.hosts, - }); - }, - [filterManager, name, navigateToApp] - ); - return ( - - - {name} - - - ); -}; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.test.tsx deleted file mode 100644 index e8a50c83a3a27..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Provider } from 'react-redux'; -import { cloneDeep } from 'lodash/fp'; -import { render, screen } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; -import { ThemeProvider } from 'styled-components'; -import type { State } from '../../../common/store'; -import { createStore } from '../../../common/store'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; -import { RiskyHostsDisabledModule } from './risky_hosts_disabled_module'; -import { mockTheme } from '../overview_cti_links/mock'; - -jest.mock('../../../common/lib/kibana'); - -describe('RiskyHostsModule', () => { - const state: State = mockGlobalState; - - const { storage } = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - - it('renders expected children', () => { - render( - - - - - - - - ); - - expect(screen.getByTestId('risky-hosts-dashboard-links')).toBeInTheDocument(); - expect(screen.getByTestId('risky-hosts-inner-panel-danger-learn-more')).toBeInTheDocument(); - - expect(screen.getByTestId('disabled-open-in-console-button-with-tooltip')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.tsx deleted file mode 100644 index e13089dc6404e..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_disabled_module.tsx +++ /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 React from 'react'; - -import * as i18n from './translations'; -import { DisabledLinkPanel } from '../link_panel/disabled_link_panel'; -import { RiskyHostsPanelView } from './risky_hosts_panel_view'; - -import { ENABLE_VIA_DEV_TOOLS } from './translations'; - -import { OpenInDevConsoleButton } from '../../../common/components/open_in_dev_console'; -import { useCheckSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_check_signal_index'; -import type { LinkPanelListItem } from '../link_panel'; -import { useEnableHostRiskFromUrl } from '../../../common/hooks/use_enable_host_risk_from_url'; - -export const RISKY_HOSTS_DOC_LINK = - 'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/host-risk-score.md'; - -const emptyList: LinkPanelListItem[] = []; - -export const RiskyHostsDisabledModuleComponent = () => { - const loadFromUrl = useEnableHostRiskFromUrl(); - const { signalIndexExists } = useCheckSignalIndex(); - - return ( - - } - /> - ); -}; - -export const RiskyHostsDisabledModule = React.memo(RiskyHostsDisabledModuleComponent); -RiskyHostsDisabledModule.displayName = 'RiskyHostsDisabledModule'; 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 deleted file mode 100644 index d5e77c478aa1d..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 } from 'react-redux'; -import { cloneDeep } from 'lodash/fp'; -import { render, screen } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; -import { ThemeProvider } from 'styled-components'; -import type { State } from '../../../common/store'; -import { createStore } from '../../../common/store'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; - -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('../../../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'); -const useRiskyHostsDashboardLinksMock = useRiskyHostsDashboardLinks as jest.Mock; -useRiskyHostsDashboardLinksMock.mockReturnValue({ - listItemsWithLinks: [{ title: 'a', count: 1, path: '/test' }], -}); - -describe('RiskyHostsEnabledModule', () => { - const state: State = mockGlobalState; - - const { storage } = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - - it('renders expected children', () => { - render( - - - - - - - - ); - expect(screen.getByTestId('risky-hosts-dashboard-links')).toBeInTheDocument(); - expect(screen.getByTestId('create-saved-object-success-button')).toBeInTheDocument(); - }); -}); 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 deleted file mode 100644 index fae3c4db21737..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useMemo } from 'react'; -import { RiskyHostsPanelView } from './risky_hosts_panel_view'; -import type { LinkPanelListItem } from '../link_panel'; -import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; -import type { HostsRiskScore } from '../../../../common/search_strategy'; - -const getListItemsFromHits = (items: HostsRiskScore[]): LinkPanelListItem[] => { - return items.map(({ host, risk_stats: riskStats, risk: copy }) => ({ - title: host.name, - count: riskStats.risk_score, - copy, - path: '', - })); -}; - -const RiskyHostsEnabledModuleComponent: React.FC<{ - from: string; - hostRiskScore?: HostsRiskScore[]; - to: string; -}> = ({ hostRiskScore, to, from }) => { - const listItems = useMemo(() => getListItemsFromHits(hostRiskScore || []), [hostRiskScore]); - const { listItemsWithLinks } = useRiskyHostsDashboardLinks(to, from, listItems); - - return ( - - ); -}; - -export const RiskyHostsEnabledModule = React.memo(RiskyHostsEnabledModuleComponent); -RiskyHostsEnabledModule.displayName = 'RiskyHostsEnabledModule'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.test.tsx deleted file mode 100644 index 863bd4fcbd35d..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import type { State } from '../../../common/store'; -import { createStore } from '../../../common/store'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, - TestProviders, -} from '../../../common/mock'; - -import { RiskyHostsPanelView } from './risky_hosts_panel_view'; -import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; - -jest.mock('../../../common/lib/kibana'); - -jest.mock('../../../common/hooks/use_dashboard_button_href'); -const useRiskyHostsDashboardButtonHrefMock = useDashboardButtonHref as jest.Mock; -useRiskyHostsDashboardButtonHrefMock.mockReturnValue({ buttonHref: '/test' }); - -describe('RiskyHostsPanelView', () => { - const state: State = mockGlobalState; - - beforeEach(() => { - const { storage } = createSecuritySolutionStorageMock(); - const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - render( - - - - ); - }); - - it('renders title', () => { - expect(screen.getByTestId('header-section-title')).toHaveTextContent( - 'Current host risk scores' - ); - }); - - it('renders host number', () => { - expect(screen.getByTestId('header-panel-subtitle')).toHaveTextContent('Showing: 1 host'); - }); - - it('renders view dashboard button', () => { - expect(screen.getByTestId('create-saved-object-success-button')).toHaveAttribute( - 'href', - '/test' - ); - expect(screen.getByTestId('create-saved-object-success-button')).toHaveTextContent( - 'View dashboard' - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx deleted file mode 100644 index 7aadf6bcfa991..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useMemo, useState } from 'react'; - -import type { EuiTableFieldDataColumnType } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { SavedObject, SavedObjectAttributes } from '@kbn/core/types'; -import type { LinkPanelListItem } from '../link_panel'; -import { InnerLinkPanel, LinkPanel } from '../link_panel'; -import type { LinkPanelViewProps } from '../link_panel/types'; -import { Link } from '../link_panel/link'; -import * as i18n from './translations'; -import { NavigateToHost } from './navigate_to_host'; -import { HostRiskScoreQueryId } from '../../../risk_score/containers'; -import { useKibana } from '../../../common/lib/kibana'; -import { RISKY_HOSTS_DASHBOARD_TITLE } from '../../../hosts/pages/navigation/constants'; -import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; -import { ImportSavedObjectsButton } from '../../../common/components/create_prebuilt_saved_objects/components/bulk_create_button'; -import { VIEW_DASHBOARD } from '../overview_cti_links/translations'; - -const columns: Array> = [ - { - name: 'Host Name', - field: 'title', - sortable: true, - truncateText: true, - width: '55%', - render: (name) => () as JSX.Element, - }, - { - align: 'right', - field: 'count', - name: 'Risk Score', - render: (riskScore) => - Number.isNaN(riskScore) ? riskScore : Number.parseFloat(riskScore).toFixed(2), - sortable: true, - truncateText: true, - width: '15%', - }, - { - field: 'copy', - name: 'Current Risk', - sortable: true, - truncateText: true, - width: '15%', - }, - { - field: 'path', - name: '', - render: (path: string) => () as JSX.Element, - truncateText: true, - width: '80px', - }, -]; - -const warningPanel = ( - -); - -const RiskyHostsPanelViewComponent: React.FC = ({ - isInspectEnabled, - listItems, - splitPanel, - totalCount = 0, - to, - from, -}) => { - const splitPanelElement = - typeof splitPanel === 'undefined' - ? listItems.length === 0 - ? warningPanel - : undefined - : splitPanel; - - const [dashboardUrl, setDashboardUrl] = useState(); - const { buttonHref } = useDashboardButtonHref({ - to, - from, - title: RISKY_HOSTS_DASHBOARD_TITLE, - }); - const { - services: { dashboard }, - } = useKibana(); - - const onImportDashboardSuccessCallback = useCallback( - (response: Array>) => { - const targetDashboard = response.find( - (obj) => obj.type === 'dashboard' && obj?.attributes?.title === RISKY_HOSTS_DASHBOARD_TITLE - ); - - const fetchDashboardUrl = (targetDashboardId: string | null | undefined) => { - if (to && from && targetDashboardId) { - const targetUrl = dashboard?.locator?.getRedirectUrl({ - dashboardId: targetDashboardId, - timeRange: { - to, - from, - }, - }); - - setDashboardUrl(targetUrl); - } - }; - - fetchDashboardUrl(targetDashboard?.id); - }, - [dashboard?.locator, from, to] - ); - - return ( - - ), - columns, - dataTestSubj: 'risky-hosts-dashboard-links', - defaultSortField: 'count', - defaultSortOrder: 'desc', - inspectQueryId: isInspectEnabled ? HostRiskScoreQueryId.OVERVIEW_RISKY_HOSTS : undefined, - listItems, - panelTitle: i18n.PANEL_TITLE, - splitPanel: splitPanelElement, - subtitle: useMemo( - () => ( - - ), - [totalCount] - ), - }} - /> - ); -}; - -export const RiskyHostsPanelView = React.memo(RiskyHostsPanelViewComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/translations.ts b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/translations.ts deleted file mode 100644 index 5ba4bb2323b24..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/translations.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const WARNING_TITLE = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardWarningPanelTitle', - { - defaultMessage: 'No host risk score data available to display', - } -); - -export const WARNING_BODY = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardWarningPanelBody', - { - defaultMessage: `We haven't detected any host risk score data from the hosts in your environment for the selected time range.`, - } -); - -export const DANGER_TITLE = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardDangerPanelTitle', - { - defaultMessage: 'No host risk score data', - } -); - -export const DANGER_BODY = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardEnableThreatIntel', - { - defaultMessage: 'You must enable the host risk module to view risky hosts.', - } -); - -export const ENABLE_VIA_DEV_TOOLS = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardDangerPanelButton', - { - defaultMessage: 'Enable via Dev Tools', - } -); - -export const LEARN_MORE = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardLearnMoreButton', - { - defaultMessage: 'Learn More', - } -); - -export const LINK_COPY = i18n.translate('xpack.securitySolution.overview.riskyHostsSource', { - defaultMessage: 'Source', -}); - -export const PANEL_TITLE = i18n.translate( - 'xpack.securitySolution.overview.riskyHostsDashboardTitle', - { - defaultMessage: 'Current host risk scores', - } -); - -export const IMPORT_DASHBOARD = i18n.translate('xpack.securitySolution.overview.importDasboard', { - defaultMessage: 'Import dashboard', -}); - -export const ENABLE_RISK_SCORE_POPOVER = i18n.translate( - 'xpack.securitySolution.overview.enableRiskScorePopoverTitle', - { - defaultMessage: 'Alerts need to be available before enabling module', - } -); diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx index 5cf51615a395d..9bc5ce903e2ca 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx @@ -93,13 +93,13 @@ describe('User Summary Component', () => { { data: [ { - host: { + user: { name: 'testUsermame', - }, - risk, - risk_stats: { - rule_risks: [], - risk_score: riskScore, + risk: { + rule_risks: [], + calculated_level: risk, + calculated_score_norm: riskScore, + }, }, }, ], diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx index 6349a33a58fa3..6c5f4a952e9a7 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx @@ -106,7 +106,9 @@ export const UserOverview = React.memo( title: i18n.USER_RISK_SCORE, description: ( <> - {userRiskData ? Math.round(userRiskData.risk_stats.risk_score) : getEmptyTagValue()} + {userRiskData + ? Math.round(userRiskData.user.risk.calculated_score_norm) + : getEmptyTagValue()} ), }, @@ -115,7 +117,10 @@ export const UserOverview = React.memo( description: ( <> {userRiskData ? ( - + ) : ( getEmptyTagValue() )} diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_id.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_id.ts deleted file mode 100644 index 1e0758343ba47..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_id.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 { useState, useEffect } from 'react'; -import type { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; -import { useKibana } from '../../../common/lib/kibana'; - -const DASHBOARD_REQUEST_BODY_SEARCH = '"Drilldown of Host Risk Score"'; -export const DASHBOARD_REQUEST_BODY = { - type: 'dashboard', - search: DASHBOARD_REQUEST_BODY_SEARCH, - fields: ['title'], -}; - -export const useRiskyHostsDashboardId = () => { - const savedObjectsClient = useKibana().services.savedObjects.client; - const [dashboardId, setDashboardId] = useState(); - - useEffect(() => { - if (savedObjectsClient) { - savedObjectsClient.find(DASHBOARD_REQUEST_BODY).then( - async (DashboardsSO?: { - savedObjects?: Array<{ - attributes?: SavedObjectAttributes; - id?: string; - }>; - }) => { - if (DashboardsSO?.savedObjects?.length) { - setDashboardId(DashboardsSO.savedObjects[0].id); - } - } - ); - } - }, [savedObjectsClient]); - - return dashboardId; -}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx deleted file mode 100644 index bf09bb56bb6f4..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useState, useEffect } from 'react'; -import { useKibana } from '../../../common/lib/kibana'; -import type { LinkPanelListItem } from '../../components/link_panel'; -import { useRiskyHostsDashboardId } from './use_risky_hosts_dashboard_id'; - -export const useRiskyHostsDashboardLinks = ( - to: string, - from: string, - listItems: LinkPanelListItem[] -) => { - const { dashboard } = useKibana().services; - - const dashboardId = useRiskyHostsDashboardId(); - const [listItemsWithLinks, setListItemsWithLinks] = useState([]); - - useEffect(() => { - let cancelled = false; - const createLinks = async () => { - if (dashboard?.locator && dashboardId) { - const dashboardUrls = await Promise.all( - listItems.reduce( - (acc: Array>, listItem) => - dashboard && dashboard.locator - ? [ - ...acc, - dashboard.locator.getUrl({ - dashboardId, - timeRange: { - to, - from, - }, - filters: [ - { - meta: { - alias: null, - disabled: false, - negate: false, - }, - query: { match_phrase: { 'host.name': listItem.title } }, - }, - ], - }), - ] - : acc, - [] - ) - ); - if (!cancelled && dashboardUrls.length) { - setListItemsWithLinks( - listItems.map((item, i) => ({ - ...item, - path: dashboardUrls[i], - })) - ); - } - } else { - setListItemsWithLinks(listItems); - } - }; - createLinks(); - return () => { - cancelled = true; - }; - }, [dashboard, dashboardId, from, listItems, to]); - - return { listItemsWithLinks }; -}; diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 2e3aa7c4d8d28..6cccf353e4b1c 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -30,9 +30,7 @@ import { useDeepEqualSelector } from '../../common/hooks/use_selector'; import { ThreatIntelLinkPanel } from '../components/overview_cti_links'; import { useAllTiDataSources } from '../containers/overview_cti_links/use_all_ti_data_sources'; import { useUserPrivileges } from '../../common/components/user_privileges'; -import { RiskyHostLinks } from '../components/overview_risky_host_links'; import { useAlertsPrivileges } from '../../detections/containers/detection_engine/alerts/use_alerts_privileges'; -import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { LandingPageComponent } from '../../common/components/landing_page'; const OverviewComponent = () => { @@ -68,15 +66,6 @@ const OverviewComponent = () => { const { hasIndexRead, hasKibanaREAD } = useAlertsPrivileges(); const { tiDataSources: allTiDataSources, isInitiallyLoaded: isTiLoaded } = useAllTiDataSources(); - const riskyHostsEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); - - const timerange = useMemo( - () => ({ - from, - to, - }), - [from, to] - ); return ( <> {indicesExist ? ( @@ -146,15 +135,6 @@ const OverviewComponent = () => { /> )} - - {riskyHostsEnabled && ( - - )} - diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap index b033febcd1ac8..f6afb2bbe033c 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap @@ -15,11 +15,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -33,11 +33,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -58,11 +58,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -88,11 +88,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "lsass.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "lsass.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -103,11 +103,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "notepad.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "notepad.exe", + "name": "explorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -118,11 +118,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "lsass.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "lsass.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -148,11 +148,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "powershell.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "powershell.exe", + "name": "notepad.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -178,11 +178,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "notepad.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "notepad.exe", + "name": "explorer.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -439,11 +439,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -475,11 +475,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "lsass.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "lsass.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -493,11 +493,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "notepad.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "notepad.exe", + "name": "explorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -511,11 +511,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "lsass.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "lsass.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -547,11 +547,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "powershell.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "powershell.exe", + "name": "notepad.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -583,11 +583,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "notepad.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "notepad.exe", + "name": "explorer.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -608,11 +608,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -623,11 +623,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "mimikatz.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "mimikatz.exe", + "name": "notepad.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -661,11 +661,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -679,11 +679,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "mimikatz.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "mimikatz.exe", + "name": "notepad.exe", "parent": "A", "stats": Object { "byCategory": Object {}, 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 56e3ff14ce148..323a6d26acb34 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 @@ -5,7 +5,7 @@ * 2.0. */ -import type { HostsRiskScore } from '../../../common/search_strategy/security_solution/risk_score'; +import type { HostRiskScore } from '../../../common/search_strategy/security_solution/risk_score'; export * from './all'; export * from './kpi'; @@ -25,5 +25,5 @@ export const enum HostRiskScoreQueryId { export interface HostRisk { loading: boolean; isModuleEnabled?: boolean; - result?: HostsRiskScore[]; + result?: HostRiskScore[]; } diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx index 396d86e2d6acc..6d4ee5c73c5f6 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx +++ b/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx @@ -16,13 +16,13 @@ import { createFilter } from '../../../common/containers/helpers'; import type { KpiRiskScoreRequestOptions, KpiRiskScoreStrategyResponse, - RiskScoreAggByFields, } from '../../../../common/search_strategy'; import { getHostRiskIndex, getUserRiskIndex, RiskQueries, RiskSeverity, + RiskScoreEntity, } from '../../../../common/search_strategy'; import { useKibana } from '../../../common/lib/kibana'; @@ -32,7 +32,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_exper import type { SeverityCount } from '../../../common/components/severity/types'; import { useSpaceId } from '../../../common/hooks/use_space_id'; -type GetHostsRiskScoreProps = KpiRiskScoreRequestOptions & { +type GetHostRiskScoreProps = KpiRiskScoreRequestOptions & { data: DataPublicPluginStart; signal: AbortSignal; }; @@ -42,14 +42,14 @@ const getRiskScoreKpi = ({ defaultIndex, signal, filterQuery, - aggBy, -}: GetHostsRiskScoreProps): Observable => + entity, +}: GetHostRiskScoreProps): Observable => data.search.search( { defaultIndex, factoryQueryType: RiskQueries.kpiRiskScore, filterQuery: createFilter(filterQuery), - aggBy, + entity, }, { strategy: 'securitySolutionSearchStrategy', @@ -58,7 +58,7 @@ const getRiskScoreKpi = ({ ); const getRiskScoreKpiComplete = ( - props: GetHostsRiskScoreProps + props: GetHostRiskScoreProps ): Observable => { return getRiskScoreKpi(props).pipe( filter((response) => { @@ -80,11 +80,11 @@ interface RiskScoreKpi { type UseHostRiskScoreKpiProps = Omit< UseRiskScoreKpiProps, - 'defaultIndex' | 'aggBy' | 'featureEnabled' + 'defaultIndex' | 'aggBy' | 'featureEnabled' | 'entity' >; type UseUserRiskScoreKpiProps = Omit< UseRiskScoreKpiProps, - 'defaultIndex' | 'aggBy' | 'featureEnabled' + 'defaultIndex' | 'aggBy' | 'featureEnabled' | 'entity' >; export const useUserRiskScoreKpi = ({ @@ -99,7 +99,7 @@ export const useUserRiskScoreKpi = ({ filterQuery, skip, defaultIndex, - aggBy: 'user.name', + entity: RiskScoreEntity.user, featureEnabled: riskyUsersFeatureEnabled, }); }; @@ -116,7 +116,7 @@ export const useHostRiskScoreKpi = ({ filterQuery, skip, defaultIndex, - aggBy: 'host.name', + entity: RiskScoreEntity.host, featureEnabled: riskyHostsFeatureEnabled, }); }; @@ -125,7 +125,7 @@ interface UseRiskScoreKpiProps { filterQuery?: string | ESTermQuery; skip?: boolean; defaultIndex: string | undefined; - aggBy: RiskScoreAggByFields; + entity: RiskScoreEntity; featureEnabled: boolean; } @@ -133,7 +133,7 @@ const useRiskScoreKpi = ({ filterQuery, skip, defaultIndex, - aggBy, + entity, featureEnabled, }: UseRiskScoreKpiProps): RiskScoreKpi => { const { error, result, start, loading } = useRiskScoreKpiComplete(); @@ -146,10 +146,10 @@ const useRiskScoreKpi = ({ data, filterQuery, defaultIndex: [defaultIndex], - aggBy, + entity, }); } - }, [data, defaultIndex, start, filterQuery, skip, aggBy, featureEnabled]); + }, [data, defaultIndex, start, filterQuery, skip, entity, featureEnabled]); const severityCount = useMemo( () => ({ @@ -162,5 +162,6 @@ const useRiskScoreKpi = ({ }), [result] ); + return { error, severityCount, loading, isModuleDisabled }; }; diff --git a/x-pack/plugins/security_solution/public/rules/routes.tsx b/x-pack/plugins/security_solution/public/rules/routes.tsx index 0ca80c735344f..633b4005110af 100644 --- a/x-pack/plugins/security_solution/public/rules/routes.tsx +++ b/x-pack/plugins/security_solution/public/rules/routes.tsx @@ -30,7 +30,7 @@ const RulesSubRoutes = [ exact: true, }, { - path: `/rules/id/:detailName/:tabName(${RuleDetailTabs.alerts}|${RuleDetailTabs.exceptions}|${RuleDetailTabs.executionResults}|${RuleDetailTabs.executionEvents})`, + path: `/rules/id/:detailName/:tabName(${RuleDetailTabs.alerts}|${RuleDetailTabs.exceptions}|${RuleDetailTabs.endpointExceptions}|${RuleDetailTabs.executionResults}|${RuleDetailTabs.executionEvents})`, main: RuleDetailsPage, exact: true, }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx index 0d7a23800d404..d15fd7a501ea0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx @@ -24,6 +24,8 @@ import { defaultColumnHeaderType } from '../timeline/body/column_headers/default import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants'; import { useCreateFieldButton } from './create_field_button'; import { useFieldTableColumns } from './field_table_columns'; +import { useStartTransaction } from '../../../common/lib/apm/use_start_transaction'; +import { FIELD_BROWSER_ACTIONS } from '../../../common/lib/apm/user_actions'; export type FieldEditorActions = { closeEditor: () => void } | null; export type FieldEditorActionsRef = MutableRefObject; @@ -50,6 +52,7 @@ export const useFieldBrowserOptions: UseFieldBrowserOptions = ({ const dispatch = useDispatch(); const [dataView, setDataView] = useState(null); + const { startTransaction } = useStartTransaction(); const { indexFieldsSearch } = useDataView(); const { dataViewFieldEditor, @@ -75,6 +78,8 @@ export const useFieldBrowserOptions: UseFieldBrowserOptions = ({ ctx: { dataView }, fieldName, onSave: async (savedField: DataViewField) => { + startTransaction({ name: FIELD_BROWSER_ACTIONS.FIELD_SAVED }); + // Fetch the updated list of fields // Using cleanCache since the number of fields might have not changed, but we need to update the state anyway await indexFieldsSearch({ dataViewId: selectedDataViewId, cleanCache: true }); @@ -124,6 +129,7 @@ export const useFieldBrowserOptions: UseFieldBrowserOptions = ({ indexFieldsSearch, dispatch, timelineId, + startTransaction, ] ); @@ -134,6 +140,8 @@ export const useFieldBrowserOptions: UseFieldBrowserOptions = ({ ctx: { dataView }, fieldName, onDelete: async () => { + startTransaction({ name: FIELD_BROWSER_ACTIONS.FIELD_DELETED }); + // Fetch the updated list of fields await indexFieldsSearch({ dataViewId: selectedDataViewId }); @@ -147,7 +155,15 @@ export const useFieldBrowserOptions: UseFieldBrowserOptions = ({ }); } }, - [dataView, selectedDataViewId, dataViewFieldEditor, indexFieldsSearch, dispatch, timelineId] + [ + dataView, + selectedDataViewId, + dataViewFieldEditor, + indexFieldsSearch, + dispatch, + timelineId, + startTransaction, + ] ); const hasFieldEditPermission = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx index 1dd795bd795b5..628d2bf14d5e5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx @@ -48,6 +48,8 @@ import { deleteTimelinesByIds } from '../../containers/api'; import type { Direction } from '../../../../common/search_strategy'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; +import { useStartTransaction } from '../../../common/lib/apm/use_start_transaction'; +import { TIMELINE_ACTIONS } from '../../../common/lib/apm/user_actions'; interface OwnProps { /** Displays open timeline in modal */ @@ -86,6 +88,7 @@ export const StatefulOpenTimelineComponent = React.memo( title, }) => { const dispatch = useDispatch(); + const { startTransaction } = useStartTransaction(); /** Required by EuiTable for expandable rows: a map of `TimelineResult.savedObjectId` to rendered notes */ const [itemIdToExpandedNotesRowMap, setItemIdToExpandedNotesRowMap] = useState< Record @@ -197,6 +200,10 @@ export const StatefulOpenTimelineComponent = React.memo( const deleteTimelines: DeleteTimelines = useCallback( async (timelineIds: string[]) => { + startTransaction({ + name: timelineIds.length > 1 ? TIMELINE_ACTIONS.BULK_DELETE : TIMELINE_ACTIONS.DELETE, + }); + if (timelineIds.includes(timelineSavedObjectId)) { dispatch( dispatchCreateNewTimeline({ @@ -212,7 +219,7 @@ export const StatefulOpenTimelineComponent = React.memo( await deleteTimelinesByIds(timelineIds); refetch(); }, - [timelineSavedObjectId, refetch, dispatch, dataViewId, selectedPatterns] + [startTransaction, timelineSavedObjectId, refetch, dispatch, dataViewId, selectedPatterns] ); const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback( @@ -274,6 +281,10 @@ export const StatefulOpenTimelineComponent = React.memo( const openTimeline: OnOpenTimeline = useCallback( ({ duplicate, timelineId, timelineType: timelineTypeToOpen }) => { + if (duplicate) { + startTransaction({ name: TIMELINE_ACTIONS.DUPLICATE }); + } + if (isModal && closeModalTimeline != null) { closeModalTimeline(); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/helpers.tsx index b34338b4cbce9..065ac297ee468 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/helpers.tsx @@ -11,8 +11,9 @@ import type { TimelineEventsDetailsItem } from '../../../../../common/search_str import { getFieldValue } from '../../../../detections/components/host_isolation/helpers'; import { DEFAULT_ALERTS_INDEX, DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants'; -interface GetBasicDataFromDetailsData { +export interface GetBasicDataFromDetailsData { alertId: string; + agentId?: string; isAlert: boolean; hostName: string; ruleName: string; @@ -31,6 +32,11 @@ export const useBasicDataFromDetailsData = ( const alertId = useMemo(() => getFieldValue({ category: '_id', field: '_id' }, data), [data]); + const agentId = useMemo( + () => getFieldValue({ category: 'agent', field: 'agent.id' }, data), + [data] + ); + const hostName = useMemo( () => getFieldValue({ category: 'host', field: 'host.name' }, data), [data] @@ -44,17 +50,18 @@ export const useBasicDataFromDetailsData = ( return useMemo( () => ({ alertId, + agentId, isAlert, hostName, ruleName, timestamp, }), - [alertId, hostName, isAlert, ruleName, timestamp] + [agentId, alertId, hostName, isAlert, ruleName, timestamp] ); }; /* -The referenced alert _index in the flyout uses the `.internal.` such as +The referenced alert _index in the flyout uses the `.internal.` such as `.internal.alerts-security.alerts-spaceId` in the alert page flyout and .internal.preview.alerts-security.alerts-spaceId` in the rule creation preview flyout but we always want to use their respective aliase indices rather than accessing their backing .internal. indices. diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx index 1e53ba23c39af..ea7d6e0ef4687 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx @@ -39,6 +39,8 @@ import { TimelineId, TimelineTabs } from '../../../../../../common/types/timelin import { timelineActions, timelineSelectors } from '../../../../store/timeline'; import { timelineDefaults } from '../../../../store/timeline/defaults'; import { isInvestigateInResolverActionEnabled } from '../../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; +import { useStartTransaction } from '../../../../../common/lib/apm/use_start_transaction'; +import { ALERTS_ACTIONS } from '../../../../../common/lib/apm/user_actions'; export const isAlert = (eventType: TimelineEventsType | Omit): boolean => eventType === 'signal'; @@ -71,6 +73,7 @@ const ActionsComponent: React.FC = ({ const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled'); const emptyNotes: string[] = []; const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { startTransaction } = useStartTransaction(); const onPinEvent: OnPinEvent = useCallback( (evtId) => dispatch(timelineActions.pinEvent({ id: timelineId, eventId: evtId })), @@ -118,6 +121,8 @@ const ActionsComponent: React.FC = ({ const { setGlobalFullScreen } = useGlobalFullScreen(); const { setTimelineFullScreen } = useTimelineFullScreen(); const handleClick = useCallback(() => { + startTransaction({ name: ALERTS_ACTIONS.OPEN_ANALYZER }); + const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen'); dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: ecsData._id })); if (timelineId === TimelineId.active) { @@ -130,7 +135,14 @@ const ActionsComponent: React.FC = ({ setGlobalFullScreen(true); } } - }, [dispatch, ecsData._id, timelineId, setGlobalFullScreen, setTimelineFullScreen]); + }, [ + startTransaction, + dispatch, + timelineId, + ecsData._id, + setTimelineFullScreen, + setGlobalFullScreen, + ]); const sessionViewConfig = useMemo(() => { const { process, _id, timestamp } = ecsData; @@ -155,6 +167,8 @@ const ActionsComponent: React.FC = ({ const openSessionView = useCallback(() => { const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen'); + startTransaction({ name: ALERTS_ACTIONS.OPEN_SESSION_VIEW }); + if (timelineId === TimelineId.active) { if (dataGridIsFullScreen) { setTimelineFullScreen(true); @@ -170,7 +184,14 @@ const ActionsComponent: React.FC = ({ if (sessionViewConfig !== null) { dispatch(updateTimelineSessionViewConfig({ id: timelineId, sessionViewConfig })); } - }, [dispatch, timelineId, sessionViewConfig, setGlobalFullScreen, setTimelineFullScreen]); + }, [ + startTransaction, + timelineId, + sessionViewConfig, + setTimelineFullScreen, + dispatch, + setGlobalFullScreen, + ]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/title_and_description.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/title_and_description.tsx index bd267abb23c7d..d887b72cdb33a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/title_and_description.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/title_and_description.tsx @@ -30,6 +30,8 @@ import { useCreateTimeline } from '../properties/use_create_timeline'; import * as commonI18n from '../properties/translations'; import * as i18n from './translations'; import { formSchema } from './schema'; +import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; +import { TIMELINE_ACTIONS } from '../../../../common/lib/apm/user_actions'; const CommonUseField = getUseField({ component: Field }); interface TimelineTitleAndDescriptionProps { @@ -44,6 +46,7 @@ interface TimelineTitleAndDescriptionProps { // the unsaved timeline / template export const TimelineTitleAndDescription = React.memo( ({ closeSaveTimeline, initialFocus, timelineId, showWarning }) => { + const { startTransaction } = useStartTransaction(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const { isSaving, @@ -99,6 +102,11 @@ export const TimelineTitleAndDescription = React.memo { + startTransaction({ name: TIMELINE_ACTIONS.SAVE }); + submit(); + }, [submit, startTransaction]); + const handleCancel = useCallback(() => { if (showWarning) { handleCreateNewTimeline(); @@ -236,7 +244,7 @@ export const TimelineTitleAndDescription = React.memo {saveButtonTitle} 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 index 07fd273625d93..5da702eb87797 100644 --- 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 @@ -105,9 +105,9 @@ const UserRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => voi { const defaultProps = { @@ -19,8 +20,8 @@ describe('getUserRiskScoreColumns', () => { const columns = getUserRiskScoreColumns(defaultProps); expect(columns[0].field).toBe('user.name'); - expect(columns[1].field).toBe('risk_stats.risk_score'); - expect(columns[2].field).toBe('risk'); + expect(columns[1].field).toBe(RiskScoreFields.userRiskScore); + expect(columns[2].field).toBe(RiskScoreFields.userRisk); columns.forEach((column) => { expect(column).toHaveProperty('name'); 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 24ccfd3eb01f5..c50ae488383f0 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 @@ -21,6 +21,7 @@ import type { UserRiskScoreColumns } from '.'; import * as i18n from './translations'; import { RiskScore } from '../../../common/components/severity/common'; import type { RiskSeverity } from '../../../../common/search_strategy'; +import { RiskScoreFields } from '../../../../common/search_strategy'; import { UserDetailsLink } from '../../../common/components/links'; import { UsersTableType } from '../../store/model'; @@ -68,7 +69,7 @@ export const getUserRiskScoreColumns = ({ }, }, { - field: 'risk_stats.risk_score', + field: RiskScoreFields.userRiskScore, name: i18n.USER_RISK_SCORE, truncateText: true, mobileOptions: { show: true }, @@ -85,7 +86,7 @@ export const getUserRiskScoreColumns = ({ }, }, { - field: 'risk', + field: RiskScoreFields.userRisk, name: ( <> diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx index c0cd2e351298e..34f0116bb055e 100644 --- a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx @@ -9,6 +9,8 @@ import { render } from '@testing-library/react'; import { noop } from 'lodash'; import React from 'react'; import { UserRiskScoreTable } from '.'; +import type { UserRiskScore } from '../../../../common/search_strategy'; +import { RiskSeverity } from '../../../../common/search_strategy'; import { TestProviders } from '../../../common/mock'; import { UsersType } from '../../store/model'; @@ -18,16 +20,17 @@ describe('UserRiskScoreTable', () => { data: [ { '@timestamp': '1641902481', - risk: 'High', - risk_stats: { - rule_risks: [], - risk_score: 71, - }, user: { name: username, + risk: { + rule_risks: [], + calculated_score_norm: 71, + calculated_level: RiskSeverity.high, + multipliers: [], + }, }, }, - ], + ] as UserRiskScore[], id: 'test_id', isInspect: false, loading: false, diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx index 96a81ab4f5073..245150f4fb49d 100644 --- a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx @@ -18,10 +18,7 @@ import { getUserRiskScoreColumns } from './columns'; import * as i18nUsers from '../../pages/translations'; import * as i18n from './translations'; import { usersModel, usersSelectors, usersActions } from '../../store'; -import type { - UserRiskScoreFields, - UserRiskScoreItem, -} from '../../../../common/search_strategy/security_solution/users/common'; +import type { UserRiskScoreItem } from '../../../../common/search_strategy/security_solution/users/common'; import type { SeverityCount } from '../../../common/components/severity/types'; import { SeverityBadges } from '../../../common/components/severity/severity_badges'; import { SeverityBar } from '../../../common/components/severity/severity_bar'; @@ -29,9 +26,10 @@ import { SeverityFilterGroup } from '../../../common/components/severity/severit import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import type { State } from '../../../common/store'; import type { + RiskScoreFields, RiskScoreSortField, RiskSeverity, - UsersRiskScore, + UserRiskScore, } from '../../../../common/search_strategy'; const IconWrapper = styled.span` @@ -52,7 +50,7 @@ export const rowItems: ItemsPerRow[] = [ const tableType = usersModel.UsersTableType.risk; interface UserRiskScoreTableProps { - data: UsersRiskScore[]; + data: UserRiskScore[]; id: string; isInspect: boolean; loading: boolean; @@ -64,9 +62,9 @@ interface UserRiskScoreTableProps { } export type UserRiskScoreColumns = [ - Columns, - Columns, - Columns + Columns, + Columns, + Columns ]; const UserRiskScoreTableComponent: React.FC = ({ @@ -170,7 +168,7 @@ const UserRiskScoreTableComponent: React.FC = ({ ); const getUserRiskScoreFilterQuerySelector = useMemo( - () => usersSelectors.usersRiskScoreSeverityFilterSelector(), + () => usersSelectors.userRiskScoreSeverityFilterSelector(), [] ); const severitySelectionRedux = useDeepEqualSelector((state: State) => 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 index cef4740500a97..de45a4cdeeba4 100644 --- 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 @@ -16,6 +16,7 @@ import { RiskScoreOverTime } from '../../../common/components/risk_score_over_ti import { TopRiskScoreContributors } from '../../../common/components/top_risk_score_contributors'; import { useQueryToggle } from '../../../common/containers/query_toggle'; import { UserRiskScoreQueryId, useUserRiskScore } from '../../../risk_score/containers'; +import type { UserRiskScore } from '../../../../common/search_strategy'; import { buildUserNamesFilter } from '../../../../common/search_strategy'; import type { UsersComponentsQueryProps } from './types'; import { UserRiskInformationButtonEmpty } from '../../components/user_risk_information'; @@ -86,7 +87,9 @@ const UserRiskTabBodyComponent: React.FC< [setOverTimeToggleStatus] ); - const rules = data && data.length > 0 ? data[data.length - 1].risk_stats.rule_risks : []; + const lastUsertRiskItem: UserRiskScore | null = + data && data.length > 0 ? data[data.length - 1] : null; + const rules = lastUsertRiskItem ? lastUsertRiskItem.user.risk.rule_risks : []; return ( <> diff --git a/x-pack/plugins/security_solution/public/users/pages/users.tsx b/x-pack/plugins/security_solution/public/users/pages/users.tsx index 01b239ee89b48..94345fa24f377 100644 --- a/x-pack/plugins/security_solution/public/users/pages/users.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/users.tsx @@ -45,7 +45,7 @@ import { useDeepEqualSelector } from '../../common/hooks/use_selector'; import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query'; import { UsersKpiComponent } from '../components/kpi_users'; import type { UpdateDateRange } from '../../common/components/charts/common'; -import { LastEventIndexKey } from '../../../common/search_strategy'; +import { LastEventIndexKey, RiskScoreEntity } from '../../../common/search_strategy'; import { generateSeverityFilter } from '../../hosts/store/helpers'; import { UsersTableType } from '../store/model'; import { hasMlUserPermissions } from '../../../common/machine_learning/has_ml_user_permissions'; @@ -77,12 +77,12 @@ const UsersComponent = () => { const query = useDeepEqualSelector(getGlobalQuerySelector); const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector); - const getUsersRiskScoreFilterQuerySelector = useMemo( - () => usersSelectors.usersRiskScoreSeverityFilterSelector(), + const getUserRiskScoreFilterQuerySelector = useMemo( + () => usersSelectors.userRiskScoreSeverityFilterSelector(), [] ); const severitySelection = useDeepEqualSelector((state: State) => - getUsersRiskScoreFilterQuerySelector(state) + getUserRiskScoreFilterQuerySelector(state) ); const { to, from, deleteQuery, setQuery, isInitializing } = useGlobalTime(); @@ -96,7 +96,7 @@ const UsersComponent = () => { } if (tabName === UsersTableType.risk) { - const severityFilter = generateSeverityFilter(severitySelection); + const severityFilter = generateSeverityFilter(severitySelection, RiskScoreEntity.user); return [...severityFilter, ...filters]; } diff --git a/x-pack/plugins/security_solution/public/users/store/model.ts b/x-pack/plugins/security_solution/public/users/store/model.ts index de9606d163944..bee5eca0d7198 100644 --- a/x-pack/plugins/security_solution/public/users/store/model.ts +++ b/x-pack/plugins/security_solution/public/users/store/model.ts @@ -37,7 +37,7 @@ export interface AllUsersQuery extends BasicQueryPaginated { sort: SortUsersField; } -export interface UsersRiskScoreQuery extends BasicQueryPaginated { +export interface UserRiskScoreQuery extends BasicQueryPaginated { sort: RiskScoreSortField; severitySelection: RiskSeverity[]; } @@ -51,7 +51,7 @@ export interface UsersQueries { [UsersTableType.allUsers]: AllUsersQuery; [UsersTableType.authentications]: BasicQueryPaginated; [UsersTableType.anomalies]: UsersAnomaliesQuery; - [UsersTableType.risk]: UsersRiskScoreQuery; + [UsersTableType.risk]: UserRiskScoreQuery; [UsersTableType.events]: BasicQueryPaginated; } diff --git a/x-pack/plugins/security_solution/public/users/store/reducer.ts b/x-pack/plugins/security_solution/public/users/store/reducer.ts index 0699f3d3c3acc..79e9511bbd6f0 100644 --- a/x-pack/plugins/security_solution/public/users/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/users/store/reducer.ts @@ -44,7 +44,7 @@ export const initialUsersState: UsersModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: RiskScoreFields.riskScore, + field: RiskScoreFields.userRiskScore, direction: Direction.desc, }, severitySelection: [], diff --git a/x-pack/plugins/security_solution/public/users/store/selectors.ts b/x-pack/plugins/security_solution/public/users/store/selectors.ts index db054c88cf3ad..eb69c941fa236 100644 --- a/x-pack/plugins/security_solution/public/users/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/users/store/selectors.ts @@ -23,7 +23,7 @@ export const allUsersSelector = () => export const userRiskScoreSelector = () => createSelector(selectUserPage, (users) => users.queries[UsersTableType.risk]); -export const usersRiskScoreSeverityFilterSelector = () => +export const userRiskScoreSeverityFilterSelector = () => createSelector(selectUserPage, (users) => users.queries[UsersTableType.risk].severitySelection); export const authenticationsSelector = () => diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts index 72dbf3ade5e4a..213f839421a71 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts @@ -8,6 +8,7 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import { KbnClient } from '@kbn/test'; +import type { StatusResponse } from '@kbn/core-status-common-internal'; import { createSecuritySuperuser } from './security_user_services'; export interface RuntimeServices { @@ -116,3 +117,24 @@ export const createKbnClient = ({ return new KbnClient({ log, url: kbnUrl }); }; + +/** + * Retrieves the Stack (kibana/ES) version from the `/api/status` kibana api + * @param kbnClient + */ +export const fetchStackVersion = async (kbnClient: KbnClient): Promise => { + const status = ( + await kbnClient.request({ + method: 'GET', + path: '/api/status', + }) + ).data; + + if (!status?.version?.number) { + throw new Error( + `unable to get stack version from '/api/status' \n${JSON.stringify(status, null, 2)}` + ); + } + + return status.version.number; +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index 9eb03dd80e326..a871151ed0b0d 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -5,7 +5,7 @@ * 2.0. */ -/* eslint-disable no-console */ +/* eslint-disable no-console,max-classes-per-file */ import yargs from 'yargs'; import fs from 'fs'; import { Client, errors } from '@elastic/elasticsearch'; @@ -14,8 +14,10 @@ import { CA_CERT_PATH } from '@kbn/dev-utils'; import { ToolingLog } from '@kbn/tooling-log'; import type { KbnClientOptions } from '@kbn/test'; import { KbnClient } from '@kbn/test'; +import { EndpointMetadataGenerator } from '../../common/endpoint/data_generators/endpoint_metadata_generator'; import { indexHostsAndAlerts } from '../../common/endpoint/index_data'; import { ANCESTRY_LIMIT, EndpointDocGenerator } from '../../common/endpoint/generate_data'; +import { fetchStackVersion } from './common/stack_services'; main(); @@ -249,6 +251,13 @@ async function main() { type: 'string', default: '', }, + randomVersions: { + describe: + 'By default, the data generated (that contains a stack version - ex: `agent.version`) will have a ' + + 'version number set to be the same as the version of the running stack. Using this flag (`--randomVersions=true`) ' + + 'will result in random version being generated', + default: false, + }, }).argv; let ca: Buffer; @@ -323,11 +332,14 @@ async function main() { } let seed = argv.seed; + if (!seed) { seed = Math.random().toString(); console.log(`No seed supplied, using random seed: ${seed}`); } + const startTime = new Date().getTime(); + if (argv.fleet && !argv.withNewUser) { // warn and exit when using fleet flag console.log( @@ -336,6 +348,29 @@ async function main() { // eslint-disable-next-line no-process-exit process.exit(0); } + + let DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator; + + // If `--randomVersions` is NOT set, then use custom generator that ensures all data generated + // has a stack version number that matches that of the running stack + if (!argv.randomVersions) { + const stackVersion = await fetchStackVersion(kbnClient); + + // Document Generator override that uses a custom Endpoint Metadata generator and sets the + // `agent.version` to the current version + DocGenerator = class extends EndpointDocGenerator { + constructor(...args: ConstructorParameters) { + const MetadataGenerator = class extends EndpointMetadataGenerator { + protected randomVersion(): string { + return stackVersion; + } + }; + + super(args[0], MetadataGenerator); + } + }; + } + await indexHostsAndAlerts( client, kbnClient, @@ -360,10 +395,11 @@ async function main() { ancestryArraySize: argv.ancestryArraySize, eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(argv.eventIndex), alertsDataStream: EndpointDocGenerator.createDataStreamFromIndex(argv.alertIndex), - } + }, + DocGenerator ); - // delete endpoint_user after + // delete endpoint_user after if (user) { const deleted = await deleteUser(client, user.username); if (deleted.found) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 906b1e3168a70..56682d81bdab1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -280,7 +280,13 @@ describe('test endpoint routes', () => { bool: { must_not: { bool: { - should: [{ exists: { field: 'united.agent.upgraded_at' } }], + should: [ + { + match: { + 'united.agent.upgrade_status': 'completed', + }, + }, + ], minimum_should_match: 1, }, }, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts index 30189459ff167..c4fc8c8619e7e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts @@ -101,8 +101,9 @@ export const expectedCompleteUnitedIndexQuery = { bool: { should: [ { - exists: { - field: 'united.agent.upgraded_at', + match: { + 'united.agent.upgrade_status': + 'completed', }, }, ], @@ -192,7 +193,11 @@ export const expectedCompleteUnitedIndexQuery = { must_not: { bool: { should: [ - { exists: { field: 'united.agent.upgraded_at' } }, + { + match: { + 'united.agent.upgrade_status': 'completed', + }, + }, ], minimum_should_match: 1, }, @@ -296,7 +301,11 @@ export const expectedCompleteUnitedIndexQuery = { must_not: { bool: { should: [ - { exists: { field: 'united.agent.upgraded_at' } }, + { + match: { + 'united.agent.upgrade_status': 'completed', + }, + }, ], minimum_should_match: 1, }, @@ -367,7 +376,13 @@ export const expectedCompleteUnitedIndexQuery = { bool: { must_not: { bool: { - should: [{ exists: { field: 'united.agent.upgraded_at' } }], + should: [ + { + match: { + 'united.agent.upgrade_status': 'completed', + }, + }, + ], minimum_should_match: 1, }, }, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts index 7f89444b21fa5..d24ef99323eae 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts @@ -93,7 +93,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['healthy']; const kuery = buildStatusesKuery(status); const expected = - '(not (united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) )) AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; + '(not (united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) )) AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; expect(kuery).toEqual(expected); }); @@ -101,7 +101,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['offline']; const kuery = buildStatusesKuery(status); const expected = - '(united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) ))'; + '(united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) ))'; expect(kuery).toEqual(expected); }); @@ -109,7 +109,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['unhealthy']; const kuery = buildStatusesKuery(status); const expected = - '((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; + '((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; expect(kuery).toEqual(expected); }); @@ -117,7 +117,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['updating']; const kuery = buildStatusesKuery(status); const expected = - '(((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))'; + '(((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))'; expect(kuery).toEqual(expected); }); @@ -132,7 +132,7 @@ describe('test filtering endpoint hosts by agent status', () => { const statuses = ['offline', 'unhealthy']; const kuery = buildStatusesKuery(statuses); const expected = - '(united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) ) OR (united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; + '(united.agent.last_checkin < now-300s AND not ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*))) AND not ( ((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*) ) OR (united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not (((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)))'; expect(kuery).toEqual(expected); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.test.ts new file mode 100644 index 0000000000000..1ca0fd4dabf60 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.test.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 { DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL } from '../../../../../common/constants'; +import { + getEmptyFindResult, + getFindResultWithSingleHit, + getRuleMock, +} from '../__mocks__/request_responses'; +import { requestContextMock, serverMock, requestMock } from '../__mocks__'; +import { findRuleExceptionReferencesRoute } from './find_rule_exceptions_route'; +import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; + +describe('findRuleExceptionReferencesRoute', () => { + let server: ReturnType; + let { clients, context } = requestContextMock.createTools(); + + beforeEach(() => { + server = serverMock.create(); + ({ clients, context } = requestContextMock.createTools()); + + clients.rulesClient.find.mockResolvedValue({ + ...getFindResultWithSingleHit(), + data: [ + { + ...getRuleMock({ + ...getQueryRuleParams(), + exceptionsList: [ + { + type: 'detection', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + list_id: 'my_default_list', + namespace_type: 'single', + }, + ], + }), + }, + ], + }); + + findRuleExceptionReferencesRoute(server.router); + }); + + describe('happy paths', () => { + test('returns 200 when adding an exception item and rule_default exception list already exists', async () => { + const request = requestMock.create({ + method: 'get', + path: `${DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL}?exception_list`, + query: { + ids: `4656dc92-5832-11ea-8e2d-0242ac130003`, + list_ids: `my_default_list`, + namespace_types: `single`, + }, + }); + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + references: [ + { + my_default_list: [ + { + exception_lists: [ + { + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + list_id: 'my_default_list', + namespace_type: 'single', + type: 'detection', + }, + ], + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + name: 'Detect Root/Admin Users', + rule_id: 'rule-1', + }, + ], + }, + ], + }); + }); + + test('returns 200 when no references found', async () => { + const request = requestMock.create({ + method: 'get', + path: DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + query: { + ids: `4656dc92-5832-11ea-8e2d-0242ac130003`, + list_ids: `my_default_list`, + namespace_types: `single`, + }, + }); + + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + references: [ + { + my_default_list: [], + }, + ], + }); + }); + }); + + describe('error codes', () => { + test('returns 400 if query param lengths do not match', async () => { + const request = requestMock.create({ + method: 'get', + path: DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + query: { + ids: `4656dc92-5832-11ea-8e2d-0242ac130003`, + list_ids: `my_default_list`, + namespace_types: `single,agnostic`, + }, + }); + + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(400); + expect(response.body).toEqual({ + message: + '"ids", "list_ids" and "namespace_types" need to have the same comma separated number of values. Expected "ids" length: 1 to equal "namespace_types" length: 2 and "list_ids" length: 1.', + status_code: 400, + }); + }); + + test('returns 500 if rules client fails', async () => { + const request = requestMock.create({ + method: 'get', + path: DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + query: { + ids: `4656dc92-5832-11ea-8e2d-0242ac130003`, + list_ids: `my_default_list`, + namespace_types: `single`, + }, + }); + + clients.rulesClient.find.mockRejectedValue(new Error('find request failed')); + + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(500); + expect(response.body).toEqual({ message: 'find request failed', status_code: 500 }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.ts new file mode 100644 index 0000000000000..8ac258322e260 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route.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 { transformError } from '@kbn/securitysolution-es-utils'; +import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; +import { validate } from '@kbn/securitysolution-io-ts-utils'; +import type { FindResult } from '@kbn/alerting-plugin/server'; + +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL } from '../../../../../common/constants'; +import { buildSiemResponse } from '../utils'; +import { enrichFilterWithRuleTypeMapping } from '../../rules/enrich_filter_with_rule_type_mappings'; +import type { FindExceptionReferencesOnRuleSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/find_exception_list_references_schema'; +import { findExceptionReferencesOnRuleSchema } from '../../../../../common/detection_engine/schemas/request/find_exception_list_references_schema'; +import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import type { RuleReferencesSchema } from '../../../../../common/detection_engine/schemas/response/find_exception_list_references_schema'; +import { rulesReferencedByExceptionListsSchema } from '../../../../../common/detection_engine/schemas/response/find_exception_list_references_schema'; +import type { RuleParams } from '../../schemas/rule_schemas'; + +export const findRuleExceptionReferencesRoute = (router: SecuritySolutionPluginRouter) => { + router.get( + { + path: DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + validate: { + query: buildRouteValidation< + typeof findExceptionReferencesOnRuleSchema, + FindExceptionReferencesOnRuleSchemaDecoded + >(findExceptionReferencesOnRuleSchema), + }, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const { ids, namespace_types: namespaceTypes, list_ids: listIds } = request.query; + + const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); + const rulesClient = ctx.alerting.getRulesClient(); + + if (ids.length !== namespaceTypes.length || ids.length !== listIds.length) { + return siemResponse.error({ + body: `"ids", "list_ids" and "namespace_types" need to have the same comma separated number of values. Expected "ids" length: ${ids.length} to equal "namespace_types" length: ${namespaceTypes.length} and "list_ids" length: ${listIds.length}.`, + statusCode: 400, + }); + } + + const foundRules: Array> = await Promise.all( + ids.map(async (id, index) => { + return rulesClient.find({ + options: { + perPage: 10000, + filter: enrichFilterWithRuleTypeMapping(null), + hasReference: { + id, + type: getSavedObjectType({ namespaceType: namespaceTypes[index] }), + }, + }, + }); + }) + ); + + const references = foundRules.map(({ data }, index) => { + const wantedData = data.map(({ name, id, params }) => ({ + name, + id, + rule_id: params.ruleId, + exception_lists: params.exceptionsList, + })); + return { [listIds[index]]: wantedData }; + }); + + const [validated, errors] = validate({ references }, rulesReferencedByExceptionListsSchema); + + if (errors != null) { + return siemResponse.error({ statusCode: 500, body: errors }); + } else { + return response.ok({ body: validated ?? { references: [] } }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 5e8e700fcc1e0..af14b3c01226d 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -74,6 +74,7 @@ import { createPrebuiltSavedObjectsRoute } from '../lib/prebuilt_saved_objects/r import { readAlertsIndexExistsRoute } from '../lib/detection_engine/routes/index/read_alerts_index_exists_route'; import { getInstalledIntegrationsRoute } from '../lib/detection_engine/routes/fleet/get_installed_integrations/get_installed_integrations_route'; import { registerResolverRoutes } from '../endpoint/routes/resolver'; +import { findRuleExceptionReferencesRoute } from '../lib/detection_engine/routes/rules/find_rule_exceptions_route'; import { createRuleExceptionsRoute } from '../lib/detection_engine/routes/rules/create_rule_exceptions_route'; export const initRoutes = ( @@ -132,6 +133,7 @@ export const initRoutes = ( patchTimelinesRoute(router, config, security); importRulesRoute(router, config, ml); exportRulesRoute(router, config, logger); + findRuleExceptionReferencesRoute(router); importTimelinesRoute(router, config, security); exportTimelinesRoute(router, config, security); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts index b0b2292d6b5f1..d21bc53de178f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts @@ -91,6 +91,12 @@ describe('allHosts search strategy', () => { risk, host: { name: hostName, + risk: { + multipliers: [], + calculated_score_norm: 9999, + calculated_level: risk, + rule_risks: [], + }, }, }, }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts index 57f30ed8703b0..cecfc60fbbaed 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts @@ -18,7 +18,7 @@ import type { HostsEdges, } from '../../../../../../common/search_strategy/security_solution/hosts'; -import type { HostsRiskScore } from '../../../../../../common/search_strategy'; +import type { HostRiskScore } from '../../../../../../common/search_strategy'; import { getHostRiskIndex, buildHostNamesFilter } from '../../../../../../common/search_strategy'; import { inspectStringifyObject } from '../../../../../utils/build_query'; @@ -92,7 +92,7 @@ async function enhanceEdges( const hostsRiskByHostName: Record | undefined = hostRiskData?.hits.hits.reduce( (acc, hit) => ({ ...acc, - [hit._source?.host.name ?? '']: hit._source?.risk, + [hit._source?.host.name ?? '']: hit._source?.host.risk.calculated_level, }), {} ); @@ -114,7 +114,7 @@ async function getHostRiskData( hostNames: string[] ) { try { - const hostRiskResponse = await esClient.asCurrentUser.search( + const hostRiskResponse = await esClient.asCurrentUser.search( buildRiskScoreQuery({ defaultIndex: [getHostRiskIndex(spaceId)], filterQuery: buildHostNamesFilter(hostNames), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts index 069a3e01cdbc1..d4ec14bb29acf 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts @@ -64,8 +64,12 @@ const getQueryOrder = (sort?: RiskScoreSortField): Sort => { ]; } - if (sort.field === RiskScoreFields.risk) { - return [{ [RiskScoreFields.riskScore]: sort.direction }]; + if (sort.field === RiskScoreFields.hostRisk) { + return [{ [RiskScoreFields.hostRiskScore]: sort.direction }]; + } + + if (sort.field === RiskScoreFields.userRisk) { + return [{ [RiskScoreFields.userRiskScore]: sort.direction }]; } return [{ [sort.field]: sort.direction }]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts index 94830d71e6337..e494849cc6ceb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts @@ -6,7 +6,7 @@ */ import type { KpiRiskScoreRequestOptions } from '../../../../../../../common/search_strategy'; -import { RiskQueries } from '../../../../../../../common/search_strategy'; +import { RiskScoreEntity, RiskQueries } from '../../../../../../../common/search_strategy'; export const mockOptions: KpiRiskScoreRequestOptions = { defaultIndex: [ @@ -22,5 +22,5 @@ export const mockOptions: KpiRiskScoreRequestOptions = { factoryQueryType: RiskQueries.kpiRiskScore, filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', - aggBy: 'host.name', + entity: RiskScoreEntity.host, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts index ace0cece7c981..f68eb647ad88c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts @@ -5,13 +5,14 @@ * 2.0. */ +import { RiskScoreEntity, RiskScoreFields } from '../../../../../../common/search_strategy'; import type { KpiRiskScoreRequestOptions } from '../../../../../../common/search_strategy'; import { createQueryFilterClauses } from '../../../../../utils/build_query'; export const buildKpiRiskScoreQuery = ({ defaultIndex, filterQuery, - aggBy, + entity, }: KpiRiskScoreRequestOptions) => { const filter = [...createQueryFilterClauses(filterQuery)]; @@ -24,12 +25,16 @@ export const buildKpiRiskScoreQuery = ({ aggs: { risk: { terms: { - field: 'risk.keyword', + field: + entity === RiskScoreEntity.user ? RiskScoreFields.userRisk : RiskScoreFields.hostRisk, }, aggs: { unique_entries: { cardinality: { - field: aggBy, + field: + entity === RiskScoreEntity.user + ? RiskScoreFields.userName + : RiskScoreFields.hostName, }, }, }, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx index eea9acaabda31..9cd711d313a54 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx @@ -82,18 +82,6 @@ export const IndicatorsTable: VFC = ({ const start = pagination.pageIndex * pagination.pageSize; const end = start + pagination.pageSize; - const flyoutFragment = useMemo( - () => - expanded ? ( - setExpanded(undefined)} - /> - ) : null, - [expanded, fieldTypesMap] - ); - const leadingControlColumns = useMemo( () => [ { @@ -140,42 +128,71 @@ export const IndicatorsTable: VFC = ({ onToggleColumn: handleToggleColumn, }); - if (loading) { + const flyoutFragment = useMemo( + () => + expanded ? ( + setExpanded(undefined)} + /> + ) : null, + [expanded, fieldTypesMap] + ); + + const gridFragment = useMemo(() => { + if (loading) { + return ( + + + + + + + + ); + } + + if (!indicatorCount) { + return ; + } + return ( - - - - - - - + ); - } - - if (!indicatorCount) { - return ; - } + }, [ + columnVisibility, + columns, + indicatorCount, + leadingControlColumns, + loading, + onChangeItemsPerPage, + onChangePage, + pagination, + renderCellValue, + toolbarOptions, + ]); return (
- {flyoutFragment} + {gridFragment}
); diff --git a/x-pack/plugins/timelines/public/container/index.tsx b/x-pack/plugins/timelines/public/container/index.tsx index ef82aa527bab3..15e72e7aed2bb 100644 --- a/x-pack/plugins/timelines/public/container/index.tsx +++ b/x-pack/plugins/timelines/public/container/index.tsx @@ -16,7 +16,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { clearEventsLoading, clearEventsDeleted, @@ -43,7 +42,7 @@ import type { KueryFilterQueryKind } from '../../common/types/timeline'; import { useAppToasts } from '../hooks/use_app_toasts'; import { TimelineId } from '../store/t_grid/types'; import * as i18n from './translations'; -import { TimelinesStartPlugins } from '../types'; +import { getSearchTransactionName, useStartTransaction } from '../lib/apm/use_start_transaction'; export type InspectResponse = Inspect & { response: string[] }; @@ -118,14 +117,16 @@ export const initSortDefault = [ ]; const useApmTracking = (timelineId: string) => { - const { apm } = useKibana().services; + const { startTransaction } = useStartTransaction(); const startTracking = useCallback(() => { // Create the transaction, the managed flag is turned off to prevent it from being polluted by non-related automatic spans. // The managed flag can be turned on to investigate high latency requests in APM. // However, note that by enabling the managed flag, the transaction trace may be distorted by other requests information. - const transaction = apm?.startTransaction(`Timeline search ${timelineId}`, 'http-request', { - managed: false, + const transaction = startTransaction({ + name: getSearchTransactionName(timelineId), + type: 'http-request', + options: { managed: false }, }); // Create a blocking span to control the transaction time and prevent it from closing automatically with partial batch responses. // The blocking span needs to be ended manually when the batched request finishes. @@ -136,7 +137,7 @@ const useApmTracking = (timelineId: string) => { span?.end(); }, }; - }, [apm, timelineId]); + }, [startTransaction, timelineId]); return { startTracking }; }; diff --git a/x-pack/plugins/timelines/public/hooks/use_bulk_action_items.tsx b/x-pack/plugins/timelines/public/hooks/use_bulk_action_items.tsx index 02abe3a229df4..202a648916081 100644 --- a/x-pack/plugins/timelines/public/hooks/use_bulk_action_items.tsx +++ b/x-pack/plugins/timelines/public/hooks/use_bulk_action_items.tsx @@ -13,6 +13,8 @@ import type { AlertStatus, BulkActionsProps } from '../../common/types/timeline' import { useUpdateAlertsStatus } from '../container/use_update_alerts'; import { useAppToasts } from './use_app_toasts'; import { STANDALONE_ID } from '../components/t_grid/standalone'; +import { useStartTransaction } from '../lib/apm/use_start_transaction'; +import { APM_USER_INTERACTIONS } from '../lib/apm/constants'; export const getUpdateAlertsQuery = (eventIds: Readonly) => { return { bool: { filter: { terms: { _id: eventIds } } } }; @@ -33,6 +35,7 @@ export const useBulkActionItems = ({ }: BulkActionsProps) => { const { updateAlertStatus } = useUpdateAlertsStatus(timelineId !== STANDALONE_ID); const { addSuccess, addError, addWarning } = useAppToasts(); + const { startTransaction } = useStartTransaction(); const onAlertStatusUpdateSuccess = useCallback( (updated: number, conflicts: number, newStatus: AlertStatus) => { @@ -88,6 +91,14 @@ export const useBulkActionItems = ({ const onClickUpdate = useCallback( async (status: AlertStatus) => { + if (query) { + startTransaction({ name: APM_USER_INTERACTIONS.BULK_QUERY_STATUS_UPDATE }); + } else if (eventIds.length > 1) { + startTransaction({ name: APM_USER_INTERACTIONS.BULK_STATUS_UPDATE }); + } else { + startTransaction({ name: APM_USER_INTERACTIONS.STATUS_UPDATE }); + } + try { setEventsLoading({ eventIds, isLoading: true }); @@ -120,6 +131,7 @@ export const useBulkActionItems = ({ setEventsDeleted, onAlertStatusUpdateSuccess, onAlertStatusUpdateFailure, + startTransaction, ] ); diff --git a/x-pack/plugins/timelines/public/lib/apm/constants.ts b/x-pack/plugins/timelines/public/lib/apm/constants.ts new file mode 100644 index 0000000000000..6b8036f2d2393 --- /dev/null +++ b/x-pack/plugins/timelines/public/lib/apm/constants.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 const APM_USER_INTERACTIONS = { + BULK_QUERY_STATUS_UPDATE: 'Timeline bulkQueryStatusUpdate', + BULK_STATUS_UPDATE: 'Timeline bulkStatusUpdate', + STATUS_UPDATE: 'Timeline statusUpdate', +} as const; diff --git a/x-pack/plugins/timelines/public/lib/apm/types.ts b/x-pack/plugins/timelines/public/lib/apm/types.ts new file mode 100644 index 0000000000000..eb52ab17b2f94 --- /dev/null +++ b/x-pack/plugins/timelines/public/lib/apm/types.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { APM_USER_INTERACTIONS } from './constants'; + +export type ApmUserInteractionName = + typeof APM_USER_INTERACTIONS[keyof typeof APM_USER_INTERACTIONS]; + +export type ApmSearchRequestName = `Timeline search ${string}`; + +export type ApmTransactionName = ApmSearchRequestName | ApmUserInteractionName; diff --git a/x-pack/plugins/timelines/public/lib/apm/use_start_transaction.ts b/x-pack/plugins/timelines/public/lib/apm/use_start_transaction.ts new file mode 100644 index 0000000000000..fa47db412e467 --- /dev/null +++ b/x-pack/plugins/timelines/public/lib/apm/use_start_transaction.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 { useCallback } from 'react'; +import type { TransactionOptions } from '@elastic/apm-rum'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { TimelinesStartPlugins } from '../../types'; +import type { ApmSearchRequestName, ApmTransactionName } from './types'; + +const DEFAULT_TRANSACTION_OPTIONS: TransactionOptions = { managed: true }; + +interface StartTransactionOptions { + name: ApmTransactionName; + type?: string; + options?: TransactionOptions; +} + +export const useStartTransaction = () => { + const { apm } = useKibana().services; + + const startTransaction = useCallback( + ({ name, type = 'user-interaction', options }: StartTransactionOptions) => { + return apm?.startTransaction(name, type, options ?? DEFAULT_TRANSACTION_OPTIONS); + }, + [apm] + ); + + return { startTransaction }; +}; + +export const getSearchTransactionName = (timelineId: string): ApmSearchRequestName => + `Timeline search ${timelineId}`; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 64f14578018dd..3e1a916085b40 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17142,8 +17142,6 @@ "xpack.lens.indexPattern.percentileRanks.documentation.markdown": "\nRetourne le pourcentage de valeurs qui sont en dessous d'une certaine valeur. Par exemple, si une valeur est supérieure à 95 % des valeurs observées, elle est placée au 95e rang centile.\n\nExemple : Obtenir le pourcentage de valeurs qui sont en dessous de 100 :\n`percentile_rank(bytes, value=100)`\n ", "xpack.lens.indexPattern.standardDeviation.documentation.markdown": "\nRetourne la taille de la variation ou de la dispersion du champ. Cette fonction ne s’applique qu’aux champs numériques.\n\n#### Exemples\n\nPour obtenir l'écart type d'un prix, utilisez standard_deviation(price).\n\nPour obtenir la variance du prix des commandes passées au Royaume-Uni, utilisez `square(standard_deviation(price, kql='location:UK'))`.\n ", "xpack.lens.indexPattern.time_scale.documentation.markdown": "\n\nCette fonction avancée est utile pour normaliser les comptes et les sommes sur un intervalle de temps spécifique. Elle permet l'intégration avec les indicateurs qui sont stockés déjà normalisés sur un intervalle de temps spécifique.\n\nVous pouvez faire appel à cette fonction uniquement si une fonction d'histogramme des dates est utilisée dans le graphique actuel.\n\nExemple : Un rapport comparant un indicateur déjà normalisé à un autre indicateur devant être normalisé.\n\"normalize_by_unit(counter_rate(max(system.diskio.write.bytes)), unit='s') / last_value(apache.status.bytes_per_second)\"\n ", - "xpack.lens.advancedSettings.useFieldExistenceSampling.description": "Lorsque cette option est activée, l’échantillonnage de document est utilisé pour déterminer l’existence des champs (disponibles ou vides) pour la liste de champs Lens au lieu de se fonder sur les mappings d’index.", - "xpack.lens.advancedSettings.useFieldExistenceSampling.title": "Utiliser l’échantillonnage d’existence des champs", "xpack.lens.app.addToLibrary": "Enregistrer dans la bibliothèque", "xpack.lens.app.cancel": "Annuler", "xpack.lens.app.cancelButtonAriaLabel": "Retour à la dernière application sans enregistrer les modifications", @@ -25212,7 +25210,6 @@ "xpack.security.unauthenticated.pageTitle": "Impossible de vous connecter", "xpack.security.users.breadcrumb": "Utilisateurs", "xpack.security.users.editUserPage.createBreadcrumb": "Créer", - "xpack.securitySolution.alertDetails.overview.hostDataTooltipContent": "La classification des risques n’est affichée que lorsqu’elle est disponible pour un hôte. Vérifiez que {hostsRiskScoreDocumentationLink} est activé dans votre environnement.", "xpack.securitySolution.alertDetails.overview.insights_related_alerts_by_source_event_count": "{count} {count, plural, =1 {alerte} other {alertes}} par événement source", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content": "Cette alerte a été détectée dans {caseCount}", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content_count": "{caseCount} {caseCount, plural, =0 {cas.} =1 {cas :} other {cas :}}", @@ -25451,17 +25448,12 @@ "xpack.securitySolution.eventsViewer.unit": "{totalCount, plural, =1 {événement} other {événements}}", "xpack.securitySolution.exceptions.dissasociateListSuccessText": "La liste d'exceptions ({id}) a été retirée avec succès", "xpack.securitySolution.exceptions.exceptionItem.showCommentsLabel": "Afficher {comments, plural, =1 {commentaire} other {commentaires}} ({comments})", - "xpack.securitySolution.exceptions.exceptionsPaginationLabel": "Éléments par page : {items}", - "xpack.securitySolution.exceptions.failedLoadPolicies": "Une erreur s'est produite lors du chargement des politiques : \"{error}\"", + "xpack.securitySolution.exceptions.failedLoadPolicies": "Une erreur s'est produite lors du chargement des politiques : \"{error}\"", "xpack.securitySolution.exceptions.fetch404Error": "La liste d'exceptions associée ({listId}) n'existe plus. Veuillez retirer la liste d'exceptions manquante pour ajouter des exceptions supplémentaires à la règle de détection.", "xpack.securitySolution.exceptions.hideCommentsLabel": "Masquer ({comments}) {comments, plural, =1 {commentaire} other {commentaires}}", - "xpack.securitySolution.exceptions.paginationNumberOfItemsLabel": "{items} éléments", "xpack.securitySolution.exceptions.referenceModalDescription": "Cette liste d'exceptions est associée à ({referenceCount}) {referenceCount, plural, =1 {règle} other {règles}}. Le retrait de cette liste d'exceptions supprimera également sa référence des règles associées.", "xpack.securitySolution.exceptions.referenceModalSuccessDescription": "Liste d'exceptions - {listId} - supprimée avec succès.", "xpack.securitySolution.exceptions.showCommentsLabel": "Afficher ({comments}) {comments, plural, =1 {commentaire} other {commentaires}}", - "xpack.securitySolution.exceptions.utilityNumberExceptionsLabel": "Affichage de {items} {items, plural, =1 {exception} other {exceptions}}", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription": "Toutes les exceptions à cette règle sont appliquées à la règle de détection, et non au point de terminaison. Afficher les {ruleSettings} pour plus de détails.", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription": "Toutes les exceptions à cette règle sont appliquées au point de terminaison et à la règle de détection. Afficher les {ruleSettings} pour plus de détails.", "xpack.securitySolution.fieldBrowser.descriptionForScreenReaderOnly": "Description pour le champ {field} :", "xpack.securitySolution.footer.autoRefreshActiveTooltip": "Lorsque l'actualisation automatique est activée, la chronologie vous montrera les {numberOfItems} derniers événements correspondant à votre recherche.", "xpack.securitySolution.formattedNumber.countsLabel": "{mantissa}{scale}{hasRemainder}", @@ -25473,7 +25465,6 @@ "xpack.securitySolution.hostIsolationExceptions.flyoutCreateSubmitSuccess": "\"{name}\" a été ajouté à votre liste d'exceptions d'isolation de l'hôte.", "xpack.securitySolution.hostIsolationExceptions.flyoutEditSubmitSuccess": "\"{name}\" a été mis à jour.", "xpack.securitySolution.hostIsolationExceptions.showingTotal": "Affichage de {total} {total, plural, one {exception d'isolation de l'hôte} other {exceptions d'isolation de l'hôte}}", - "xpack.securitySolution.hosts.hostRiskInformation.learnMore": "Pour en savoir plus sur le risque de l'hôte, cliquez {hostsRiskScoreDocumentationLink}", "xpack.securitySolution.hosts.navigaton.eventsUnit": "{totalCount, plural, =1 {événement} other {événements}}", "xpack.securitySolution.hostsRiskTable.filteredHostsTitle": "Afficher les hôtes à risque {severity}", "xpack.securitySolution.hostsTable.rows": "{numRows} {numRows, plural, =0 {ligne} =1 {ligne} other {lignes}}", @@ -25526,7 +25517,6 @@ "xpack.securitySolution.overview.ctiDashboardSubtitle": "Affichage : {totalCount} {totalCount, plural, one {indicateur} other {indicateurs}}", "xpack.securitySolution.overview.overviewHost.hostsSubtitle": "Affichage de : {formattedHostEventsCount} {hostEventsCount, plural, one {événement} other {événements}}", "xpack.securitySolution.overview.overviewNetwork.networkSubtitle": "Affichage de : {formattedNetworkEventsCount} {networkEventsCount, plural, one {événement} other {événements}}", - "xpack.securitySolution.overview.riskyHostsDashboardSubtitle": "Affichage : {totalCount} {totalCount, plural, one {hôte} other {hôtes}}", "xpack.securitySolution.overview.topNLabel": "Premiers {fieldName}", "xpack.securitySolution.pages.common.updateAlertStatusFailed": "Impossible de mettre à jour { conflicts } {conflicts, plural, =1 {alerte} other {alertes}}.", "xpack.securitySolution.pages.common.updateAlertStatusFailedDetailed": "{ updated } {updated, plural, =1 {alerte a été mise à jour} other {alertes ont été mises à jour}} correctement, mais { conflicts } n'ont pas pu être mis à jour\n car { conflicts, plural, =1 {elle était} other {elles étaient}} déjà en cours de modification.", @@ -25544,7 +25534,6 @@ "xpack.securitySolution.responder.header.lastSeen": "Vu en dernier le {date}", "xpack.securitySolution.responder.hostOffline.callout.body": "L'hôte {name} est hors connexion, donc ses réponses peuvent avoir du retard. Les commandes en attente seront exécutées quand l'hôte se reconnectera.", "xpack.securitySolution.responseActionsList.flyout.title": "Log d'action : {hostname}", - "xpack.securitySolution.responseActionsList.list.item.command": "{command}", "xpack.securitySolution.responseActionsList.list.item.hasExpired": "Échec de {command} : action expirée", "xpack.securitySolution.responseActionsList.list.item.hasFailed": "Échec de {command}", "xpack.securitySolution.responseActionsList.list.item.isPending": "{command} est en attente", @@ -25601,7 +25590,6 @@ "xpack.securitySolution.uncommonProcessTable.unit": "{totalCount, plural, other {processus}}", "xpack.securitySolution.useInputHints.exampleInstructions": "Ex : [ {exampleUsage} ]", "xpack.securitySolution.useInputHints.unknownCommand": "Commande inconnue {commandName}", - "xpack.securitySolution.users.userRiskInformation.learnMore": "Pour en savoir plus sur le risque de l'utilisateur, cliquez {usersRiskScoreDocumentationLink}", "xpack.securitySolution.usersRiskTable.filteredUsersTitle": "Afficher les utilisateurs à risque {severity}", "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, =0 {ligne} =1 {ligne} other {lignes}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, =1 {utilisateur} other {utilisateurs}}", @@ -25626,7 +25614,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "Champ", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "Valeur", "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "Données de risque de l’hôte", - "xpack.securitySolution.alertDetails.overview.hostsRiskScoreLink": "Score de risque de l’hôte", "xpack.securitySolution.alertDetails.overview.insights": "Informations exploitables", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "Alertes connexes par processus ancêtre", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "Impossible de récupérer les alertes.", @@ -28028,16 +28015,11 @@ "xpack.securitySolution.exceptions.addException.operatingSystemPlaceHolder": "Sélectionner un système d'exploitation", "xpack.securitySolution.exceptions.addException.sequenceWarning": "La requête de cette règle contient une instruction de séquence EQL. L'exception créée s'appliquera à tous les événements de la séquence.", "xpack.securitySolution.exceptions.addException.success": "Exception ajoutée avec succès", - "xpack.securitySolution.exceptions.andDescription": "AND", "xpack.securitySolution.exceptions.badge.readOnly.tooltip": "Impossible de créer, de modifier ou de supprimer des exceptions", "xpack.securitySolution.exceptions.cancelLabel": "Annuler", "xpack.securitySolution.exceptions.clearExceptionsLabel": "Retirer la liste d'exceptions", "xpack.securitySolution.exceptions.commentEventLabel": "a ajouté un commentaire", - "xpack.securitySolution.exceptions.commentLabel": "Commentaire", - "xpack.securitySolution.exceptions.descriptionLabel": "Description", - "xpack.securitySolution.exceptions.detectionListLabel": "Liste de détection", "xpack.securitySolution.exceptions.dissasociateExceptionListError": "Impossible de retirer la liste d'exceptions", - "xpack.securitySolution.exceptions.editButtonLabel": "Modifier", "xpack.securitySolution.exceptions.editException.bulkCloseLabel": "Fermer toutes les alertes qui correspondent à cette exception et ont été générées par cette règle", "xpack.securitySolution.exceptions.editException.bulkCloseLabel.disabled": "Fermer toutes les alertes qui correspondent à cette exception et ont été générées par cette règle (les listes et les champs non ECS ne sont pas pris en charge)", "xpack.securitySolution.exceptions.editException.cancel": "Annuler", @@ -28050,8 +28032,9 @@ "xpack.securitySolution.exceptions.editException.success": "L'exception a été mise à jour avec succès", "xpack.securitySolution.exceptions.editException.versionConflictDescription": "Cette exception semble avoir été mise à jour depuis que vous l'avez sélectionnée pour la modifier. Essayez de cliquer sur \"Annuler\" et de modifier à nouveau l'exception.", "xpack.securitySolution.exceptions.editException.versionConflictTitle": "Désolé, une erreur est survenue", - "xpack.securitySolution.exceptions.endpointListLabel": "Liste de points de terminaison", "xpack.securitySolution.exceptions.errorLabel": "Erreur", + "xpack.securitySolution.exceptions.fetchError": "Erreur lors de la récupération de la liste d'exceptions", + "xpack.securitySolution.exceptions.modalErrorAccordionText": "Afficher les informations de référence de la règle :", "xpack.securitySolution.exceptions.exceptionItem.conditions.and": "AND", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator": "existe", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator.not": "n'existe pas", @@ -28073,38 +28056,19 @@ "xpack.securitySolution.exceptions.exceptionItem.editItemButton": "Modifier l’élément", "xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy": "par", "xpack.securitySolution.exceptions.exceptionItem.updatedLabel": "Mis à jour", - "xpack.securitySolution.exceptions.fetchError": "Erreur lors de la récupération de la liste d'exceptions", - "xpack.securitySolution.exceptions.fieldDescription": "Champ", - "xpack.securitySolution.exceptions.modalErrorAccordionText": "Afficher les informations de référence de la règle :", - "xpack.securitySolution.exceptions.nameLabel": "Nom", "xpack.securitySolution.exceptions.operatingSystemFullLabel": "Système d'exploitation", "xpack.securitySolution.exceptions.operatingSystemLinux": "Linux", "xpack.securitySolution.exceptions.operatingSystemMac": "macOS", "xpack.securitySolution.exceptions.operatingSystemWindows": "Windows", "xpack.securitySolution.exceptions.operatingSystemWindowsAndMac": "Windows et macOS", - "xpack.securitySolution.exceptions.operatorDescription": "Opérateur", - "xpack.securitySolution.exceptions.orDescription": "OR", "xpack.securitySolution.exceptions.referenceModalCancelButton": "Annuler", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "Retirer la liste d'exceptions", "xpack.securitySolution.exceptions.referenceModalTitle": "Retirer la liste d'exceptions", - "xpack.securitySolution.exceptions.removeButtonLabel": "Retirer", "xpack.securitySolution.exceptions.searchPlaceholder": "par ex. Exemple de liste de noms", - "xpack.securitySolution.exceptions.utilityRefreshLabel": "Actualiser", - "xpack.securitySolution.exceptions.valueDescription": "Valeur", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "Ajouter un nouveau commentaire...", - "xpack.securitySolution.exceptions.viewer.addExceptionLabel": "Ajouter une nouvelle exception", "xpack.securitySolution.exceptions.viewer.addToClipboard": "Commentaire", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "Ajouter une exception à une règle", "xpack.securitySolution.exceptions.viewer.addToEndpointListLabel": "Ajouter une exception de point de terminaison", - "xpack.securitySolution.exceptions.viewer.deleteExceptionError": "Erreur lors de la suppression de l'exception", - "xpack.securitySolution.exceptions.viewer.emptyPromptBody": "Vous pouvez ajouter des exceptions pour affiner la règle de façon à ce que les alertes de détection ne soient pas créées lorsque les conditions d'exception sont remplies. Les exceptions améliorent la précision de la détection, ce qui peut permettre de réduire le nombre de faux positifs.", - "xpack.securitySolution.exceptions.viewer.emptyPromptTitle": "Cette règle ne possède aucune exception", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription.ruleSettingsLink": "paramètres de règles", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription.ruleSettingsLink": "paramètres de règles", - "xpack.securitySolution.exceptions.viewer.fetchingListError": "Erreur lors de la récupération des exceptions", - "xpack.securitySolution.exceptions.viewer.fetchTotalsError": "Erreur lors de l'obtention des totaux d'éléments de l'exception", - "xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody": "Aucun résultat n'a été trouvé pour la recherche.", - "xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder": "Champ de recherche (ex : host.name)", "xpack.securitySolution.exitFullScreenButton": "Quitter le plein écran", "xpack.securitySolution.expandedValue.hideTopValues.HideTopValues": "Masquer les valeurs les plus élevées", "xpack.securitySolution.expandedValue.links.expandIpDetails": "Développer les détails d'IP", @@ -28465,7 +28429,6 @@ "xpack.securitySolution.navigation.network": "Réseau", "xpack.securitySolution.navigation.newRuleTitle": "Créer une nouvelle règle", "xpack.securitySolution.navigation.overview": "Aperçu", - "xpack.securitySolution.navigation.responseActions": "Actions de réponse", "xpack.securitySolution.navigation.rules": "Règles", "xpack.securitySolution.navigation.threatIntelligence": "Threat Intelligence", "xpack.securitySolution.navigation.timelines": "Chronologies", @@ -28619,7 +28582,6 @@ "xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle": "Autres", "xpack.securitySolution.overview.ctiDashboardTitle": "Threat Intelligence", "xpack.securitySolution.overview.ctiViewDasboard": "Afficher le tableau de bord", - "xpack.securitySolution.overview.enableRiskScorePopoverTitle": "Les alertes doivent être disponibles avant d'activer le module", "xpack.securitySolution.overview.endgameDnsTitle": "DNS", "xpack.securitySolution.overview.endgameFileTitle": "Fichier", "xpack.securitySolution.overview.endgameImageLoadTitle": "Chargement de la page", @@ -28645,7 +28607,6 @@ "xpack.securitySolution.overview.hostStatGroupFilebeat": "Filebeat", "xpack.securitySolution.overview.hostStatGroupWinlogbeat": "Winlogbeat", "xpack.securitySolution.overview.hostsTitle": "Événements d'hôte", - "xpack.securitySolution.overview.importDasboard": "Importer un tableau de bord", "xpack.securitySolution.overview.landingCards.box.cloudCard.desc": "Évaluez votre niveau de cloud et protégez vos charges de travail contre les attaques.", "xpack.securitySolution.overview.landingCards.box.cloudCard.title": "Protection cloud de bout en bout", "xpack.securitySolution.overview.landingCards.box.endpoint.desc": "Prévention, collecte, détection et réponse, le tout avec Elastic Agent.", @@ -28668,14 +28629,6 @@ "xpack.securitySolution.overview.packetBeatFlowTitle": "Flux", "xpack.securitySolution.overview.packetbeatTLSTitle": "TLS", "xpack.securitySolution.overview.recentTimelinesSidebarTitle": "Chronologies récentes", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelButton": "Activer via Dev Tools", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelTitle": "Pas de données de score de risque de l'hôte", - "xpack.securitySolution.overview.riskyHostsDashboardEnableThreatIntel": "Vous devez activer le module de risque des hôtes pour visualiser les hôtes à risque.", - "xpack.securitySolution.overview.riskyHostsDashboardLearnMoreButton": "En savoir plus", - "xpack.securitySolution.overview.riskyHostsDashboardTitle": "Scores de risque de l'hôte actuel", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelBody": "Nous n'avons détecté aucune donnée de score de risque de l'hôte provenant des hôtes de votre environnement pour la plage temporelle sélectionnée.", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelTitle": "Aucune donnée de score de risque de l'hôte disponible pour l'affichage", - "xpack.securitySolution.overview.riskyHostsSource": "Source", "xpack.securitySolution.overview.signalCountTitle": "Tendance des alertes", "xpack.securitySolution.overview.viewAlertsButtonLabel": "Afficher les alertes", "xpack.securitySolution.overview.viewEventsButtonLabel": "Afficher les événements", @@ -28776,7 +28729,6 @@ "xpack.securitySolution.responseActionsList.list.screenReader.expand": "Développer les lignes", "xpack.securitySolution.responseActionsList.list.status": "Statut", "xpack.securitySolution.responseActionsList.list.time": "Heure", - "xpack.securitySolution.responseActionsList.list.title": "Actions de réponse", "xpack.securitySolution.responseActionsList.list.user": "Utilisateur", "xpack.securitySolution.riskScore.errorSearchDescription": "Une erreur s'est produite sur la recherche du score de risque", "xpack.securitySolution.riskScore.failSearchDescription": "Impossible de lancer une recherche sur le score de risque", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 09261d60004d5..6c875ca0c2333 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17127,8 +17127,6 @@ "xpack.lens.indexPattern.percentileRanks.documentation.markdown": "\n特定の値未満の値の割合が返されます。たとえば、値が観察された値の95%以上の場合、95パーセンタイルランクであるとされます。\n\n例:100未満の値のパーセンタイルを取得します。\n`percentile_rank(bytes, value=100)`\n ", "xpack.lens.indexPattern.standardDeviation.documentation.markdown": "\nフィールドの分散または散布度が返されます。この関数は数値フィールドでのみ動作します。\n\n#### 例\n\n価格の標準偏差を取得するには、standard_deviation(price)を使用します。\n\n英国からの注文書の価格の分散を取得するには、square(standard_deviation(price, kql='location:UK'))を使用します。\n ", "xpack.lens.indexPattern.time_scale.documentation.markdown": "\n\nこの高度な機能は、特定の期間に対してカウントと合計を正規化する際に役立ちます。すでに特定の期間に対して正規化され、保存されたメトリックとの統合が可能です。\n\nこの機能は、現在のグラフで日付ヒストグラム関数が使用されている場合にのみ使用できます。\n\n例:すでに正規化されているメトリックを、正規化が必要な別のメトリックと比較した比率。\n`normalize_by_unit(counter_rate(max(system.diskio.write.bytes)), unit='s') / last_value(apache.status.bytes_per_second)`\n ", - "xpack.lens.advancedSettings.useFieldExistenceSampling.description": "有効な場合、インデックスマッピングを使用するのではなく、ドキュメントサンプリングを使用して、Lensフィールドリストのフィールドの存在(使用可能または空)を決定します。", - "xpack.lens.advancedSettings.useFieldExistenceSampling.title": "フィールド存在サンプリングを使用", "xpack.lens.app.addToLibrary": "ライブラリに保存", "xpack.lens.app.cancel": "キャンセル", "xpack.lens.app.cancelButtonAriaLabel": "変更を保存せずに最後に使用していたアプリに戻る", @@ -25191,7 +25189,6 @@ "xpack.security.unauthenticated.pageTitle": "ログインできませんでした", "xpack.security.users.breadcrumb": "ユーザー", "xpack.security.users.editUserPage.createBreadcrumb": "作成", - "xpack.securitySolution.alertDetails.overview.hostDataTooltipContent": "リスク分類は、ホストで使用可能なときにのみ表示されます。環境内で{hostsRiskScoreDocumentationLink}が有効であることを確認してください。", "xpack.securitySolution.alertDetails.overview.insights_related_alerts_by_source_event_count": "ソースイベントに関連する{count} {count, plural, other {件のアラート}}", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content": "このアラートは{caseCount}で見つかりました", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content_count": "{caseCount} {caseCount, plural, other {個のケース:}}", @@ -25428,17 +25425,12 @@ "xpack.securitySolution.eventsViewer.unit": "{totalCount, plural, other {イベント}}", "xpack.securitySolution.exceptions.dissasociateListSuccessText": "例外リスト({id})が正常に削除されました", "xpack.securitySolution.exceptions.exceptionItem.showCommentsLabel": "{comments, plural, other {件のコメント}}を表示({comments})", - "xpack.securitySolution.exceptions.exceptionsPaginationLabel": "ページごとの項目数:{items}", "xpack.securitySolution.exceptions.failedLoadPolicies": "ポリシーの読み込みエラーが発生しました:\"{error}\"", "xpack.securitySolution.exceptions.fetch404Error": "関連付けられた例外リスト({listId})は存在しません。その他の例外を検出ルールに追加するには、見つからない例外リストを削除してください。", "xpack.securitySolution.exceptions.hideCommentsLabel": "({comments}){comments, plural, other {件のコメント}}を非表示", - "xpack.securitySolution.exceptions.paginationNumberOfItemsLabel": "{items}個の項目", "xpack.securitySolution.exceptions.referenceModalDescription": "この例外リストは、({referenceCount}) {referenceCount, plural, other {個のルール}}に関連付けられています。この例外リストを削除すると、関連付けられたルールからの参照も削除されます。", "xpack.securitySolution.exceptions.referenceModalSuccessDescription": "例外リスト{listId}が正常に削除されました。", "xpack.securitySolution.exceptions.showCommentsLabel": "({comments}){comments, plural, other {件のコメント}}を表示", - "xpack.securitySolution.exceptions.utilityNumberExceptionsLabel": "{items} {items, plural, other {件の例外}}を表示しています", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription": "このルールのすべての例外は、エンドポイントではなく、検出ルールに適用されます。詳細については、{ruleSettings}を確認してください。", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription": "このルールのすべての例外は、エンドポイントと検出ルールに適用されます。詳細については、{ruleSettings}を確認してください。", "xpack.securitySolution.fieldBrowser.descriptionForScreenReaderOnly": "フィールド {field} の説明:", "xpack.securitySolution.footer.autoRefreshActiveTooltip": "自動更新が有効な間、タイムラインはクエリに一致する最新の {numberOfItems} 件のイベントを表示します。", "xpack.securitySolution.formattedNumber.countsLabel": "{mantissa}{scale}{hasRemainder}", @@ -25450,7 +25442,6 @@ "xpack.securitySolution.hostIsolationExceptions.flyoutCreateSubmitSuccess": "\"{name}\"はホスト分離例外リストに追加されました。", "xpack.securitySolution.hostIsolationExceptions.flyoutEditSubmitSuccess": "\"{name}\"が更新されました。", "xpack.securitySolution.hostIsolationExceptions.showingTotal": "{total} {total, plural, other {個のホスト分離例外}}", - "xpack.securitySolution.hosts.hostRiskInformation.learnMore": "ホストリスクの詳細をご覧ください。{hostsRiskScoreDocumentationLink}", "xpack.securitySolution.hosts.navigaton.eventsUnit": "{totalCount, plural, other {イベント}}", "xpack.securitySolution.hostsRiskTable.filteredHostsTitle": "{severity}のリスクがあるホストを表示", "xpack.securitySolution.hostsTable.rows": "{numRows} {numRows, plural, other {行}}", @@ -25503,7 +25494,6 @@ "xpack.securitySolution.overview.ctiDashboardSubtitle": "{totalCount} {totalCount, plural, other {個の指標}}を表示しています", "xpack.securitySolution.overview.overviewHost.hostsSubtitle": "表示中:{formattedHostEventsCount} {hostEventsCount, plural, other {イベント}}", "xpack.securitySolution.overview.overviewNetwork.networkSubtitle": "表示中:{formattedNetworkEventsCount} {networkEventsCount, plural, other {イベント}}", - "xpack.securitySolution.overview.riskyHostsDashboardSubtitle": "{totalCount} {totalCount, plural, other {個のホスト}}を表示しています", "xpack.securitySolution.overview.topNLabel": "トップ{fieldName}", "xpack.securitySolution.pages.common.updateAlertStatusFailed": "{ conflicts } {conflicts, plural, other {アラート}}を更新できませんでした。", "xpack.securitySolution.pages.common.updateAlertStatusFailedDetailed": "{ updated } {updated, plural, other {アラート}}が正常に更新されましたが、{ conflicts }は更新できませんでした。\n { conflicts, plural, other {}}すでに修正されています。", @@ -25521,7 +25511,6 @@ "xpack.securitySolution.responder.header.lastSeen": "前回表示日時 {date}", "xpack.securitySolution.responder.hostOffline.callout.body": "ホスト{name}はオフラインであるため、応答が遅延する可能性があります。保留中のコマンドは、ホストが再接続されたときに実行されます。", "xpack.securitySolution.responseActionsList.flyout.title": "アクションログ:{hostname}", - "xpack.securitySolution.responseActionsList.list.item.command": "{command}", "xpack.securitySolution.responseActionsList.list.item.hasExpired": "{command}が失敗しました:アクションの有効期限が切れました", "xpack.securitySolution.responseActionsList.list.item.hasFailed": "{command}が失敗しました", "xpack.securitySolution.responseActionsList.list.item.isPending": "{command}は保留中です", @@ -25578,7 +25567,6 @@ "xpack.securitySolution.uncommonProcessTable.unit": "{totalCount, plural, other {プロセス}}", "xpack.securitySolution.useInputHints.exampleInstructions": "例:[ {exampleUsage} ]", "xpack.securitySolution.useInputHints.unknownCommand": "不明なコマンド{commandName}", - "xpack.securitySolution.users.userRiskInformation.learnMore": "ユーザーリスクの詳細をご覧ください。{usersRiskScoreDocumentationLink}", "xpack.securitySolution.usersRiskTable.filteredUsersTitle": "{severity}リスクのユーザーを表示", "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, other {ユーザー}}", @@ -25603,7 +25591,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "フィールド", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "値", "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "ホストリスクデータ", - "xpack.securitySolution.alertDetails.overview.hostsRiskScoreLink": "ホストリスクスコア", "xpack.securitySolution.alertDetails.overview.insights": "インサイト", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "上位プロセス別関連アラート", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "アラートを取得できませんでした。", @@ -28005,16 +27992,11 @@ "xpack.securitySolution.exceptions.addException.operatingSystemPlaceHolder": "オペレーティングシステムを選択", "xpack.securitySolution.exceptions.addException.sequenceWarning": "このルールのクエリにはEQLシーケンス文があります。作成された例外は、シーケンスのすべてのイベントに適用されます。", "xpack.securitySolution.exceptions.addException.success": "正常に例外を追加しました", - "xpack.securitySolution.exceptions.andDescription": "AND", "xpack.securitySolution.exceptions.badge.readOnly.tooltip": "例外を作成、編集、削除できません", "xpack.securitySolution.exceptions.cancelLabel": "キャンセル", "xpack.securitySolution.exceptions.clearExceptionsLabel": "例外リストを削除", "xpack.securitySolution.exceptions.commentEventLabel": "コメントを追加しました", - "xpack.securitySolution.exceptions.commentLabel": "コメント", - "xpack.securitySolution.exceptions.descriptionLabel": "説明", - "xpack.securitySolution.exceptions.detectionListLabel": "検出リスト", "xpack.securitySolution.exceptions.dissasociateExceptionListError": "例外リストを削除できませんでした", - "xpack.securitySolution.exceptions.editButtonLabel": "編集", "xpack.securitySolution.exceptions.editException.bulkCloseLabel": "この例外一致し、このルールによって生成された、すべてのアラートを閉じる", "xpack.securitySolution.exceptions.editException.bulkCloseLabel.disabled": "この例外と一致し、このルールによって生成された、すべてのアラートを閉じる(リストと非ECSフィールドはサポートされません)", "xpack.securitySolution.exceptions.editException.cancel": "キャンセル", @@ -28027,8 +28009,8 @@ "xpack.securitySolution.exceptions.editException.success": "正常に例外を更新しました", "xpack.securitySolution.exceptions.editException.versionConflictDescription": "最初に編集することを選択したときからこの例外が更新されている可能性があります。[キャンセル]をクリックし、もう一度例外を編集してください。", "xpack.securitySolution.exceptions.editException.versionConflictTitle": "申し訳ございません、エラーが発生しました", - "xpack.securitySolution.exceptions.endpointListLabel": "エンドポイントリスト", "xpack.securitySolution.exceptions.errorLabel": "エラー", + "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", "xpack.securitySolution.exceptions.exceptionItem.conditions.and": "AND", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator": "存在する", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator.not": "存在しない", @@ -28050,38 +28032,20 @@ "xpack.securitySolution.exceptions.exceptionItem.editItemButton": "項目を編集", "xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy": "グループ基準", "xpack.securitySolution.exceptions.exceptionItem.updatedLabel": "更新しました", - "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", - "xpack.securitySolution.exceptions.fieldDescription": "フィールド", "xpack.securitySolution.exceptions.modalErrorAccordionText": "ルール参照情報を表示:", - "xpack.securitySolution.exceptions.nameLabel": "名前", "xpack.securitySolution.exceptions.operatingSystemFullLabel": "オペレーティングシステム", "xpack.securitySolution.exceptions.operatingSystemLinux": "Linux", "xpack.securitySolution.exceptions.operatingSystemMac": "macOS", "xpack.securitySolution.exceptions.operatingSystemWindows": "Windows", "xpack.securitySolution.exceptions.operatingSystemWindowsAndMac": "WindowsおよびmacOS", - "xpack.securitySolution.exceptions.operatorDescription": "演算子", - "xpack.securitySolution.exceptions.orDescription": "OR", "xpack.securitySolution.exceptions.referenceModalCancelButton": "キャンセル", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "例外リストを削除", "xpack.securitySolution.exceptions.referenceModalTitle": "例外リストを削除", - "xpack.securitySolution.exceptions.removeButtonLabel": "削除", "xpack.securitySolution.exceptions.searchPlaceholder": "例:例外リスト名", - "xpack.securitySolution.exceptions.utilityRefreshLabel": "更新", - "xpack.securitySolution.exceptions.valueDescription": "値", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "新しいコメントを追加...", - "xpack.securitySolution.exceptions.viewer.addExceptionLabel": "新しい例外を追加", "xpack.securitySolution.exceptions.viewer.addToClipboard": "コメント", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "ルール例外の追加", "xpack.securitySolution.exceptions.viewer.addToEndpointListLabel": "エンドポイント例外の追加", - "xpack.securitySolution.exceptions.viewer.deleteExceptionError": "例外の削除エラー", - "xpack.securitySolution.exceptions.viewer.emptyPromptBody": "例外を追加してルールを微調整し、例外条件が満たされたときに検出アラートが作成されないようにすることができます。例外により検出の精度が改善します。これにより、誤検出数が減ります。", - "xpack.securitySolution.exceptions.viewer.emptyPromptTitle": "このルールには例外がありません", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription.ruleSettingsLink": "ルール設定", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription.ruleSettingsLink": "ルール設定", - "xpack.securitySolution.exceptions.viewer.fetchingListError": "例外の取得エラー", - "xpack.securitySolution.exceptions.viewer.fetchTotalsError": "例外項目合計数の取得エラー", - "xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody": "検索結果が見つかりません。", - "xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder": "検索フィールド(例:host.name)", "xpack.securitySolution.exitFullScreenButton": "全画面を終了", "xpack.securitySolution.expandedValue.hideTopValues.HideTopValues": "上位の値を非表示", "xpack.securitySolution.expandedValue.links.expandIpDetails": "IP詳細を展開", @@ -28442,7 +28406,6 @@ "xpack.securitySolution.navigation.network": "ネットワーク", "xpack.securitySolution.navigation.newRuleTitle": "新規ルールを作成", "xpack.securitySolution.navigation.overview": "概要", - "xpack.securitySolution.navigation.responseActions": "対応アクション", "xpack.securitySolution.navigation.rules": "ルール", "xpack.securitySolution.navigation.threatIntelligence": "脅威インテリジェンス", "xpack.securitySolution.navigation.timelines": "タイムライン", @@ -28596,7 +28559,6 @@ "xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle": "その他", "xpack.securitySolution.overview.ctiDashboardTitle": "脅威インテリジェンス", "xpack.securitySolution.overview.ctiViewDasboard": "ダッシュボードを表示", - "xpack.securitySolution.overview.enableRiskScorePopoverTitle": "モジュールを有効にする前に、アラートが使用可能でなければなりません", "xpack.securitySolution.overview.endgameDnsTitle": "DNS", "xpack.securitySolution.overview.endgameFileTitle": "ファイル", "xpack.securitySolution.overview.endgameImageLoadTitle": "画像読み込み", @@ -28622,7 +28584,6 @@ "xpack.securitySolution.overview.hostStatGroupFilebeat": "Filebeat", "xpack.securitySolution.overview.hostStatGroupWinlogbeat": "Winlogbeat", "xpack.securitySolution.overview.hostsTitle": "ホストイベント", - "xpack.securitySolution.overview.importDasboard": "ダッシュボードをインポート", "xpack.securitySolution.overview.landingCards.box.cloudCard.desc": "クラウド態勢を評価し、ワークロードを攻撃から保護します。", "xpack.securitySolution.overview.landingCards.box.cloudCard.title": "エンドツーエンドのクラウド保護", "xpack.securitySolution.overview.landingCards.box.endpoint.desc": "防御から収集、検知、対応まで実行する、Elastic Agent。", @@ -28645,14 +28606,6 @@ "xpack.securitySolution.overview.packetBeatFlowTitle": "フロー", "xpack.securitySolution.overview.packetbeatTLSTitle": "TLS", "xpack.securitySolution.overview.recentTimelinesSidebarTitle": "最近のタイムライン", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelButton": "開発ツールで有効化", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelTitle": "ホストリスクスコアデータがありません", - "xpack.securitySolution.overview.riskyHostsDashboardEnableThreatIntel": "リスクがあるホストを表示するには、ホストリスクモジュールを有効化する必要があります。", - "xpack.securitySolution.overview.riskyHostsDashboardLearnMoreButton": "詳細情報", - "xpack.securitySolution.overview.riskyHostsDashboardTitle": "現在のホストリスクスコア", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelBody": "選択した期間では、ご使用の環境のホストからホストリスクスコアデータが検出されませんでした。", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelTitle": "表示するホストリスクスコアデータがありません", - "xpack.securitySolution.overview.riskyHostsSource": "送信元", "xpack.securitySolution.overview.signalCountTitle": "アラート傾向", "xpack.securitySolution.overview.viewAlertsButtonLabel": "アラートを表示", "xpack.securitySolution.overview.viewEventsButtonLabel": "イベントを表示", @@ -28753,7 +28706,6 @@ "xpack.securitySolution.responseActionsList.list.screenReader.expand": "行を展開", "xpack.securitySolution.responseActionsList.list.status": "ステータス", "xpack.securitySolution.responseActionsList.list.time": "時間", - "xpack.securitySolution.responseActionsList.list.title": "対応アクション", "xpack.securitySolution.responseActionsList.list.user": "ユーザー", "xpack.securitySolution.riskScore.errorSearchDescription": "リスクスコア検索でエラーが発生しました", "xpack.securitySolution.riskScore.failSearchDescription": "リスクスコアで検索を実行できませんでした", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b5b1c849a696f..05d1f7e1f51e8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17149,8 +17149,6 @@ "xpack.lens.indexPattern.percentileRanks.documentation.markdown": "\n返回小于某个值的值的百分比。例如,如果某个值大于或等于 95% 的观察值,则称它处于第 95 个百分位等级\n\n例如:获取小于 100 的值的百分比:\n`percentile_rank(bytes, value=100)`\n ", "xpack.lens.indexPattern.standardDeviation.documentation.markdown": "\n返回字段的变量或差量数量。此函数仅适用于数字字段。\n\n#### 示例\n\n要获取价格的标准偏差,请使用 `standard_deviation(price)`。\n\n要获取来自英国的订单的价格方差,请使用 `square(standard_deviation(price, kql='location:UK'))`。\n ", "xpack.lens.indexPattern.time_scale.documentation.markdown": "\n\n此高级函数用于将计数和总和标准化为特定时间间隔。它允许集成所存储的已标准化为特定时间间隔的指标。\n\n此函数只能在当前图表中使用了日期直方图函数时使用。\n\n例如:将已标准化指标与其他需要标准化的指标进行比较的比率。\n`normalize_by_unit(counter_rate(max(system.diskio.write.bytes)), unit='s') / last_value(apache.status.bytes_per_second)`\n ", - "xpack.lens.advancedSettings.useFieldExistenceSampling.description": "如果启用,文档采样将用于确定 Lens 字段列表中的字段是否存在(可用或为空),而不依赖索引映射。", - "xpack.lens.advancedSettings.useFieldExistenceSampling.title": "使用字段存在采样", "xpack.lens.app.addToLibrary": "保存到库", "xpack.lens.app.cancel": "取消", "xpack.lens.app.cancelButtonAriaLabel": "返回到上一个应用而不保存更改", @@ -25220,7 +25218,6 @@ "xpack.security.unauthenticated.pageTitle": "我们无法使您登录", "xpack.security.users.breadcrumb": "用户", "xpack.security.users.editUserPage.createBreadcrumb": "创建", - "xpack.securitySolution.alertDetails.overview.hostDataTooltipContent": "仅在其对主机可用时才会显示风险分类。确保在您的环境中启用了 {hostsRiskScoreDocumentationLink}。", "xpack.securitySolution.alertDetails.overview.insights_related_alerts_by_source_event_count": "{count} 个{count, plural, other {告警}}与源事件相关", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content": "发现此告警位于 {caseCount}", "xpack.securitySolution.alertDetails.overview.insights_related_cases_found_content_count": "{caseCount} 个{caseCount, plural, other {案例:}}", @@ -25459,17 +25456,12 @@ "xpack.securitySolution.eventsViewer.unit": "{totalCount, plural, other {个事件}}", "xpack.securitySolution.exceptions.dissasociateListSuccessText": "例外列表 ({id}) 已成功移除", "xpack.securitySolution.exceptions.exceptionItem.showCommentsLabel": "显示{comments, plural, other {注释}} ({comments})", - "xpack.securitySolution.exceptions.exceptionsPaginationLabel": "每页项数:{items}", "xpack.securitySolution.exceptions.failedLoadPolicies": "加载策略时出错:“{error}”", "xpack.securitySolution.exceptions.fetch404Error": "关联的例外列表 ({listId}) 已不存在。请移除缺少的例外列表,以将其他例外添加到检测规则。", "xpack.securitySolution.exceptions.hideCommentsLabel": "隐藏 ({comments}) 个{comments, plural, other {注释}}", - "xpack.securitySolution.exceptions.paginationNumberOfItemsLabel": "{items} 项", "xpack.securitySolution.exceptions.referenceModalDescription": "此例外列表与 ({referenceCount}) 个{referenceCount, plural, other {规则}}关联。移除此例外列表还将会删除其对关联规则的引用。", "xpack.securitySolution.exceptions.referenceModalSuccessDescription": "例外列表 - {listId} - 已成功删除。", "xpack.securitySolution.exceptions.showCommentsLabel": "显示 ({comments} 个) {comments, plural, other {注释}}", - "xpack.securitySolution.exceptions.utilityNumberExceptionsLabel": "正在显示 {items} 个{items, plural, other {例外}}", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription": "此规则的所有例外将应用到检测规则,而非终端。查看{ruleSettings}以了解详情。", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription": "此规则的所有例外将应用到终端和检测规则。查看{ruleSettings}以了解详情。", "xpack.securitySolution.fieldBrowser.descriptionForScreenReaderOnly": "{field} 字段的描述:", "xpack.securitySolution.footer.autoRefreshActiveTooltip": "自动刷新已启用时,时间线将显示匹配查询的最近 {numberOfItems} 个事件。", "xpack.securitySolution.formattedNumber.countsLabel": "{mantissa}{scale}{hasRemainder}", @@ -25481,7 +25473,6 @@ "xpack.securitySolution.hostIsolationExceptions.flyoutCreateSubmitSuccess": "已将“{name}”添加到您的主机隔离例外列表。", "xpack.securitySolution.hostIsolationExceptions.flyoutEditSubmitSuccess": "“{name}”已更新。", "xpack.securitySolution.hostIsolationExceptions.showingTotal": "正在显示 {total} 个{total, plural, other {主机隔离例外}}", - "xpack.securitySolution.hosts.hostRiskInformation.learnMore": "您可以详细了解主机风险{hostsRiskScoreDocumentationLink}", "xpack.securitySolution.hosts.navigaton.eventsUnit": "{totalCount, plural, other {个事件}}", "xpack.securitySolution.hostsRiskTable.filteredHostsTitle": "查看{severity}风险主机", "xpack.securitySolution.hostsTable.rows": "{numRows} {numRows, plural, other {行}}", @@ -25534,7 +25525,6 @@ "xpack.securitySolution.overview.ctiDashboardSubtitle": "正在显示:{totalCount} 个{totalCount, plural, other {指标}}", "xpack.securitySolution.overview.overviewHost.hostsSubtitle": "正在显示:{formattedHostEventsCount} 个{hostEventsCount, plural, other {事件}}", "xpack.securitySolution.overview.overviewNetwork.networkSubtitle": "正在显示:{formattedNetworkEventsCount} 个{networkEventsCount, plural, other {事件}}", - "xpack.securitySolution.overview.riskyHostsDashboardSubtitle": "正在显示:{totalCount} 台{totalCount, plural, other {主机}}", "xpack.securitySolution.overview.topNLabel": "排名靠前的{fieldName}", "xpack.securitySolution.pages.common.updateAlertStatusFailed": "无法更新{ conflicts } 个{conflicts, plural, other {告警}}。", "xpack.securitySolution.pages.common.updateAlertStatusFailedDetailed": "{ updated } 个{updated, plural, other {告警}}已成功更新,但是 { conflicts } 个无法更新,\n 因为{ conflicts, plural, other {其}}已被修改。", @@ -25552,7 +25542,6 @@ "xpack.securitySolution.responder.header.lastSeen": "最后看到时间 {date}", "xpack.securitySolution.responder.hostOffline.callout.body": "主机 {name} 脱机,因此其响应可能会延迟。主机重新建立连接后将执行待处理的命令。", "xpack.securitySolution.responseActionsList.flyout.title": "操作日志:{hostname}", - "xpack.securitySolution.responseActionsList.list.item.command": "{command}", "xpack.securitySolution.responseActionsList.list.item.hasExpired": "{command} 失败:操作已过期", "xpack.securitySolution.responseActionsList.list.item.hasFailed": "{command} 失败", "xpack.securitySolution.responseActionsList.list.item.isPending": "{command} 待处理", @@ -25609,7 +25598,6 @@ "xpack.securitySolution.uncommonProcessTable.unit": "{totalCount, plural, other {个进程}}", "xpack.securitySolution.useInputHints.exampleInstructions": "例如:[ {exampleUsage} ]", "xpack.securitySolution.useInputHints.unknownCommand": "未知命令 {commandName}", - "xpack.securitySolution.users.userRiskInformation.learnMore": "您可以详细了解用户风险{usersRiskScoreDocumentationLink}", "xpack.securitySolution.usersRiskTable.filteredUsersTitle": "查看{severity}风险用户", "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, other {个用户}}", @@ -25634,7 +25622,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "字段", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "值", "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "主机风险数据", - "xpack.securitySolution.alertDetails.overview.hostsRiskScoreLink": "主机风险分数", "xpack.securitySolution.alertDetails.overview.insights": "洞见", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "按进程体系列出相关告警", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "无法获取告警。", @@ -28036,16 +28023,11 @@ "xpack.securitySolution.exceptions.addException.operatingSystemPlaceHolder": "选择操作系统", "xpack.securitySolution.exceptions.addException.sequenceWarning": "此规则的查询包含 EQL 序列语句。创建的例外将应用于序列中的所有事件。", "xpack.securitySolution.exceptions.addException.success": "已成功添加例外", - "xpack.securitySolution.exceptions.andDescription": "且", "xpack.securitySolution.exceptions.badge.readOnly.tooltip": "无法创建、编辑或删除例外", "xpack.securitySolution.exceptions.cancelLabel": "取消", "xpack.securitySolution.exceptions.clearExceptionsLabel": "移除例外列表", "xpack.securitySolution.exceptions.commentEventLabel": "已添加注释", - "xpack.securitySolution.exceptions.commentLabel": "注释", - "xpack.securitySolution.exceptions.descriptionLabel": "描述", - "xpack.securitySolution.exceptions.detectionListLabel": "检测列表", "xpack.securitySolution.exceptions.dissasociateExceptionListError": "无法移除例外列表", - "xpack.securitySolution.exceptions.editButtonLabel": "编辑", "xpack.securitySolution.exceptions.editException.bulkCloseLabel": "关闭所有与此例外匹配且根据此规则生成的告警", "xpack.securitySolution.exceptions.editException.bulkCloseLabel.disabled": "关闭所有与此例外匹配且根据此规则生成的告警(不支持列表和非 ECS 字段)", "xpack.securitySolution.exceptions.editException.cancel": "取消", @@ -28058,8 +28040,8 @@ "xpack.securitySolution.exceptions.editException.success": "已成功更新例外", "xpack.securitySolution.exceptions.editException.versionConflictDescription": "此例外可能自您首次选择编辑后已更新。尝试单击“取消”,重新编辑该例外。", "xpack.securitySolution.exceptions.editException.versionConflictTitle": "抱歉,有错误", - "xpack.securitySolution.exceptions.endpointListLabel": "终端列表", "xpack.securitySolution.exceptions.errorLabel": "错误", + "xpack.securitySolution.exceptions.fetchError": "提取例外列表时出错", "xpack.securitySolution.exceptions.exceptionItem.conditions.and": "且", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator": "存在", "xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator.not": "不存在", @@ -28081,38 +28063,20 @@ "xpack.securitySolution.exceptions.exceptionItem.editItemButton": "编辑项目", "xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy": "依据", "xpack.securitySolution.exceptions.exceptionItem.updatedLabel": "已更新", - "xpack.securitySolution.exceptions.fetchError": "提取例外列表时出错", - "xpack.securitySolution.exceptions.fieldDescription": "字段", "xpack.securitySolution.exceptions.modalErrorAccordionText": "显示规则引用信息:", - "xpack.securitySolution.exceptions.nameLabel": "名称", "xpack.securitySolution.exceptions.operatingSystemFullLabel": "操作系统", "xpack.securitySolution.exceptions.operatingSystemLinux": "Linux", "xpack.securitySolution.exceptions.operatingSystemMac": "macOS", "xpack.securitySolution.exceptions.operatingSystemWindows": "Windows", "xpack.securitySolution.exceptions.operatingSystemWindowsAndMac": "Windows 和 macOS", - "xpack.securitySolution.exceptions.operatorDescription": "运算符", - "xpack.securitySolution.exceptions.orDescription": "OR", "xpack.securitySolution.exceptions.referenceModalCancelButton": "取消", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "移除例外列表", "xpack.securitySolution.exceptions.referenceModalTitle": "移除例外列表", - "xpack.securitySolution.exceptions.removeButtonLabel": "移除", "xpack.securitySolution.exceptions.searchPlaceholder": "例如,示例列表名称", - "xpack.securitySolution.exceptions.utilityRefreshLabel": "刷新", - "xpack.securitySolution.exceptions.valueDescription": "值", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "添加新注释......", - "xpack.securitySolution.exceptions.viewer.addExceptionLabel": "添加新例外", "xpack.securitySolution.exceptions.viewer.addToClipboard": "注释", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "添加规则例外", "xpack.securitySolution.exceptions.viewer.addToEndpointListLabel": "添加终端例外", - "xpack.securitySolution.exceptions.viewer.deleteExceptionError": "删除例外时出错", - "xpack.securitySolution.exceptions.viewer.emptyPromptBody": "可以添加例外以微调规则,以便在满足例外条件时不创建检测告警。例外提升检测精确性,从而可以减少误报数。", - "xpack.securitySolution.exceptions.viewer.emptyPromptTitle": "此规则没有例外", - "xpack.securitySolution.exceptions.viewer.exceptionDetectionDetailsDescription.ruleSettingsLink": "规则设置", - "xpack.securitySolution.exceptions.viewer.exceptionEndpointDetailsDescription.ruleSettingsLink": "规则设置", - "xpack.securitySolution.exceptions.viewer.fetchingListError": "提取例外时出错", - "xpack.securitySolution.exceptions.viewer.fetchTotalsError": "获取例外项总数时出错", - "xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody": "找不到搜索结果。", - "xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder": "搜索字段(例如:host.name)", "xpack.securitySolution.exitFullScreenButton": "退出全屏", "xpack.securitySolution.expandedValue.hideTopValues.HideTopValues": "隐藏排名最前值", "xpack.securitySolution.expandedValue.links.expandIpDetails": "展开 IP 详情", @@ -28473,7 +28437,6 @@ "xpack.securitySolution.navigation.network": "网络", "xpack.securitySolution.navigation.newRuleTitle": "创建新规则", "xpack.securitySolution.navigation.overview": "概览", - "xpack.securitySolution.navigation.responseActions": "响应操作", "xpack.securitySolution.navigation.rules": "规则", "xpack.securitySolution.navigation.threatIntelligence": "威胁情报", "xpack.securitySolution.navigation.timelines": "时间线", @@ -28627,7 +28590,6 @@ "xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle": "其他", "xpack.securitySolution.overview.ctiDashboardTitle": "威胁情报", "xpack.securitySolution.overview.ctiViewDasboard": "查看仪表板", - "xpack.securitySolution.overview.enableRiskScorePopoverTitle": "启用模块之前,告警需要处于可用状态", "xpack.securitySolution.overview.endgameDnsTitle": "DNS", "xpack.securitySolution.overview.endgameFileTitle": "文件", "xpack.securitySolution.overview.endgameImageLoadTitle": "映像加载", @@ -28653,7 +28615,6 @@ "xpack.securitySolution.overview.hostStatGroupFilebeat": "Filebeat", "xpack.securitySolution.overview.hostStatGroupWinlogbeat": "Winlogbeat", "xpack.securitySolution.overview.hostsTitle": "主机事件", - "xpack.securitySolution.overview.importDasboard": "导入仪表板", "xpack.securitySolution.overview.landingCards.box.cloudCard.desc": "评估您的云态势并防止工作负载受到攻击。", "xpack.securitySolution.overview.landingCards.box.cloudCard.title": "端到端云防护", "xpack.securitySolution.overview.landingCards.box.endpoint.desc": "防御、收集、检测和响应 — 所有这些活动均可通过 Elastic 代理来实现。", @@ -28676,14 +28637,6 @@ "xpack.securitySolution.overview.packetBeatFlowTitle": "流", "xpack.securitySolution.overview.packetbeatTLSTitle": "TLS", "xpack.securitySolution.overview.recentTimelinesSidebarTitle": "最近的时间线", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelButton": "通过开发工具启用", - "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelTitle": "无主机风险分数数据", - "xpack.securitySolution.overview.riskyHostsDashboardEnableThreatIntel": "必须启用主机风险模块才能查看有风险主机。", - "xpack.securitySolution.overview.riskyHostsDashboardLearnMoreButton": "了解详情", - "xpack.securitySolution.overview.riskyHostsDashboardTitle": "当前主机风险分数", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelBody": "对于选定时间范围,我们尚未从您环境中的主机中检测到任何主机风险分数数据。", - "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelTitle": "没有可显示的主机风险分数数据", - "xpack.securitySolution.overview.riskyHostsSource": "源", "xpack.securitySolution.overview.signalCountTitle": "告警趋势", "xpack.securitySolution.overview.viewAlertsButtonLabel": "查看告警", "xpack.securitySolution.overview.viewEventsButtonLabel": "查看事件", @@ -28784,7 +28737,6 @@ "xpack.securitySolution.responseActionsList.list.screenReader.expand": "展开行", "xpack.securitySolution.responseActionsList.list.status": "状态", "xpack.securitySolution.responseActionsList.list.time": "时间", - "xpack.securitySolution.responseActionsList.list.title": "响应操作", "xpack.securitySolution.responseActionsList.list.user": "用户", "xpack.securitySolution.riskScore.errorSearchDescription": "搜索风险分数时发生错误", "xpack.securitySolution.riskScore.failSearchDescription": "无法对风险分数执行搜索", 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 770a939620b9b..5f2dcfcb5ff62 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -73,7 +73,7 @@ export const renderApp = (deps: TriggersAndActionsUiServices) => { export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => { const { dataViews, uiSettings, theme$ } = deps; - const sections: Section[] = ['rules', 'connectors', 'alerts']; + const sections: Section[] = ['rules', 'connectors', 'logs', 'alerts']; const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); const sectionsRegex = sections.join('|'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 0792f6703efb4..64bd67989cfbf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -13,11 +13,12 @@ export { } from '@kbn/alerting-plugin/common'; export { BASE_ACTION_API_PATH, INTERNAL_BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; -export type Section = 'connectors' | 'rules' | 'alerts'; +export type Section = 'connectors' | 'rules' | 'alerts' | 'logs'; export const routeToHome = `/`; export const routeToConnectors = `/connectors`; export const routeToRules = `/rules`; +export const routeToLogs = `/logs`; export const routeToRuleDetails = `/rule/:ruleId`; export const routeToInternalAlerts = `/alerts`; export const legacyRouteToRules = `/alerts`; @@ -41,6 +42,8 @@ export const DEFAULT_SEARCH_PAGE_SIZE: number = 10; export const DEFAULT_RULE_INTERVAL = '1m'; export const RULE_EXECUTION_LOG_COLUMN_IDS = [ + 'rule_id', + 'rule_name', 'id', 'timestamp', 'execution_duration', @@ -73,6 +76,7 @@ export const RULE_EXECUTION_LOG_ALERT_COUNT_COLUMNS = [ ]; export const LOCKED_COLUMNS = [ + 'rule_name', 'timestamp', 'execution_duration', 'status', @@ -81,4 +85,5 @@ export const LOCKED_COLUMNS = [ 'num_errored_actions', ]; -export const RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS = [...LOCKED_COLUMNS]; +export const RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS = [...LOCKED_COLUMNS.slice(1)]; +export const GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS = ['rule_name', ...LOCKED_COLUMNS]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx index 6236e9e2d3d27..0024fef8ac125 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx @@ -70,7 +70,7 @@ describe('home', () => { let home = mountWithIntl(); // Just rules/connectors - expect(home.find('.euiTab__content').length).toBe(2); + expect(home.find('.euiTab__content').length).toBe(3); (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => { if (feature === 'internalAlertsTable') { @@ -81,6 +81,6 @@ describe('home', () => { home = mountWithIntl(); // alerts now too! - expect(home.find('.euiTab__content').length).toBe(3); + expect(home.find('.euiTab__content').length).toBe(4); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 9110ebe1f51c8..8963e63eb44fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -11,7 +11,13 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiButtonEmpty, EuiPageHeader } from '@elastic/eui'; import { getIsExperimentalFeatureEnabled } from '../common/get_experimental_features'; -import { Section, routeToConnectors, routeToRules, routeToInternalAlerts } from './constants'; +import { + Section, + routeToConnectors, + routeToRules, + routeToInternalAlerts, + routeToLogs, +} from './constants'; import { getAlertingSectionBreadcrumb } from './lib/breadcrumb'; import { getCurrentDocTitle } from './lib/doc_title'; import { hasShowActionsCapability } from './lib/capabilities'; @@ -25,6 +31,7 @@ const ActionsConnectorsList = lazy( () => import('./sections/actions_connectors_list/components/actions_connectors_list') ); const RulesList = lazy(() => import('./sections/rules_list/components/rules_list')); +const LogsList = lazy(() => import('./sections/logs_list/components/logs_list')); const AlertsPage = lazy(() => import('./sections/alerts_table/alerts_page')); export interface MatchParams { @@ -71,6 +78,11 @@ export const TriggersActionsUIHome: React.FunctionComponent, + }); + if (isInternalAlertsTableEnabled) { tabs.push({ id: 'alerts', @@ -138,6 +150,11 @@ export const TriggersActionsUIHome: React.FunctionComponent + {canShowActions && ( ; + export const loadExecutionLogAggregations = async ({ id, http, @@ -106,3 +108,35 @@ export const loadExecutionLogAggregations = async ({ return rewriteBodyRes(result); }; + +export const loadGlobalExecutionLogAggregations = async ({ + http, + dateStart, + dateEnd, + outcomeFilter, + message, + perPage = 10, + page = 0, + sort = [], +}: LoadGlobalExecutionLogAggregationsProps & { http: HttpSetup }) => { + const sortField: any[] = sort; + const filter = getFilter({ outcomeFilter, message }); + + const result = await http.get>( + `${INTERNAL_BASE_ALERTING_API_PATH}/_global_execution_logs`, + { + query: { + date_start: dateStart, + date_end: dateEnd, + filter: filter.length ? filter.join(' and ') : undefined, + per_page: perPage, + // Need to add the + 1 for pages because APIs are 1 indexed, + // whereas data grid sorts are 0 indexed. + page: page + 1, + sort: sortField.length ? JSON.stringify(sortField) : undefined, + }, + } + ); + + return rewriteBodyRes(result); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx index f83cffae60c68..54e273c489100 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx @@ -36,7 +36,9 @@ import { alertingFrameworkHealth, resolveRule, loadExecutionLogAggregations, + loadGlobalExecutionLogAggregations, LoadExecutionLogAggregationsProps, + LoadGlobalExecutionLogAggregationsProps, loadActionErrorLog, LoadActionErrorLogProps, snoozeRule, @@ -70,6 +72,9 @@ export interface ComponentOpts { loadExecutionLogAggregations: ( props: LoadExecutionLogAggregationsProps ) => Promise; + loadGlobalExecutionLogAggregations: ( + props: LoadGlobalExecutionLogAggregationsProps + ) => Promise; loadActionErrorLog: (props: LoadActionErrorLogProps) => Promise; getHealth: () => Promise; resolveRule: (id: Rule['id']) => Promise; @@ -151,6 +156,14 @@ export function withBulkRuleOperations( http, }) } + loadGlobalExecutionLogAggregations={async ( + loadProps: LoadGlobalExecutionLogAggregationsProps + ) => + loadGlobalExecutionLogAggregations({ + ...loadProps, + http, + }) + } loadActionErrorLog={async (loadProps: LoadActionErrorLogProps) => loadActionErrorLog({ ...loadProps, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/logs_list/components/logs_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/logs_list/components/logs_list.tsx new file mode 100644 index 0000000000000..79e617ee05a49 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/logs_list/components/logs_list.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 { suspendedComponentWithProps } from '../../../lib/suspended_component_with_props'; +import { RuleEventLogListTableWithApi } from '../../rule_details/components/rule_event_log_list_table'; + +const GLOBAL_EVENT_LOG_LIST_STORAGE_KEY = + 'xpack.triggersActionsUI.globalEventLogList.initialColumns'; + +export const LogsList = () => { + return suspendedComponentWithProps( + RuleEventLogListTableWithApi, + 'xl' + )({ + ruleId: '*', + refreshToken: 0, + initialPageSize: 50, + hasRuleNames: true, + localStorageKey: GLOBAL_EVENT_LOG_LIST_STORAGE_KEY, + }); +}; + +// eslint-disable-next-line import/no-default-export +export default LogsList; 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 cac3aabad076b..5c44b9161b2c2 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 @@ -31,7 +31,7 @@ import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experime import { suspendedComponentWithProps } from '../../../lib/suspended_component_with_props'; import RuleStatusPanelWithApi from './rule_status_panel'; -const RuleEventLogListWithApi = lazy(() => import('./rule_event_log_list')); +const RuleEventLogList = lazy(() => import('./rule_event_log_list')); const RuleAlertList = lazy(() => import('./rule_alert_list')); const RuleDefinition = lazy(() => import('./rule_definition')); @@ -104,11 +104,11 @@ export function RuleComponent({ }), 'data-test-subj': 'eventLogListTab', content: suspendedComponentWithProps>( - RuleEventLogListWithApi, + RuleEventLogList, 'xl' )({ fetchRuleSummary: false, - rule, + ruleId: rule.id, ruleType, ruleSummary, numberOfExecutions, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.test.tsx index 5939209bdf591..dee90d8ceb9f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.test.tsx @@ -11,7 +11,6 @@ import { act } from 'react-dom/test-utils'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { RuleActionErrorLogFlyout } from './rule_action_error_log_flyout'; import { loadActionErrorLog } from '../../../lib/rule_api/load_action_error_log'; -import { Rule } from '../../../../types'; jest.mock('../../../lib/rule_api/load_action_error_log', () => ({ loadActionErrorLog: jest.fn(), @@ -43,33 +42,9 @@ const mockErrorLogResponse = { ], }; -const mockRule: Rule = { - id: uuid.v4(), - enabled: true, - name: `rule-${uuid.v4()}`, - tags: [], - ruleTypeId: '.noop', - consumer: 'consumer', - schedule: { interval: '1m' }, - actions: [], - params: {}, - createdBy: null, - updatedBy: null, - createdAt: new Date(), - updatedAt: new Date(), - apiKeyOwner: null, - throttle: null, - notifyWhen: null, - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'unknown', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - }, -}; - const mockExecution: any = { id: uuid.v4(), + rule_id: uuid.v4(), timestamp: '2022-03-20T07:40:44-07:00', duration: 5000000, status: 'success', @@ -98,7 +73,7 @@ describe('rule_action_error_log_flyout', () => { it('renders correctly', async () => { const wrapper = mountWithIntl( - + ); await act(async () => { @@ -115,7 +90,7 @@ describe('rule_action_error_log_flyout', () => { it('can close the flyout', async () => { const wrapper = mountWithIntl( - + ); await act(async () => { @@ -130,7 +105,7 @@ describe('rule_action_error_log_flyout', () => { it('switches between push and overlay flyout depending on the size of the screen', async () => { const wrapper = mountWithIntl( - + ); await act(async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.tsx index 3cf361008dbc4..8c46e3574560c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_action_error_log_flyout.tsx @@ -21,23 +21,21 @@ import { useEuiTheme, } from '@elastic/eui'; import { IExecutionLog } from '@kbn/alerting-plugin/common'; -import { Rule } from '../../../../types'; import { RuleErrorLogWithApi } from './rule_error_log'; import { RuleActionErrorBadge } from './rule_action_error_badge'; export interface RuleActionErrorLogFlyoutProps { - rule: Rule; runLog: IExecutionLog; refreshToken?: number; onClose: () => void; } export const RuleActionErrorLogFlyout = (props: RuleActionErrorLogFlyoutProps) => { - const { rule, runLog, refreshToken, onClose } = props; + const { runLog, refreshToken, onClose } = props; const { euiTheme } = useEuiTheme(); - const { id, message, num_errored_actions: totalErrors } = runLog; + const { id, rule_id: ruleId, message, num_errored_actions: totalErrors } = runLog; const isFlyoutPush = useIsWithinBreakpoints(['xl']); @@ -84,7 +82,7 @@ export const RuleActionErrorLogFlyout = (props: RuleActionErrorLogFlyoutProps) = }} />
- + diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.test.tsx index ac27cab92b1fa..4ce6b4be05ade 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.test.tsx @@ -141,7 +141,7 @@ describe('rule_error_log', () => { it('renders correctly', async () => { const nowMock = jest.spyOn(Date, 'now').mockReturnValue(0); const wrapper = mountWithIntl( - + ); // No data initially @@ -184,7 +184,7 @@ describe('rule_error_log', () => { const nowMock = jest.spyOn(Date, 'now').mockReturnValue(0); const wrapper = mountWithIntl( - + ); await act(async () => { @@ -233,7 +233,7 @@ describe('rule_error_log', () => { }); const wrapper = mountWithIntl( - + ); await act(async () => { @@ -280,7 +280,7 @@ describe('rule_error_log', () => { const nowMock = jest.spyOn(Date, 'now').mockReturnValue(0); const wrapper = mountWithIntl( - + ); await act(async () => { @@ -328,7 +328,7 @@ describe('rule_error_log', () => { it('does not show the refine search prompt normally', async () => { const wrapper = mountWithIntl( - + ); await act(async () => { @@ -346,7 +346,7 @@ describe('rule_error_log', () => { }); const wrapper = mountWithIntl( - + ); await act(async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx index aeed7c0f8261b..7c14b17f8d12b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx @@ -25,7 +25,6 @@ import { IExecutionErrors } from '@kbn/alerting-plugin/common'; import { useKibana } from '../../../../common/lib/kibana'; import { RefineSearchPrompt } from '../refine_search_prompt'; -import { Rule } from '../../../../types'; import { ComponentOpts as RuleApis, withBulkRuleOperations, @@ -61,14 +60,14 @@ const updateButtonProps = { const MAX_RESULTS = 1000; export type RuleErrorLogProps = { - rule: Rule; + ruleId: string; runId?: string; refreshToken?: number; requestRefresh?: () => Promise; } & Pick; export const RuleErrorLog = (props: RuleErrorLogProps) => { - const { rule, runId, loadActionErrorLog, refreshToken } = props; + const { ruleId, runId, loadActionErrorLog, refreshToken } = props; const { uiSettings, notifications } = useKibana().services; @@ -131,7 +130,7 @@ export const RuleErrorLog = (props: RuleErrorLogProps) => { setIsLoading(true); try { const result = await loadActionErrorLog({ - id: rule.id, + id: ruleId, runId, message: searchText, dateStart: getParsedDate(dateStart), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_data_grid.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_data_grid.tsx index 5145b1aa2cd65..0f6dcc13b1667 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_data_grid.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_data_grid.tsx @@ -59,6 +59,7 @@ export interface RuleEventLogDataGrid { dateFormat: string; pageSizeOptions?: number[]; selectedRunLog?: IExecutionLog; + showRuleNameAndIdColumns?: boolean; onChangeItemsPerPage: (pageSize: number) => void; onChangePage: (pageIndex: number) => void; onFilterChange: (filter: string[]) => void; @@ -160,6 +161,7 @@ export const RuleEventLogDataGrid = (props: RuleEventLogDataGrid) => { dateFormat, visibleColumns, selectedRunLog, + showRuleNameAndIdColumns = false, setVisibleColumns, setSortingColumns, onChangeItemsPerPage, @@ -180,6 +182,39 @@ export const RuleEventLogDataGrid = (props: RuleEventLogDataGrid) => { const columns: EuiDataGridColumn[] = useMemo( () => [ + ...(showRuleNameAndIdColumns + ? [ + { + id: 'rule_id', + displayAsText: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.ruleId', + { + defaultMessage: 'Rule Id', + } + ), + isSortable: getIsColumnSortable('rule_id'), + actions: { + showSortAsc: false, + showSortDesc: false, + }, + }, + { + id: 'rule_name', + displayAsText: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.ruleName', + { + defaultMessage: 'Rule', + } + ), + isSortable: getIsColumnSortable('rule_name'), + actions: { + showSortAsc: false, + showSortDesc: false, + showHide: false, + }, + }, + ] + : []), { id: 'id', displayAsText: i18n.translate( @@ -394,7 +429,7 @@ export const RuleEventLogDataGrid = (props: RuleEventLogDataGrid) => { isSortable: getIsColumnSortable('timed_out'), }, ], - [getPaginatedRowIndex, onFlyoutOpen, onFilterChange, logs] + [getPaginatedRowIndex, onFlyoutOpen, onFilterChange, showRuleNameAndIdColumns, logs] ); const columnVisibilityProps = useMemo( @@ -524,6 +559,7 @@ export const RuleEventLogDataGrid = (props: RuleEventLogDataGrid) => { const value = logs[pagedRowIndex]?.[columnId as keyof IExecutionLog] as string; const actionErrors = logs[pagedRowIndex]?.num_errored_actions || (0 as number); const version = logs?.[pagedRowIndex]?.version; + const ruleId = runLog?.rule_id; if (columnId === 'num_errored_actions' && runLog) { return ( @@ -555,6 +591,7 @@ export const RuleEventLogDataGrid = (props: RuleEventLogDataGrid) => { value={value} version={version} dateFormat={dateFormat} + ruleId={ruleId} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.test.tsx index 826c208b66498..541cf94d1d539 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.test.tsx @@ -15,7 +15,10 @@ import { EuiSuperDatePicker, EuiDataGrid } from '@elastic/eui'; import { RuleEventLogListStatusFilter } from './rule_event_log_list_status_filter'; import { RuleEventLogList } from './rule_event_log_list'; import { RefineSearchPrompt } from '../refine_search_prompt'; -import { RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS } from '../../../constants'; +import { + RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS, + GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS, +} from '../../../constants'; import { mockRule, mockRuleType, mockRuleSummary } from './test_helpers'; import { RuleType } from '../../../../types'; import { loadActionErrorLog } from '../../../lib/rule_api/load_action_error_log'; @@ -161,7 +164,7 @@ describe.skip('rule_event_log_list', () => { it('renders correctly', async () => { const wrapper = mountWithIntl( { it('can sort by single and/or multiple column(s)', async () => { const wrapper = mountWithIntl( { it('can filter by execution log outcome status', async () => { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { it('can save display columns to localStorage', async () => { const wrapper = mountWithIntl( { JSON.parse( localStorage.getItem('xpack.triggersActionsUI.ruleEventLogList.initialColumns') ?? 'null' ) - ).toEqual(RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS); + ).toEqual(GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS); wrapper.find('[data-test-subj="dataGridColumnSelectorButton"] button').simulate('click'); @@ -535,7 +538,7 @@ describe.skip('rule_event_log_list', () => { it('does not show the refine search prompt normally', async () => { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { - if (date.includes('now')) { - return datemath.parse(date)?.format() || date; - } - return date; -}; - -const API_FAILED_MESSAGE = i18n.translate( - 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.apiError', - { - defaultMessage: 'Failed to fetch execution history', - } -); - -const SEARCH_PLACEHOLDER = i18n.translate( - 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.searchPlaceholder', - { - defaultMessage: 'Search event log message', - } -); +import { RuleSummary, RuleType } from '../../../../types'; +import { ComponentOpts as RuleApis } from '../../common/components/with_bulk_rule_api_operations'; +import { RuleEventLogListTableWithApi } from './rule_event_log_list_table'; const RULE_EVENT_LOG_LIST_STORAGE_KEY = 'xpack.triggersActionsUI.ruleEventLogList.initialColumns'; -const getDefaultColumns = (columns: string[]) => { - const columnsWithoutLockedColumn = columns.filter((column) => !LOCKED_COLUMNS.includes(column)); - return [...LOCKED_COLUMNS, ...columnsWithoutLockedColumn]; -}; - -const updateButtonProps = { - iconOnly: true, - fill: false, -}; - -const MAX_RESULTS = 1000; - const ruleEventListContainerStyle = { minHeight: 400 }; export type RuleEventLogListOptions = 'stackManagement' | 'default'; export interface RuleEventLogListCommonProps { - rule: Rule; + ruleId: string; ruleType: RuleType; localStorageKey?: string; refreshToken?: number; requestRefresh?: () => Promise; loadExecutionLogAggregations?: RuleApis['loadExecutionLogAggregations']; fetchRuleSummary?: boolean; + hideChart?: boolean; } export interface RuleEventLogListStackManagementProps { @@ -103,7 +48,7 @@ export const RuleEventLogList = ( props: RuleEventLogListProps ) => { const { - rule, + ruleId, ruleType, localStorageKey = RULE_EVENT_LOG_LIST_STORAGE_KEY, refreshToken, @@ -118,228 +63,11 @@ export const RuleEventLogList = ( onChangeDuration, isLoadingRuleSummary = false, } = props as RuleEventLogListStackManagementProps; - - const { uiSettings, notifications } = useKibana().services; - - const [searchText, setSearchText] = useState(''); - const [search, setSearch] = useState(''); - const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); - const [selectedRunLog, setSelectedRunLog] = useState(); - - // Data grid states - const [logs, setLogs] = useState(); - const [visibleColumns, setVisibleColumns] = useState(() => { - return getDefaultColumns( - JSON.parse(localStorage.getItem(localStorageKey) ?? 'null') || - RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS - ); - }); - const [sortingColumns, setSortingColumns] = useState([]); - const [filter, setFilter] = useState([]); - const [actualTotalItemCount, setActualTotalItemCount] = useState(0); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 10, - totalItemCount: 0, - }); - - // Date related states - const [isLoading, setIsLoading] = useState(false); - const [dateStart, setDateStart] = useState('now-24h'); - const [dateEnd, setDateEnd] = useState('now'); - const [dateFormat] = useState(() => uiSettings?.get('dateFormat')); - const [commonlyUsedRanges] = useState(() => { - return ( - uiSettings - ?.get('timepicker:quickRanges') - ?.map(({ from, to, display }: { from: string; to: string; display: string }) => ({ - start: from, - end: to, - label: display, - })) || [] - ); - }); - - const isInitialized = useRef(false); - - const isOnLastPage = useMemo(() => { - const { pageIndex, pageSize } = pagination; - return (pageIndex + 1) * pageSize >= MAX_RESULTS; - }, [pagination]); - - // Formats the sort columns to be consumed by the API endpoint - const formattedSort = useMemo(() => { - return sortingColumns.map(({ id: sortId, direction }) => ({ - [sortId]: { - order: direction, - }, - })); - }, [sortingColumns]); - - const loadEventLogs = async () => { - if (!loadExecutionLogAggregations) { - return; - } - setIsLoading(true); - try { - const result = await loadExecutionLogAggregations({ - id: rule.id, - sort: formattedSort as LoadExecutionLogAggregationsProps['sort'], - outcomeFilter: filter, - message: searchText, - dateStart: getParsedDate(dateStart), - dateEnd: getParsedDate(dateEnd), - page: pagination.pageIndex, - perPage: pagination.pageSize, - }); - setLogs(result.data); - setPagination({ - ...pagination, - totalItemCount: Math.min(result.total, MAX_RESULTS), - }); - setActualTotalItemCount(result.total); - } catch (e) { - notifications.toasts.addDanger({ - title: API_FAILED_MESSAGE, - text: e.body.message, - }); - } - setIsLoading(false); - }; - - const onChangeItemsPerPage = useCallback( - (pageSize: number) => { - setPagination((prevPagination) => ({ - ...prevPagination, - pageIndex: 0, - pageSize, - })); - }, - [setPagination] - ); - - const onChangePage = useCallback( - (pageIndex: number) => { - setPagination((prevPagination) => ({ - ...prevPagination, - pageIndex, - })); - }, - [setPagination] - ); - - const onTimeChange = useCallback( - ({ start, end, isInvalid }: OnTimeChangeProps) => { - if (isInvalid) { - return; - } - setDateStart(start); - setDateEnd(end); - }, - [setDateStart, setDateEnd] - ); - - const onRefresh = () => { - loadEventLogs(); - }; - - const onFilterChange = useCallback( - (newFilter: string[]) => { - setPagination((prevPagination) => ({ - ...prevPagination, - pageIndex: 0, - })); - setFilter(newFilter); - }, - [setPagination, setFilter] - ); - - const onFlyoutOpen = useCallback((runLog: IExecutionLog) => { - setIsFlyoutOpen(true); - setSelectedRunLog(runLog); - }, []); - - const onFlyoutClose = useCallback(() => { - setIsFlyoutOpen(false); - setSelectedRunLog(undefined); - }, []); - - const onSearchChange = useCallback( - (e) => { - if (e.target.value === '') { - setSearchText(''); - } - setSearch(e.target.value); - }, - [setSearchText, setSearch] - ); - - const onKeyUp = useCallback( - (e) => { - if (e.key === 'Enter') { - setSearchText(search); - } - }, - [search, setSearchText] - ); - - const renderList = () => { - if (!logs) { - return ; - } - return ( - <> - {isLoading && ( - - )} - - - ); - }; - - useEffect(() => { - loadEventLogs(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - sortingColumns, - dateStart, - dateEnd, - filter, - pagination.pageIndex, - pagination.pageSize, - searchText, - ]); - - useEffect(() => { - if (isInitialized.current) { - loadEventLogs(); - } - isInitialized.current = true; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [refreshToken]); - - useEffect(() => { - localStorage.setItem(localStorageKey, JSON.stringify(visibleColumns)); - }, [localStorageKey, visibleColumns]); - return (
( requestRefresh={requestRefresh} fetchRuleSummary={fetchRuleSummary} /> - - - - - - - - - - - - - {renderList()} - {isOnLastPage && ( - - )} - {isFlyoutOpen && selectedRunLog && ( - - )} +
); }; -export const RuleEventLogListWithApi = withBulkRuleOperations(RuleEventLogList); - // eslint-disable-next-line import/no-default-export -export { RuleEventLogListWithApi as default }; +export { RuleEventLogList as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_cell_renderer.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_cell_renderer.tsx index 78137d56d70fc..ebab3489eb737 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_cell_renderer.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_cell_renderer.tsx @@ -5,9 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import moment from 'moment'; import type { EcsEventOutcome } from '@kbn/core/server'; +import { EuiLink } from '@elastic/eui'; +import { useHistory } from 'react-router-dom'; +import { routeToRuleDetails } from '../../../constants'; import { formatRuleAlertCount } from '../../../../common/lib/format_rule_alert_count'; import { RuleEventLogListStatus } from './rule_event_log_list_status'; import { RuleDurationFormat } from '../../rules_list/components/rule_duration_format'; @@ -26,10 +29,17 @@ interface RuleEventLogListCellRendererProps { version?: string; value?: string; dateFormat?: string; + ruleId?: string; } export const RuleEventLogListCellRenderer = (props: RuleEventLogListCellRendererProps) => { - const { columnId, value, version, dateFormat = DEFAULT_DATE_FORMAT } = props; + const { columnId, value, version, dateFormat = DEFAULT_DATE_FORMAT, ruleId } = props; + const history = useHistory(); + + const onClickRuleName = useCallback( + () => ruleId && history.push(routeToRuleDetails.replace(':ruleId', ruleId)), + [ruleId, history] + ); if (typeof value === 'undefined') { return null; @@ -43,6 +53,10 @@ export const RuleEventLogListCellRenderer = (props: RuleEventLogListCellRenderer return <>{moment(value).format(dateFormat)}; } + if (columnId === 'rule_name' && ruleId) { + return {value}; + } + if (RULE_EXECUTION_LOG_ALERT_COUNT_COLUMNS.includes(columnId)) { return <>{formatRuleAlertCount(value, version)}; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx new file mode 100644 index 0000000000000..b647442a8eaf0 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx @@ -0,0 +1,396 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, useMemo, useRef } from 'react'; +import { i18n } from '@kbn/i18n'; +import datemath from '@kbn/datemath'; +import { + EuiFieldSearch, + EuiFlexItem, + EuiFlexGroup, + EuiProgress, + EuiSpacer, + EuiDataGridSorting, + Pagination, + EuiSuperDatePicker, + OnTimeChangeProps, +} from '@elastic/eui'; +import { IExecutionLog } from '@kbn/alerting-plugin/common'; +import { useKibana } from '../../../../common/lib/kibana'; +import { + RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS, + GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS, + LOCKED_COLUMNS, +} from '../../../constants'; +import { RuleEventLogListStatusFilter } from './rule_event_log_list_status_filter'; +import { RuleEventLogDataGrid } from './rule_event_log_data_grid'; +import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; +import { RuleActionErrorLogFlyout } from './rule_action_error_log_flyout'; + +import { RefineSearchPrompt } from '../refine_search_prompt'; +import { LoadExecutionLogAggregationsProps } from '../../../lib/rule_api'; +import { + ComponentOpts as RuleApis, + withBulkRuleOperations, +} from '../../common/components/with_bulk_rule_api_operations'; + +const getParsedDate = (date: string) => { + if (date.includes('now')) { + return datemath.parse(date)?.format() || date; + } + return date; +}; + +const API_FAILED_MESSAGE = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.apiError', + { + defaultMessage: 'Failed to fetch execution history', + } +); + +const SEARCH_PLACEHOLDER = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.searchPlaceholder', + { + defaultMessage: 'Search event log message', + } +); + +const RULE_EVENT_LOG_LIST_STORAGE_KEY = 'xpack.triggersActionsUI.ruleEventLogList.initialColumns'; + +const getDefaultColumns = (columns: string[]) => { + const columnsWithoutLockedColumn = columns.filter((column) => !LOCKED_COLUMNS.includes(column)); + return [...LOCKED_COLUMNS, ...columnsWithoutLockedColumn]; +}; + +const updateButtonProps = { + iconOnly: true, + fill: false, +}; + +const MAX_RESULTS = 1000; + +export type RuleEventLogListOptions = 'stackManagement' | 'default'; + +export type RuleEventLogListCommonProps = { + ruleId: string; + localStorageKey?: string; + refreshToken?: number; + initialPageSize?: number; + // Duplicating these properties is extremely silly but it's the only way to get Jest to cooperate with the way this component is structured + overrideLoadExecutionLogAggregations?: RuleApis['loadExecutionLogAggregations']; + overrideLoadGlobalExecutionLogAggregations?: RuleApis['loadGlobalExecutionLogAggregations']; + hasRuleNames?: boolean; +} & Pick; + +export type RuleEventLogListTableProps = + T extends 'default' + ? RuleEventLogListCommonProps + : T extends 'stackManagement' + ? RuleEventLogListCommonProps + : never; + +export const RuleEventLogListTable = ( + props: RuleEventLogListTableProps +) => { + const { + ruleId, + localStorageKey = RULE_EVENT_LOG_LIST_STORAGE_KEY, + refreshToken, + loadGlobalExecutionLogAggregations, + loadExecutionLogAggregations, + overrideLoadGlobalExecutionLogAggregations, + overrideLoadExecutionLogAggregations, + initialPageSize = 10, + hasRuleNames = false, + } = props; + + const { uiSettings, notifications } = useKibana().services; + + const [searchText, setSearchText] = useState(''); + const [search, setSearch] = useState(''); + const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const [selectedRunLog, setSelectedRunLog] = useState(); + + // Data grid states + const [logs, setLogs] = useState(); + const [visibleColumns, setVisibleColumns] = useState(() => { + return getDefaultColumns( + JSON.parse(localStorage.getItem(localStorageKey) ?? 'null') || hasRuleNames + ? GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS + : RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS + ); + }); + const [sortingColumns, setSortingColumns] = useState([]); + const [filter, setFilter] = useState([]); + const [actualTotalItemCount, setActualTotalItemCount] = useState(0); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: initialPageSize, + totalItemCount: 0, + }); + + // Date related states + const [isLoading, setIsLoading] = useState(false); + const [dateStart, setDateStart] = useState('now-24h'); + const [dateEnd, setDateEnd] = useState('now'); + const [dateFormat] = useState(() => uiSettings?.get('dateFormat')); + const [commonlyUsedRanges] = useState(() => { + return ( + uiSettings + ?.get('timepicker:quickRanges') + ?.map(({ from, to, display }: { from: string; to: string; display: string }) => ({ + start: from, + end: to, + label: display, + })) || [] + ); + }); + + const isInitialized = useRef(false); + + const isOnLastPage = useMemo(() => { + const { pageIndex, pageSize } = pagination; + return (pageIndex + 1) * pageSize >= MAX_RESULTS; + }, [pagination]); + + // Formats the sort columns to be consumed by the API endpoint + const formattedSort = useMemo(() => { + return sortingColumns.map(({ id: sortId, direction }) => ({ + [sortId]: { + order: direction, + }, + })); + }, [sortingColumns]); + + const loadLogsFn = useMemo(() => { + if (ruleId === '*') { + return overrideLoadGlobalExecutionLogAggregations ?? loadGlobalExecutionLogAggregations; + } + return overrideLoadExecutionLogAggregations ?? loadExecutionLogAggregations; + }, [ + ruleId, + overrideLoadExecutionLogAggregations, + overrideLoadGlobalExecutionLogAggregations, + loadExecutionLogAggregations, + loadGlobalExecutionLogAggregations, + ]); + + const loadEventLogs = async () => { + if (!loadLogsFn) { + return; + } + setIsLoading(true); + try { + const result = await loadLogsFn({ + id: ruleId, + sort: formattedSort as LoadExecutionLogAggregationsProps['sort'], + outcomeFilter: filter, + message: searchText, + dateStart: getParsedDate(dateStart), + dateEnd: getParsedDate(dateEnd), + page: pagination.pageIndex, + perPage: pagination.pageSize, + }); + setLogs(result.data); + setPagination({ + ...pagination, + totalItemCount: Math.min(result.total, MAX_RESULTS), + }); + setActualTotalItemCount(result.total); + } catch (e) { + notifications.toasts.addDanger({ + title: API_FAILED_MESSAGE, + text: e.body?.message ?? e, + }); + } + setIsLoading(false); + }; + + const onChangeItemsPerPage = useCallback( + (pageSize: number) => { + setPagination((prevPagination) => ({ + ...prevPagination, + pageIndex: 0, + pageSize, + })); + }, + [setPagination] + ); + + const onChangePage = useCallback( + (pageIndex: number) => { + setPagination((prevPagination) => ({ + ...prevPagination, + pageIndex, + })); + }, + [setPagination] + ); + + const onTimeChange = useCallback( + ({ start, end, isInvalid }: OnTimeChangeProps) => { + if (isInvalid) { + return; + } + setDateStart(start); + setDateEnd(end); + }, + [setDateStart, setDateEnd] + ); + + const onRefresh = () => { + loadEventLogs(); + }; + + const onFilterChange = useCallback( + (newFilter: string[]) => { + setPagination((prevPagination) => ({ + ...prevPagination, + pageIndex: 0, + })); + setFilter(newFilter); + }, + [setPagination, setFilter] + ); + + const onFlyoutOpen = useCallback((runLog: IExecutionLog) => { + setIsFlyoutOpen(true); + setSelectedRunLog(runLog); + }, []); + + const onFlyoutClose = useCallback(() => { + setIsFlyoutOpen(false); + setSelectedRunLog(undefined); + }, []); + + const onSearchChange = useCallback( + (e) => { + if (e.target.value === '') { + setSearchText(''); + } + setSearch(e.target.value); + }, + [setSearchText, setSearch] + ); + + const onKeyUp = useCallback( + (e) => { + if (e.key === 'Enter') { + setSearchText(search); + } + }, + [search, setSearchText] + ); + + const renderList = () => { + if (!logs) { + return ; + } + return ( + <> + {isLoading && ( + + )} + + + ); + }; + + useEffect(() => { + loadEventLogs(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + sortingColumns, + dateStart, + dateEnd, + filter, + pagination.pageIndex, + pagination.pageSize, + searchText, + ]); + + useEffect(() => { + if (isInitialized.current) { + loadEventLogs(); + } + isInitialized.current = true; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [refreshToken]); + + useEffect(() => { + localStorage.setItem(localStorageKey, JSON.stringify(visibleColumns)); + }, [localStorageKey, visibleColumns]); + + return ( + <> + + + + + + + + + + + + + {renderList()} + {isOnLastPage && ( + + )} + {isFlyoutOpen && selectedRunLog && ( + + )} + + ); +}; + +export const RuleEventLogListTableWithApi = withBulkRuleOperations(RuleEventLogListTable); + +// eslint-disable-next-line import/no-default-export +export { RuleEventLogListTableWithApi as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_execution_summary_and_chart.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_execution_summary_and_chart.test.tsx index ff0ffcc3d67f1..c9626bad07dd6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_execution_summary_and_chart.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_execution_summary_and_chart.test.tsx @@ -44,7 +44,7 @@ describe('rule_execution_summary_and_chart', () => { it('becomes a stateless component when "fetchRuleSummary" is false', async () => { const wrapper = mountWithIntl( { it('becomes a container component when "fetchRuleSummary" is true', async () => { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { const { - rule, + ruleId, ruleType, ruleSummary, refreshToken, @@ -103,7 +103,7 @@ export const RuleExecutionSummaryAndChart = (props: RuleExecutionSummaryAndChart } setInternalIsLoadingRuleSummary(true); try { - const loadedSummary = await loadRuleSummary(rule.id, computedNumberOfExecutions); + const loadedSummary = await loadRuleSummary(ruleId, computedNumberOfExecutions); setInternalRuleSummary(loadedSummary); } catch (e) { toasts.addDanger({ @@ -124,7 +124,7 @@ export const RuleExecutionSummaryAndChart = (props: RuleExecutionSummaryAndChart useEffect(() => { getRuleSummary(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [rule, computedNumberOfExecutions]); + }, [ruleId, computedNumberOfExecutions]); useEffect(() => { if (isInitialized.current) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/global_execution_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/global_execution_log.ts new file mode 100644 index 0000000000000..b5dcb9451e854 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/global_execution_log.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +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 globalExecutionLogTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + const retry = getService('retry'); + + describe('globalExecutionLog', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + it('should return logs only from the current space', async () => { + const startTime = new Date().toISOString(); + + const spaceId = UserAtSpaceScenarios[1].space.id; + const user = UserAtSpaceScenarios[1].user; + const response = await supertest + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(spaceId, alertId, 'rule', 'alerting'); + + const spaceId2 = UserAtSpaceScenarios[4].space.id; + const response2 = await supertest + .post(`${getUrlPrefix(spaceId2)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response2.status).to.eql(200); + const alertId2 = response2.body.id; + objectRemover.add(spaceId2, alertId2, 'rule', 'alerting'); + + const logs = await retry.try(async () => { + // there can be a successful execute before the error one + const logResponse = await supertestWithoutAuth + .get( + `${getUrlPrefix( + spaceId + )}/internal/alerting/_global_execution_logs?date_start=${startTime}&date_end=9999-12-31T23:59:59Z&per_page=50&page=1` + ) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + expect(logResponse.statusCode).to.be(200); + + return logResponse.body.data; + }); + + // Filter out any excess logs from rules not created by this test + const sanitizedLogs = logs.filter((l: any) => [alertId, alertId2].includes(l.rule_id)); + const allLogsSpace0 = sanitizedLogs.every((l: any) => l.rule_id === alertId); + expect(allLogsSpace0).to.be(true); + }); + }); +} 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 index 72890c2bbd90a..a87a614e9662a 100644 --- 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 @@ -44,6 +44,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./health')); loadTestFile(require.resolve('./excluded')); loadTestFile(require.resolve('./snooze')); + loadTestFile(require.resolve('./global_execution_log')); }); }); } diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index edb421583e0d6..56c546e1349cd 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -24,7 +24,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./uptime')); loadTestFile(require.resolve('./maps')); loadTestFile(require.resolve('./security_solution')); - loadTestFile(require.resolve('./lens')); loadTestFile(require.resolve('./transform')); loadTestFile(require.resolve('./lists')); loadTestFile(require.resolve('./upgrade_assistant')); diff --git a/x-pack/test/api_integration/apis/lens/index.ts b/x-pack/test/api_integration/apis/lens/index.ts deleted file mode 100644 index 90774a2db35ed..0000000000000 --- a/x-pack/test/api_integration/apis/lens/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 lensApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('Lens', () => { - loadTestFile(require.resolve('./existing_fields')); - loadTestFile(require.resolve('./legacy_existing_fields')); - }); -} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rule_exception_references.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rule_exception_references.ts new file mode 100644 index 0000000000000..e75a35d88acc3 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rule_exception_references.ts @@ -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 expect from '@kbn/expect'; + +import { DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + CreateExceptionListSchema, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; +import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createRule, + getSimpleRule, + createSignalsIndex, + deleteSignalsIndex, + deleteAllAlerts, + createExceptionList, +} from '../../utils'; +import { deleteAllExceptions } from '../../../lists_api_integration/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + + describe('find_rule_exception_references', () => { + before(async () => { + await createSignalsIndex(supertest, log); + }); + + after(async () => { + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + + afterEach(async () => { + await deleteAllExceptions(supertest, log); + }); + + it('returns empty array per list_id if no references are found', async () => { + // create exception list + const newExceptionList: CreateExceptionListSchema = { + ...getCreateExceptionListMinimalSchemaMock(), + list_id: 'i_exist', + namespace_type: 'single', + type: ExceptionListTypeEnum.DETECTION, + }; + const exceptionList = await createExceptionList(supertest, log, newExceptionList); + + // create rule + await createRule(supertest, log, getSimpleRule('rule-1')); + + const { body: references } = await supertest + .get(DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL) + .set('kbn-xsrf', 'true') + .query({ + ids: `${exceptionList.id}`, + list_ids: `${exceptionList.list_id}`, + namespace_types: `${exceptionList.namespace_type}`, + }) + .expect(200); + + expect(references).to.eql({ references: [{ i_exist: [] }] }); + }); + + it('returns empty array per list_id if list does not exist', async () => { + // create rule + await createRule(supertest, log, getSimpleRule('rule-1')); + + const { body: references } = await supertest + .get(DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL) + .set('kbn-xsrf', 'true') + .query({ + ids: `1234`, + list_ids: `i_dont_exist`, + namespace_types: `single`, + }) + .expect(200); + + expect(references).to.eql({ references: [{ i_dont_exist: [] }] }); + }); + + it('returns found references', async () => { + // create exception list + const newExceptionList: CreateExceptionListSchema = { + ...getCreateExceptionListMinimalSchemaMock(), + list_id: 'i_exist', + namespace_type: 'single', + type: ExceptionListTypeEnum.DETECTION, + }; + const exceptionList = await createExceptionList(supertest, log, newExceptionList); + const exceptionList2 = await createExceptionList(supertest, log, { + ...newExceptionList, + list_id: 'i_exist_2', + }); + + // create rule + const rule = await createRule(supertest, log, { + ...getSimpleRule('rule-2'), + exceptions_list: [ + { + id: `${exceptionList.id}`, + list_id: `${exceptionList.list_id}`, + namespace_type: `${exceptionList.namespace_type}`, + type: `${exceptionList.type}`, + }, + { + id: `${exceptionList2.id}`, + list_id: `${exceptionList2.list_id}`, + namespace_type: `${exceptionList2.namespace_type}`, + type: `${exceptionList2.type}`, + }, + ], + }); + + const { body: references } = await supertest + .get(DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL) + .set('kbn-xsrf', 'true') + .query({ + ids: `${exceptionList.id},${exceptionList2.id}`, + list_ids: `${exceptionList.list_id},${exceptionList2.list_id}`, + namespace_types: `${exceptionList.namespace_type},${exceptionList2.namespace_type}`, + }) + .expect(200); + + expect(references).to.eql({ + references: [ + { + i_exist: [ + { + exception_lists: [ + { + id: references.references[0].i_exist[0].exception_lists[0].id, + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: references.references[0].i_exist[0].exception_lists[1].id, + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + id: rule.id, + name: 'Simple Rule Query', + rule_id: 'rule-2', + }, + ], + }, + { + i_exist_2: [ + { + exception_lists: [ + { + id: references.references[1].i_exist_2[0].exception_lists[0].id, + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: references.references[1].i_exist_2[0].exception_lists[1].id, + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + id: rule.id, + name: 'Simple Rule Query', + rule_id: 'rule-2', + }, + ], + }, + ], + }); + }); + }); +}; 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 index 283456df6b1f0..3064d412da1bd 100644 --- 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 @@ -31,30 +31,8 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./delete_rules_bulk')); loadTestFile(require.resolve('./export_rules')); loadTestFile(require.resolve('./find_rules')); + loadTestFile(require.resolve('./find_rule_exception_references')); loadTestFile(require.resolve('./generating_signals')); loadTestFile(require.resolve('./get_prepackaged_rules_status')); - loadTestFile(require.resolve('./get_rule_execution_results')); - 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('./perform_bulk_action_dry_run')); - 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/group10/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/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/group1/create_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/delete_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/delete_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/finalize_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/finalize_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/finalize_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/finalize_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_rule_execution_results.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/get_rule_execution_results.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_rule_execution_results.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/get_rule_execution_results.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_signals_migration_status.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/get_signals_migration_status.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_signals_migration_status.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/get_signals_migration_status.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/ignore_fields.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/ignore_fields.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/ignore_fields.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/ignore_fields.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_export_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts new file mode 100644 index 0000000000000..4449e9ca07800 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.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 { 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 10', 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('./get_rule_execution_results')); + 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('./perform_bulk_action_dry_run')); + 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/group10/legacy_actions_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/legacy_actions_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/open_close_signals.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/open_close_signals.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/open_close_signals.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action_dry_run.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action_dry_run.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action_dry_run.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_privileges.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_privileges.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_privileges.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_privileges.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/resolve_read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/resolve_read_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/runtime.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/runtime.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/runtime.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/runtime.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/template_data/execution_events.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/template_data/execution_events.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/template_data/execution_events.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/template_data/execution_events.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/throttle.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/throttle.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/timestamps.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/timestamps.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts index aa1c397c1305a..10b1b709d188f 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts @@ -445,6 +445,111 @@ export default function (providerContext: FtrProviderContext) { expect(policy.name).to.equal(nameWithWhitespace.trim()); }); + + describe('Simplified package policy', () => { + it('should work with valid values', async () => { + await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `create-simplified-package-policy-required-variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'with_required_variables.log': { + vars: { test_var_required: 'I am required' }, + }, + }, + }, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(200); + }); + + it('should throw with invalid variables', async () => { + const { body } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `create-simplified-package-policy-required-variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'with_required_variables.log': { + vars: { var_id_do_exists: 'I do not exists' }, + }, + }, + }, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(400); + + expect(body.message).eql( + 'Variable with_required_variables-test_input with_required_variables.log:var_id_do_exists not found' + ); + }); + + it('should throw with invalid inputs', async () => { + await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `create-simplified-package-policy-required-variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'i-do-not-exists-input': {}, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(400); + }); + + it('should throw with invalid streams', async () => { + await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `create-simplified-package-policy-required-variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'iamnotexisting.log': { + vars: { test_var_required: 'I am required' }, + }, + }, + }, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(400); + }); + }); + describe('Package verification', () => { const uninstallPackage = async (pkg: string, version: string) => { await supertest.delete(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx'); diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts index 4c5320f037dd8..8d565728c2d4c 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts @@ -30,6 +30,7 @@ export default function (providerContext: FtrProviderContext) { let managedAgentPolicyId: string; let packagePolicyId: string; let packagePolicyId2: string; + let packagePolicyId3: string; before(async () => { await kibanaServer.savedObjects.cleanStandardList(); await getService('esArchiver').load( @@ -107,6 +108,30 @@ export default function (providerContext: FtrProviderContext) { }, }); packagePolicyId2 = packagePolicyResponse2.item.id; + + const { body: packagePolicyResponse3 } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'update-package-policy-with_required_variables-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'with_required_variables.log': { + vars: { test_var_required: 'I am required' }, + }, + }, + }, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }); + packagePolicyId3 = packagePolicyResponse3.item.id; }); after(async function () { @@ -268,5 +293,54 @@ export default function (providerContext: FtrProviderContext) { }, }); }); + + describe('Simplified package policy', async () => { + it('should work with valid values', async function () { + await supertest + .put(`/api/fleet/package_policies/${packagePolicyId3}`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `update-simplified-package-policy-with_required_variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'with_required_variables.log': { + vars: { test_var_required: 'I am required' }, + }, + }, + }, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(200); + }); + + it('should return a 400 with invalid inputs', async function () { + const { body } = await supertest + .put(`/api/fleet/package_policies/${packagePolicyId3}`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `update-simplified-package-policy-with_required_variables-${Date.now()}`, + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-i-do-not-exists': {}, + }, + package: { + name: 'with_required_variables', + version: '0.1.0', + }, + }) + .expect(400); + expect(body.message).eql('Input not found: with_required_variables-i-do-not-exists'); + }); + }); }); } 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 a48bc7651a1ed..831f0475c2c11 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 @@ -35,7 +35,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); }); - // https://github.com/elastic/kibana/issues/132628 describe('global all privileges', () => { before(async () => { await security.role.create('global_all_role', { diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index d6197cc2e39ab..e139677737b42 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -76,7 +76,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ) ).to.be(true); await PageObjects.lens.closeDimensionEditor(); - await testSubjects.existOrFail('xyVisAnnotationText'); await testSubjects.existOrFail('xyVisGroupedAnnotationIcon'); }); }); diff --git a/x-pack/test/functional/services/cases/navigation.ts b/x-pack/test/functional/services/cases/navigation.ts index a54be7896877e..8d3ba0e73a24c 100644 --- a/x-pack/test/functional/services/cases/navigation.ts +++ b/x-pack/test/functional/services/cases/navigation.ts @@ -14,7 +14,7 @@ export function CasesNavigationProvider({ getPageObject, getService }: FtrProvid return { async navigateToApp(app: string = 'cases', appSelector: string = 'cases-app') { await common.navigateToApp(app); - await testSubjects.existOrFail(appSelector, { timeout: 2000 }); + await testSubjects.existOrFail(appSelector); }, async navigateToConfigurationPage(app: string = 'cases', appSelector: string = 'cases-app') { diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index 03b0d961e1d4c..c8d43207dd5ab 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -284,7 +284,7 @@ export function TrainedModelsTableProvider( } public async openStartDeploymentModal(modelId: string) { - await testSubjects.clickWhenNotDisabledWithoutRetry( + await testSubjects.clickWhenNotDisabled( this.rowSelector(modelId, 'mlModelsTableRowStartDeploymentAction'), { timeout: 5000 } ); @@ -292,7 +292,7 @@ export function TrainedModelsTableProvider( } public async clickStopDeploymentAction(modelId: string) { - await testSubjects.clickWhenNotDisabledWithoutRetry( + await testSubjects.clickWhenNotDisabled( this.rowSelector(modelId, 'mlModelsTableRowStopDeploymentAction'), { timeout: 5000 } ); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts index bb65b1c9c4933..909930d713473 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts @@ -9,8 +9,14 @@ import expect from '@kbn/expect'; import { EXCEPTION_LIST_URL, EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getExceptionListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; -import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { + getCreateExceptionListItemMinimalSchemaMock, + getCreateExceptionListItemMinimalSchemaMockWithoutId, +} from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { + getCreateExceptionListMinimalSchemaMock, + getCreateExceptionListDetectionSchemaMock, +} from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } from '../../utils'; @@ -51,6 +57,79 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('should return matching items when search is passed in', async () => { + // create exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + // create exception list items + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMockWithoutId(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + item_id: '1', + entries: [ + { field: 'host.name', value: 'some host', operator: 'included', type: 'match' }, + ], + }) + .expect(200); + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMockWithoutId(), + item_id: '2', + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + entries: [{ field: 'foo', operator: 'included', type: 'exists' }], + }) + .expect(200); + + const { body } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${ + getCreateExceptionListMinimalSchemaMock().list_id + }&namespace_type=single&page=1&per_page=25&search=host&sort_field=exception-list.created_at&sort_order=desc` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + body.data = [removeExceptionListItemServerGeneratedProperties(body.data[0])]; + expect(body).to.eql({ + data: [ + { + comments: [], + created_by: 'elastic', + description: 'some description', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match', + value: 'some host', + }, + ], + item_id: '1', + list_id: 'some-list-id', + name: 'some name', + namespace_type: 'single', + os_types: ['windows'], + tags: [], + type: 'simple', + updated_by: 'elastic', + }, + ], + page: 1, + per_page: 25, + total: 1, + }); + }); + it('should return 404 if given a list_id that does not exist', async () => { const { body } = await supertest .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=non_exist`) diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts index cdb0ea37a6417..f2a59d6b22b2e 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts @@ -20,7 +20,8 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const find = getService('find'); - describe('Observability alerts', function () { + // Failing: See https://github.com/elastic/kibana/issues/140248 + describe.skip('Observability alerts', function () { this.tags('includeFirefox'); const testSubjects = getService('testSubjects'); diff --git a/x-pack/test/performance/journeys/base.config.ts b/x-pack/test/performance/journeys/base.config.ts index ee0d298aec4a2..f2924b3a8a785 100644 --- a/x-pack/test/performance/journeys/base.config.ts +++ b/x-pack/test/performance/journeys/base.config.ts @@ -60,7 +60,7 @@ export default async function ({ readConfigFile, log }: FtrConfigProviderContext ...functionalConfig.get('kbnTestServer'), serverArgs: [ ...functionalConfig.get('kbnTestServer.serverArgs'), - `--telemetry.optIn=true`, + `--telemetry.optIn=${process.env.TEST_PERFORMANCE_PHASE === 'TEST'}`, `--telemetry.labels=${JSON.stringify(telemetryLabels)}`, '--csp.strict=false', '--csp.warnLegacyBrowsers=false', diff --git a/x-pack/test/performance/services/performance.ts b/x-pack/test/performance/services/performance.ts index 7f7156284d186..ffddc834dc115 100644 --- a/x-pack/test/performance/services/performance.ts +++ b/x-pack/test/performance/services/performance.ts @@ -8,10 +8,11 @@ /* eslint-disable no-console */ import Url from 'url'; +import * as Rx from 'rxjs'; import { inspect } from 'util'; import { setTimeout } from 'timers/promises'; import apm, { Span, Transaction } from 'elastic-apm-node'; -import playwright, { ChromiumBrowser, Page, BrowserContext, CDPSession } from 'playwright'; +import playwright, { ChromiumBrowser, Page, BrowserContext, CDPSession, Request } from 'playwright'; import { FtrService, FtrProviderContext } from '../ftr_provider_context'; export interface StepCtx { @@ -24,12 +25,16 @@ export type Steps = Array<{ name: string; handler: StepFn }>; export class PerformanceTestingService extends FtrService { private readonly auth = this.ctx.getService('auth'); + private readonly log = this.ctx.getService('log'); private readonly config = this.ctx.getService('config'); private browser: ChromiumBrowser | undefined; private currentSpanStack: Array = []; private currentTransaction: Transaction | undefined | null = undefined; + private pageTeardown$ = new Rx.Subject(); + private telemetryTrackerSubs = new Map(); + constructor(ctx: FtrProviderContext) { super(ctx); @@ -164,6 +169,44 @@ export class PerformanceTestingService extends FtrService { return client; } + private telemetryTrackerCount = 0; + + private trackTelemetryRequests(page: Page) { + const id = ++this.telemetryTrackerCount; + + const requestFailure$ = Rx.fromEvent(page, 'requestfailed'); + const requestSuccess$ = Rx.fromEvent(page, 'requestfinished'); + const request$ = Rx.fromEvent(page, 'request').pipe( + Rx.takeUntil( + this.pageTeardown$.pipe( + Rx.first((p) => p === page), + Rx.delay(3000) + // If EBT client buffers: + // Rx.mergeMap(async () => { + // await page.waitForFunction(() => { + // // return window.kibana_ebt_client.buffer_size == 0 + // }); + // }) + ) + ), + Rx.mergeMap((request) => { + if (!request.url().includes('telemetry-staging.elastic.co')) { + return Rx.EMPTY; + } + + this.log.debug(`Waiting for telemetry request #${id} to complete`); + return Rx.merge(requestFailure$, requestSuccess$).pipe( + Rx.first((r) => r === request), + Rx.tap({ + complete: () => this.log.debug(`Telemetry request #${id} complete`), + }) + ); + }) + ); + + this.telemetryTrackerSubs.set(page, request$.subscribe()); + } + private async interceptBrowserRequests(page: Page) { await page.route('**', async (route, request) => { const headers = await request.allHeaders(); @@ -196,6 +239,7 @@ export class PerformanceTestingService extends FtrService { } const client = await this.sendCDPCommands(context, page); + this.trackTelemetryRequests(page); await this.interceptBrowserRequests(page); await this.handleSteps(steps, page); await this.tearDown(page, client, context); @@ -204,6 +248,16 @@ export class PerformanceTestingService extends FtrService { private async tearDown(page: Page, client: CDPSession, context: BrowserContext) { if (page) { + const telemetryTracker = this.telemetryTrackerSubs.get(page); + this.telemetryTrackerSubs.delete(page); + + if (telemetryTracker && !telemetryTracker.closed) { + this.log.info( + `Waiting for telemetry requests to complete, including requests starting within next 3 secs` + ); + this.pageTeardown$.next(page); + await new Promise((resolve) => telemetryTracker.add(resolve)); + } await client.detach(); await page.close(); await context.close(); diff --git a/x-pack/test/security_solution_cypress/es_archives/exceptions_2/data.json b/x-pack/test/security_solution_cypress/es_archives/exceptions_2/data.json index 3ad636b20a9cc..72dc01b9bac54 100644 --- a/x-pack/test/security_solution_cypress/es_archives/exceptions_2/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/exceptions_2/data.json @@ -4,7 +4,7 @@ "id": "_aZE5nwBOpWiDweSth_E", "index": "exceptions-0002", "source": { - "@timestamp": "2019-09-02T00:41:06.527Z", + "@timestamp": "2019-09-02T00:45:06.527Z", "agent": { "name": "foo" }, @@ -23,4 +23,31 @@ ] } } -} \ No newline at end of file +} + +{ + "type": "doc", + "value": { + "id": "_aZE5nwBOpWiDweSth_F", + "index": "exceptions-0002", + "source": { + "@timestamp": "2019-09-02T00:46:06.527Z", + "agent": { + "name": "bar" + }, + "unique_value": { + "test": "test field 2" + }, + "user" : [ + { + "name" : "foo", + "id" : "123" + }, + { + "name" : "bar", + "id" : "456" + } + ] + } + } +} diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json index 3e468d7a84ca2..b10cd1b6a1c0d 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json @@ -1,174 +1,174 @@ { - "type":"doc", - "value":{ - "id":"a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 21, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "siem-kibana", + "risk": { + "calculated_level": "Low", + "calculated_score_norm": 21, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "host":{ - "name":"siem-kibana" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Low" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb71f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "host":{ - "name":"fake-1" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb71f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "fake-1", + "risk": { + "calculated_level": "Moderate", + "calculated_score_norm": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb72f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb72f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "fake-2", + "risk": { + "calculated_level": "Moderate", + "calculated_score_norm": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "host":{ - "name":"fake-2" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb73f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "host":{ - "name":"fake-3" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb73f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "fake-3", + "risk": { + "calculated_level": "Moderate", + "calculated_score_norm": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "fake-4", + "risk": { + "calculated_level": "Moderate", + "calculated_score_norm": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "host":{ - "name":"fake-4" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb75f", - "index":"ml_host_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "host":{ - "name":"fake-5" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb75f", + "index": "ml_host_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "fake-5", + "risk": { + "calculated_level": "Moderate", + "calculated_score_norm": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_host_risk_score_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 21, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "host":{ - "name":"siem-kibana" + "type": "doc", + "value": { + "id": "a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index": "ml_host_risk_score_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "host": { + "name": "siem-kibana", + "risk": { + "calculated_level": "Low", + "calculated_score_norm": 21, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Low" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json index 02ceb5b5ebccc..3e1b52cb22f5e 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json @@ -11,27 +11,21 @@ "properties": { "name": { "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "type": "keyword" + }, + "calculated_score_norm": { + "type": "long" + } } } + } }, "ingest_timestamp": { "type": "date" - }, - "risk": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } - } - }, - "risk_stats": { - "properties": { - "risk_score": { - "type": "long" - } - } } } }, @@ -69,35 +63,29 @@ "properties": { "name": { "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "type": "keyword" + }, + "calculated_score_norm": { + "type": "long" + } } } + } }, "ingest_timestamp": { "type": "date" - }, - "risk": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } - } - }, - "risk_stats": { - "properties": { - "risk_score": { - "type": "long" - } - } } } }, "settings": { "index": { "lifecycle": { - "name": "ml_host_risk_score_latest_default", - "rollover_alias": "ml_host_risk_score_latest_default" + "name": "ml_host_risk_score_default", + "rollover_alias": "ml_host_risk_score_default" }, "mapping": { "total_fields": { diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_users/data.json b/x-pack/test/security_solution_cypress/es_archives/risky_users/data.json index 2ea72c8604dc6..5cb0404a9d0d5 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_users/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_users/data.json @@ -1,174 +1,174 @@ { - "type":"doc", - "value":{ - "id":"a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 21, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user1", + "risk": { + "calculated_level": "Low", + "calculated_score_norm": 21, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "user":{ - "name":"user1" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Low" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb71f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "user":{ - "name":"user2" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb71f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user2", + "risk": { + "calculated_score_norm": 50, + "calculated_level": "Moderate", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb72f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb72f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user3", + "risk": { + "calculated_score_norm": 50, + "calculated_level": "Moderate", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "user":{ - "name":"user3" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb73f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "user":{ - "name":"user4" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb73f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user4", + "risk": { + "calculated_score_norm": 50, + "calculated_level": "Moderate", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user5", + "risk": { + "calculated_score_norm": 50, + "calculated_level": "Moderate", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "user":{ - "name":"user5" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb75f", - "index":"ml_user_risk_score_latest_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 50, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] - }, - "user":{ - "name":"user6" + "type": "doc", + "value": { + "id": "a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb75f", + "index": "ml_user_risk_score_latest_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user6", + "risk": { + "calculated_score_norm": 50, + "calculated_level": "Moderate", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Moderate" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } } { - "type":"doc", - "value":{ - "id":"a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_user_risk_score_default", - "source":{ - "@timestamp":"2021-03-10T14:51:05.766Z", - "risk_stats": { - "risk_score": 21, - "rule_risks": [ - { - "rule_name": "Unusual Linux Username", - "rule_risk": 42 - } - ] + "type": "doc", + "value": { + "id": "a4cf452c1e0375c3d4412cb550bd1783358468b3b3b777da4829d72c7d6fb74f", + "index": "ml_user_risk_score_default", + "source": { + "@timestamp": "2021-03-10T14:51:05.766Z", + "user": { + "name": "user1", + "risk": { + "calculated_score_norm": 21, + "calculated_level": "Low", + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + } }, - "user":{ - "name":"user7" - }, - "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", - "risk":"Low" + "ingest_timestamp": "2021-03-09T18:02:08.319296053Z" } } -} +} \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_users/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risky_users/mappings.json index 6e8db71b1813d..77eade9df7994 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_users/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_users/mappings.json @@ -11,27 +11,21 @@ "properties": { "name": { "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "type": "keyword" + }, + "calculated_score_norm": { + "type": "long" + } } } + } }, "ingest_timestamp": { "type": "date" - }, - "risk": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } - } - }, - "risk_stats": { - "properties": { - "risk_score": { - "type": "long" - } - } } } }, @@ -69,35 +63,29 @@ "properties": { "name": { "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "type": "keyword" + }, + "calculated_score_norm": { + "type": "long" + } } } + } }, "ingest_timestamp": { "type": "date" - }, - "risk": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } - } - }, - "risk_stats": { - "properties": { - "risk_score": { - "type": "long" - } - } } } }, "settings": { "index": { "lifecycle": { - "name": "ml_user_risk_score_latest_default", - "rollover_alias": "ml_user_risk_score_latest_default" + "name": "ml_user_risk_score_default", + "rollover_alias": "ml_user_risk_score_default" }, "mapping": { "total_fields": { @@ -111,4 +99,4 @@ } } } -} +} \ No newline at end of file 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 81a1dc109b562..8c8629002c93f 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,28 +34,38 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Actions', ], [ - 'Host-ku5jy6j0pw', + 'Host-nyierkw2gu', 'x', 'x', - 'Unsupported', + 'Failure', 'Windows', - '10.12.215.130, 10.130.188.228,10.19.102.141', - '7.0.13', + '10.180.151.227, 10.44.18.210', + '7.1.9', 'x', '', ], [ - 'Host-ntr4rkj24m', + 'Host-rs9wp4o6l9', 'x', 'x', - 'Success', + 'Warning', 'Windows', - '10.36.46.252, 10.222.152.110', - '7.4.13', + '10.218.38.118, 10.80.35.162', + '8.0.8', + 'x', + '', + ], + [ + 'Host-u5jy6j0pwb', + 'x', + 'x', + 'Warning', + 'Linux', + '10.87.11.145, 10.117.106.109,10.242.136.97', + '7.13.1', 'x', '', ], - ['Host-q9qenwrl9k', 'x', 'x', 'Warning', 'Windows', '10.206.226.90', '7.11.10', 'x', ''], ]; const formattedTableData = async () => { @@ -183,38 +193,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(tableData).to.eql(expectedDataFromQuery); }); - it('for the kql filtering for united.endpoint.host.hostname : "Host-ku5jy6j0pw", table shows 1 item', async () => { + it('for the kql filtering for united.endpoint.host.hostname, table shows 1 item', async () => { + const expectedDataFromQuery = [...expectedData.slice(0, 2).map((row) => [...row])]; + const hostName = expectedDataFromQuery[1][0]; const adminSearchBar = await testSubjects.find('adminSearchBar'); await adminSearchBar.clearValueWithKeyboard(); await adminSearchBar.type( - 'united.endpoint.host.hostname : "Host-ku5jy6j0pw" or host.hostname : "Host-ku5jy6j0pw" ' + `united.endpoint.host.hostname : "${hostName}" or host.hostname : "${hostName}" ` ); const querySubmitButton = await testSubjects.find('querySubmitButton'); await querySubmitButton.click(); - const expectedDataFromQuery = [ - [ - 'Endpoint', - 'Agent status', - 'Policy', - 'Policy status', - 'OS', - 'IP address', - 'Version', - 'Last active', - 'Actions', - ], - [ - 'Host-ku5jy6j0pw', - 'x', - 'x', - 'Unsupported', - 'Windows', - '10.12.215.130, 10.130.188.228,10.19.102.141', - '7.0.13', - 'x', - '', - ], - ]; await pageObjects.endpoint.waitForTableToHaveNumberOfEntries( 'endpointListTable', 1, diff --git a/yarn.lock b/yarn.lock index 92574a3ec5eb1..03e4ddaace093 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1478,10 +1478,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@48.0.0": - version "48.0.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-48.0.0.tgz#ccbbe7507102f078b80285e045a421105c0fc780" - integrity sha512-sj0L0JKU3KLJw0Ci1RzSj5WkhGc7ptAft/ulunF+w0i5vG7qgDdbnemCvRIPkgGiu2AnAnIj/bkK3IcxpAbmGA== +"@elastic/charts@48.0.1": + version "48.0.1" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-48.0.1.tgz#52391ab37bcc3188748e5ac4ffe5275ce8cd93cd" + integrity sha512-cmu65UTwbQSj4Gk1gdhlORfbqrCeel9FMpz/fVODOpsRWh/4dwxZhN/x9Ob1e4W5jZDfd6OV47Gc9ZfQVzxKrQ== dependencies: "@popperjs/core" "^2.4.0" bezier-easing "^2.1.0" @@ -3219,6 +3219,26 @@ version "0.0.0" uid "" +"@kbn/core-status-common-internal@link:bazel-bin/packages/core/status/core-status-common-internal": + version "0.0.0" + uid "" + +"@kbn/core-status-common@link:bazel-bin/packages/core/status/core-status-common": + version "0.0.0" + uid "" + +"@kbn/core-status-server-internal@link:bazel-bin/packages/core/status/core-status-server-internal": + version "0.0.0" + uid "" + +"@kbn/core-status-server-mocks@link:bazel-bin/packages/core/status/core-status-server-mocks": + version "0.0.0" + uid "" + +"@kbn/core-status-server@link:bazel-bin/packages/core/status/core-status-server": + version "0.0.0" + uid "" + "@kbn/core-test-helpers-deprecations-getters@link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters": version "0.0.0" uid "" @@ -3411,14 +3431,6 @@ version "0.0.0" uid "" -"@kbn/jsonc@link:bazel-bin/packages/kbn-jsonc": - version "0.0.0" - uid "" - -"@kbn/kibana-manifest-parser@link:bazel-bin/packages/kbn-kibana-manifest-parser": - version "0.0.0" - uid "" - "@kbn/kibana-manifest-schema@link:bazel-bin/packages/kbn-kibana-manifest-schema": version "0.0.0" uid "" @@ -3691,7 +3703,11 @@ version "0.0.0" uid "" -"@kbn/shared-ux-storybook-config@link:bazel-bin/packages/shared-ux/storybook/config": +"@kbn/shared-ux-router-mocks@link:bazel-bin/packages/shared-ux/router/mocks": + version "0.0.0" + uid "" + +"@kbn/shared-ux-services@link:bazel-bin/packages/kbn-shared-ux-services": version "0.0.0" uid "" @@ -3699,6 +3715,10 @@ version "0.0.0" uid "" +"@kbn/shared-ux-storybook@link:bazel-bin/packages/kbn-shared-ux-storybook": + version "0.0.0" + uid "" + "@kbn/shared-ux-utility@link:bazel-bin/packages/kbn-shared-ux-utility": version "0.0.0" uid "" @@ -7321,6 +7341,26 @@ version "0.0.0" uid "" +"@types/kbn__core-status-common-internal@link:bazel-bin/packages/core/status/core-status-common-internal/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-status-common@link:bazel-bin/packages/core/status/core-status-common/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-status-server-internal@link:bazel-bin/packages/core/status/core-status-server-internal/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-status-server-mocks@link:bazel-bin/packages/core/status/core-status-server-mocks/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-status-server@link:bazel-bin/packages/core/status/core-status-server/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-test-helpers-deprecations-getters@link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters/npm_module_types": version "0.0.0" uid "" @@ -7493,18 +7533,10 @@ version "0.0.0" uid "" -"@types/kbn__jsonc@link:bazel-bin/packages/kbn-jsonc/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__kbn-ci-stats-performance-metrics@link:bazel-bin/packages/kbn-kbn-ci-stats-performance-metrics/npm_module_types": version "0.0.0" uid "" -"@types/kbn__kibana-manifest-parser@link:bazel-bin/packages/kbn-kibana-manifest-parser/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__kibana-manifest-schema@link:bazel-bin/packages/kbn-kibana-manifest-schema/npm_module_types": version "0.0.0" uid "" @@ -7773,7 +7805,11 @@ version "0.0.0" uid "" -"@types/kbn__shared-ux-storybook-config@link:bazel-bin/packages/shared-ux/storybook/config/npm_module_types": +"@types/kbn__shared-ux-router-mocks@link:bazel-bin/packages/shared-ux/router/mocks/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 "" @@ -7781,6 +7817,10 @@ version "0.0.0" uid "" +"@types/kbn__shared-ux-storybook@link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__shared-ux-utility@link:bazel-bin/packages/kbn-shared-ux-utility/npm_module_types": version "0.0.0" uid "" @@ -18662,7 +18702,7 @@ jquery@^3.5.0: resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== -js-base64@^2.4.3: +js-base64@^2.4.9: version "2.5.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== @@ -20901,10 +20941,10 @@ node-releases@^2.0.5: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== -node-sass@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-7.0.1.tgz#ad4f6bc663de8acc0a9360db39165a1e2620aa72" - integrity sha512-uMy+Xt29NlqKCFdFRZyXKOTqGt+QaKHexv9STj2WeLottnlqZEEWx6Bj0MXNthmFRRdM/YwyNo/8Tr46TOM0jQ== +node-sass@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-7.0.3.tgz#7620bcd5559c2bf125c4fbb9087ba75cd2df2ab2" + integrity sha512-8MIlsY/4dXUkJDYht9pIWBhMil3uHmE8b/AdJPjmFn1nBx9X9BASzfzmsCy0uCCb8eqI3SYYzVPDswWqSx7gjw== dependencies: async-foreach "^0.1.3" chalk "^4.1.2" @@ -20918,7 +20958,7 @@ node-sass@7.0.1: node-gyp "^8.4.1" npmlog "^5.0.0" request "^2.88.0" - sass-graph "4.0.0" + sass-graph "^4.0.1" stdout-stream "^1.4.0" "true-case-path" "^1.0.2" @@ -24866,14 +24906,14 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sass-graph@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-4.0.0.tgz#fff8359efc77b31213056dfd251d05dadc74c613" - integrity sha512-WSO/MfXqKH7/TS8RdkCX3lVkPFQzCgbqdGsmSKq6tlPU+GpGEsa/5aW18JqItnqh+lPtcjifqdZ/VmiILkKckQ== +sass-graph@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-4.0.1.tgz#2ff8ca477224d694055bf4093f414cf6cfad1d2e" + integrity sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA== dependencies: glob "^7.0.0" lodash "^4.17.11" - scss-tokenizer "^0.3.0" + scss-tokenizer "^0.4.3" yargs "^17.2.1" sass-loader@^10.3.1: @@ -24983,13 +25023,13 @@ screenfull@^5.0.0: resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6" integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA== -scss-tokenizer@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.3.0.tgz#ef7edc3bc438b25cd6ffacf1aa5b9ad5813bf260" - integrity sha512-14Zl9GcbBvOT9057ZKjpz5yPOyUWG2ojd9D5io28wHRYsOrs7U95Q+KNL87+32p8rc+LvDpbu/i9ZYjM9Q+FsQ== +scss-tokenizer@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz#1058400ee7d814d71049c29923d2b25e61dc026c" + integrity sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw== dependencies: - js-base64 "^2.4.3" - source-map "^0.7.1" + js-base64 "^2.4.9" + source-map "^0.7.3" secure-json-parse@^2.4.0: version "2.4.0" @@ -25589,7 +25629,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.1, source-map@^0.7.3: +source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==