diff --git a/.buildkite/pipelines/pull_request/deploy_cloud.yml b/.buildkite/pipelines/pull_request/deploy_cloud.yml index 7185911953232..5306b4f0094bb 100644 --- a/.buildkite/pipelines/pull_request/deploy_cloud.yml +++ b/.buildkite/pipelines/pull_request/deploy_cloud.yml @@ -5,6 +5,7 @@ steps: queue: n2-2-spot depends_on: build timeout_in_minutes: 30 + soft_fail: true retry: automatic: - exit_status: '-1' diff --git a/.buildkite/scripts/common/setup_bazel.sh b/.buildkite/scripts/common/setup_bazel.sh index de381010b8aa3..a60543e36c941 100755 --- a/.buildkite/scripts/common/setup_bazel.sh +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -11,7 +11,7 @@ cat < $KIBANA_DIR/.bazelrc build --build_metadata=ROLE=CI EOF -BAZEL_CACHE_MODE=${BAZEL_CACHE_MODE:-gcs} +BAZEL_CACHE_MODE=none if [[ "$BAZEL_CACHE_MODE" == "gcs" ]]; then echo "[bazel] enabling caching with GCS buckets" diff --git a/.buildkite/scripts/steps/artifacts/build.sh b/.buildkite/scripts/steps/artifacts/build.sh index 337d0289daa72..7c18dcb328a28 100644 --- a/.buildkite/scripts/steps/artifacts/build.sh +++ b/.buildkite/scripts/steps/artifacts/build.sh @@ -29,5 +29,6 @@ if [ -d .beats ]; then cd .beats buildkite-agent artifact upload 'metricbeat-*' buildkite-agent artifact upload 'filebeat-*' + buildkite-agent artifact upload 'beats_manifest.json' cd - fi diff --git a/.buildkite/scripts/steps/artifacts/publish.sh b/.buildkite/scripts/steps/artifacts/publish.sh index 395a8196780d3..6b79841b7f830 100644 --- a/.buildkite/scripts/steps/artifacts/publish.sh +++ b/.buildkite/scripts/steps/artifacts/publish.sh @@ -58,6 +58,10 @@ if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]]; then export VAULT_ROLE_ID="$(retry 5 15 gcloud secrets versions access latest --secret=kibana-buildkite-vault-role-id)" export VAULT_SECRET_ID="$(retry 5 15 gcloud secrets versions access latest --secret=kibana-buildkite-vault-secret-id)" export VAULT_ADDR="https://secrets.elastic.co:8200" + + download_artifact beats_manifest.json /tmp --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" + export BEATS_MANIFEST_URL=$(jq -r .manifest_url /tmp/beats_manifest.json) + docker run --rm \ --name release-manager \ -e VAULT_ADDR \ @@ -72,6 +76,7 @@ if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]]; then --workflow "$WORKFLOW" \ --version "$BASE_VERSION" \ --qualifier "$VERSION_QUALIFIER" \ + --dependency "beats:$BEATS_MANIFEST_URL" \ --artifact-set main ARTIFACTS_SUBDOMAIN="artifacts-$WORKFLOW" diff --git a/.buildkite/scripts/steps/lint.sh b/.buildkite/scripts/steps/lint.sh index 8c6a2e2e6202a..301737c9132a0 100755 --- a/.buildkite/scripts/steps/lint.sh +++ b/.buildkite/scripts/steps/lint.sh @@ -16,9 +16,9 @@ echo '--- Lint: eslint' # after possibly commiting fixed files to the repo set +e; if is_pr && ! is_auto_commit_disabled; then - git ls-files | grep -E '\.(js|mjs|ts|tsx)$' | xargs -n 250 -P 6 node scripts/eslint --no-cache --fix + git ls-files | grep -E '\.(js|mjs|ts|tsx)$' | xargs -n 250 -P 4 node scripts/eslint --no-cache --fix else - git ls-files | grep -E '\.(js|mjs|ts|tsx)$' | xargs -n 250 -P 6 node scripts/eslint --no-cache + git ls-files | grep -E '\.(js|mjs|ts|tsx)$' | xargs -n 250 -P 4 node scripts/eslint --no-cache fi eslint_exit=$? diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ca8e8c07fec63..17e8a0f1b4d4b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -332,11 +332,15 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/event_log/ @elastic/response-ops /x-pack/plugins/task_manager/ @elastic/response-ops /x-pack/plugins/stack_connectors/ @elastic/response-ops -/x-pack/plugins/stack_connectors/public/connector_types/stack/ @elastic/response-ops-execution -/x-pack/plugins/stack_connectors/server/connector_types/stack/ @elastic/response-ops-execution -/x-pack/plugins/stack_connectors/public/connector_types/cases/ @elastic/response-ops-cases -/x-pack/plugins/stack_connectors/server/connector_types/cases/ @elastic/response-ops-cases +/x-pack/plugins/stack_connectors/public/connector_types/stack/ @elastic/response-ops @elastic/response-ops-execution +/x-pack/plugins/stack_connectors/server/connector_types/stack/ @elastic/response-ops @elastic/response-ops-execution +/x-pack/plugins/stack_connectors/public/connector_types/cases/ @elastic/response-ops @elastic/response-ops-cases +/x-pack/plugins/stack_connectors/server/connector_types/cases/ @elastic/response-ops @elastic/response-ops-cases /x-pack/test/alerting_api_integration/ @elastic/response-ops +/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/ @elastic/response-ops @elastic/response-ops-execution +/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/ @elastic/response-ops @elastic/response-ops-cases +/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/ @elastic/response-ops @elastic/response-ops-execution +/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/ @elastic/response-ops @elastic/response-ops-cases /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/response-ops /x-pack/plugins/triggers_actions_ui/ @elastic/response-ops /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/response-ops @@ -829,6 +833,7 @@ 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/test-helpers/core-test-helpers-so-type-serializer @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 diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index 86bf3911c3196..c20f6e15fb966 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -741,13 +741,9 @@ "label": "request", "description": [], "signature": [ - "({ url, data, method, responseSchema, headers, ...config }: { url: string; responseSchema: ", - "Type", - "; method?: ", - "Method", - " | undefined; } & ", - "AxiosRequestConfig", - ") => Promise<", + "({ url, data, method, responseSchema, headers, ...config }: ", + "SubActionRequestParams", + ") => Promise<", "AxiosResponse", ">" ], @@ -763,13 +759,8 @@ "label": "{\n url,\n data,\n method = 'get',\n responseSchema,\n headers,\n ...config\n }", "description": [], "signature": [ - "{ url: string; responseSchema: ", - "Type", - "; method?: ", - "Method", - " | undefined; } & ", - "AxiosRequestConfig", - "" + "SubActionRequestParams", + "" ], "path": "x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts", "deprecated": false, @@ -1246,59 +1237,12 @@ "label": "renderParameterTemplates", "description": [], "signature": [ - "((params: Params, variables: Record, actionId?: string | undefined) => Params) | undefined" + "RenderParameterTemplates", + " | undefined" ], "path": "x-pack/plugins/actions/server/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "actions", - "id": "def-server.ActionType.renderParameterTemplates.$1", - "type": "Uncategorized", - "tags": [], - "label": "params", - "description": [], - "signature": [ - "Params" - ], - "path": "x-pack/plugins/actions/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "actions", - "id": "def-server.ActionType.renderParameterTemplates.$2", - "type": "Object", - "tags": [], - "label": "variables", - "description": [], - "signature": [ - "Record" - ], - "path": "x-pack/plugins/actions/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "actions", - "id": "def-server.ActionType.renderParameterTemplates.$3", - "type": "string", - "tags": [], - "label": "actionId", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/actions/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "actions", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 311bee3f6ba9d..c8022a3dc8f13 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.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 | |-------------------|-----------|------------------------|-----------------| -| 216 | 0 | 211 | 21 | +| 213 | 0 | 208 | 23 | ## Client diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 58312f0eaa627..db45d8b0ccf58 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 0b0c41380b9d6..7df3eeb961b09 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 6757b8a06f9a7..cf275b4f7fda8 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -551,9 +551,9 @@ ", filterOpts: ", "AlertingAuthorizationFilterOpts", ") => Promise<{ filter?: ", - "KueryNode", - " | ", "JsonObject", + " | ", + "KueryNode", " | undefined; ensureRuleTypeIsAuthorized: (ruleTypeId: string, consumer: string, auth: string) => void; }>" ], "path": "x-pack/plugins/alerting/server/authorization/alerting_authorization.ts", @@ -634,9 +634,9 @@ "text": "WriteOperations" }, ") => Promise<{ filter?: ", - "KueryNode", - " | ", "JsonObject", + " | ", + "KueryNode", " | undefined; ensureRuleTypeIsAuthorized: (ruleTypeId: string, consumer: string, auth: string) => void; }>" ], "path": "x-pack/plugins/alerting/server/authorization/alerting_authorization.ts", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 558413407b380..e5cbe70302b94 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index bee8505fd83c9..e7c08ef112a82 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-10-05 +date: 2022-10-06 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 00b9a4694465a..143b086150408 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-10-05 +date: 2022-10-06 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 8a65db4587c7e..415ab28433c84 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-10-05 +date: 2022-10-06 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 661b740bfeaee..78001f970e6af 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-10-05 +date: 2022-10-06 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 d79be9464f7f0..4bdb21a16e58f 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 7057a6424a741..2195dc276c86e 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index e0ed849f6849d..f822ee3265a60 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 0c6772023c284..e96d47434b297 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 6d30ae17a1e71..5e5d6c0623095 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index ed1aefa51347b..258d23dc4e05b 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-10-05 +date: 2022-10-06 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 ddb1f141f152a..830b195a50b76 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-10-05 +date: 2022-10-06 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 3e93cc800acc1..e5fa584a95d47 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-10-05 +date: 2022-10-06 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 10cb0b13616f1..197b485695674 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -10930,14 +10930,6 @@ "plugin": "graph", "path": "x-pack/plugins/graph/public/helpers/saved_workspace_utils.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts" - }, { "plugin": "savedObjects", "path": "src/plugins/saved_objects/public/saved_object/saved_object.test.ts" @@ -14790,6 +14782,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/app/app.tsx" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts" + }, { "plugin": "@kbn/core-application-browser-internal", "path": "packages/core/application/core-application-browser-internal/src/ui/app_container.tsx" @@ -40569,14 +40569,6 @@ "plugin": "graph", "path": "x-pack/plugins/graph/public/helpers/saved_workspace_utils.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts" - }, { "plugin": "savedObjects", "path": "src/plugins/saved_objects/public/saved_object/saved_object.test.ts" @@ -53964,10 +53956,9 @@ "\n A user credentials container.\nIt accommodates the necessary auth credentials to impersonate the current user.\n" ], "signature": [ - "FakeRequest", - " | ", "KibanaRequest", - "" + " | ", + "FakeRequest" ], "path": "node_modules/@types/kbn__core-elasticsearch-server/index.d.ts", "deprecated": false, diff --git a/api_docs/core.mdx b/api_docs/core.mdx index d0e7e1126e5f8..a1e985bca2c7e 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index cee85ae1508f5..b142fb34cfb66 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-10-05 +date: 2022-10-06 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 219c1b0f10951..86181c9b1b96f 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-10-05 +date: 2022-10-06 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 8109f1ea544a3..1c3208091d7d7 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 10ca74caf4ce6..33d5834e2191a 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -2588,7 +2588,7 @@ "description": [], "signature": [ "PluginInitializerContext", - "; }>; sessions: Readonly<{} & { enabled: boolean; pageSize: number; trackingInterval: moment.Duration; cleanupInterval: moment.Duration; expireInterval: moment.Duration; monitoringTaskTimeout: moment.Duration; notTouchedTimeout: moment.Duration; notTouchedInProgressTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>>" + "; }>; sessions: Readonly<{} & { enabled: boolean; notTouchedTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>>" ], "path": "src/plugins/data/public/plugin.ts", "deprecated": false, @@ -7523,6 +7523,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-public.IKibanaSearchResponse.isStored", + "type": "CompoundType", + "tags": [], + "label": "isStored", + "description": [ + "\nIndicates whether the search has been saved to a search-session object and long keepAlive was set" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-public.IKibanaSearchResponse.warning", @@ -7649,6 +7665,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-public.ISearchOptions.isSearchStored", + "type": "CompoundType", + "tags": [], + "label": "isSearchStored", + "description": [ + "\nWhether the search was successfully polled after session was saved. Search was added to a session saved object and keepAlive extended." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-public.ISearchOptions.isRestore", @@ -11748,7 +11780,7 @@ "section": "def-server.PluginInitializerContext", "text": "PluginInitializerContext" }, - "; }>; sessions: Readonly<{} & { enabled: boolean; pageSize: number; trackingInterval: moment.Duration; cleanupInterval: moment.Duration; expireInterval: moment.Duration; monitoringTaskTimeout: moment.Duration; notTouchedTimeout: moment.Duration; notTouchedInProgressTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>>" + "; }>; sessions: Readonly<{} & { enabled: boolean; notTouchedTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>>" ], "path": "src/plugins/data/server/plugin.ts", "deprecated": false, @@ -17029,6 +17061,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-server.ISearchOptions.isSearchStored", + "type": "CompoundType", + "tags": [], + "label": "isSearchStored", + "description": [ + "\nWhether the search was successfully polled after session was saved. Search was added to a session saved object and keepAlive extended." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-server.ISearchOptions.isRestore", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index ced84972a8503..eea73f4aaae1d 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-10-05 +date: 2022-10-06 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 | |-------------------|-----------|------------------------|-----------------| -| 3213 | 33 | 2509 | 23 | +| 3221 | 33 | 2513 | 24 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index ca93395752694..ad2475ad665e9 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-10-05 +date: 2022-10-06 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 | |-------------------|-----------|------------------------|-----------------| -| 3213 | 33 | 2509 | 23 | +| 3221 | 33 | 2513 | 24 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index b923ed26434d9..08d6314c52ffe 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -208,7 +208,9 @@ "Observable", "<", "SessionMeta", - ">; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => () => void; getSessionId: () => string | undefined; getSession$: () => ", + ">; readonly disableSaveAfterSearchesExpire$: ", + "Observable", + "; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => TrackSearchHandler; getSessionId: () => string | undefined; getSession$: () => ", "Observable", "; isStored: () => boolean; isRestore: () => boolean; restore: (sessionId: string) => void; continue: (sessionId: string) => void; clear: () => void; cancel: () => Promise; renameCurrentSession: (newName: string) => Promise; isCurrentSession: (sessionId?: string | undefined) => boolean; getSearchOptions: (sessionId?: string | undefined) => Required; find: (options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - ">; update: (sessionId: string, attributes: unknown) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" + }, + ">; update: (sessionId: string, attributes: unknown) => Promise<", "SavedObjectsUpdateResponse", "<", { @@ -638,7 +646,9 @@ "Observable", "<", "SessionMeta", - ">; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => () => void; getSessionId: () => string | undefined; getSession$: () => ", + ">; readonly disableSaveAfterSearchesExpire$: ", + "Observable", + "; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => TrackSearchHandler; getSessionId: () => string | undefined; getSession$: () => ", "Observable", "; isStored: () => boolean; isRestore: () => boolean; restore: (sessionId: string) => void; continue: (sessionId: string) => void; clear: () => void; cancel: () => Promise; renameCurrentSession: (newName: string) => Promise; isCurrentSession: (sessionId?: string | undefined) => boolean; getSearchOptions: (sessionId?: string | undefined) => Required; find: (options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - ">; update: (sessionId: string, attributes: unknown) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" + }, + ">; update: (sessionId: string, attributes: unknown) => Promise<", "SavedObjectsUpdateResponse", "<", { @@ -1228,8 +1244,14 @@ ">; find: (options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - ">; update: (sessionId: string, attributes: unknown) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" + }, + ">; update: (sessionId: string, attributes: unknown) => Promise<", "SavedObjectsUpdateResponse", "<", { @@ -1288,7 +1310,9 @@ "Observable", "<", "SessionMeta", - ">; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => () => void; getSessionId: () => string | undefined; getSession$: () => ", + ">; readonly disableSaveAfterSearchesExpire$: ", + "Observable", + "; hasAccess: () => boolean; trackSearch: (searchDescriptor: TrackSearchDescriptor) => TrackSearchHandler; getSessionId: () => string | undefined; getSession$: () => ", "Observable", "; isStored: () => boolean; isRestore: () => boolean; restore: (sessionId: string) => void; continue: (sessionId: string) => void; clear: () => void; cancel: () => Promise; renameCurrentSession: (newName: string) => Promise; isCurrentSession: (sessionId?: string | undefined) => boolean; getSearchOptions: (sessionId?: string | undefined) => Required; }>; sessions: Readonly<{} & { enabled: boolean; pageSize: number; trackingInterval: moment.Duration; cleanupInterval: moment.Duration; expireInterval: moment.Duration; monitoringTaskTimeout: moment.Duration; notTouchedTimeout: moment.Duration; notTouchedInProgressTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>" + "Readonly<{} & { search: Readonly<{} & { aggs: Readonly<{} & { shardDelay: Readonly<{} & { enabled: boolean; }>; }>; sessions: Readonly<{} & { enabled: boolean; notTouchedTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }>; }>" ], "path": "src/plugins/data/server/search/session/session_service.ts", "deprecated": false, @@ -1564,7 +1588,7 @@ "section": "def-server.CoreStart", "text": "CoreStart" }, - ", deps: StartDependencies) => Promise" + ", deps: StartDependencies) => void" ], "path": "src/plugins/data/server/search/session/session_service.ts", "deprecated": false, @@ -1842,8 +1866,8 @@ "label": "find", "description": [], "signature": [ - "({ savedObjectsClient }: ", - "SearchSessionDependencies", + "({ savedObjectsClient, internalElasticsearchClient }: ", + "SearchSessionStatusDependencies", ", user: ", { "pluginId": "security", @@ -1855,16 +1879,14 @@ " | null, options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - "<", { "pluginId": "data", "scope": "common", "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionSavedObjectAttributes", - "text": "SearchSessionSavedObjectAttributes" + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" }, - ", unknown>>" + ">" ], "path": "src/plugins/data/server/search/session/session_service.ts", "deprecated": false, @@ -1875,10 +1897,10 @@ "id": "def-server.SearchSessionService.find.$1", "type": "Object", "tags": [], - "label": "{ savedObjectsClient }", + "label": "{ savedObjectsClient, internalElasticsearchClient }", "description": [], "signature": [ - "SearchSessionDependencies" + "SearchSessionStatusDependencies" ], "path": "src/plugins/data/server/search/session/session_service.ts", "deprecated": false, @@ -2398,6 +2420,93 @@ ], "returnComment": [] }, + { + "parentPluginId": "data", + "id": "def-server.SearchSessionService.status", + "type": "Function", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "(deps: ", + "SearchSessionStatusDependencies", + ", user: ", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null, sessionId: string) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionStatusResponse", + "text": "SearchSessionStatusResponse" + }, + ">" + ], + "path": "src/plugins/data/server/search/session/session_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-server.SearchSessionService.status.$1", + "type": "Object", + "tags": [], + "label": "deps", + "description": [], + "signature": [ + "SearchSessionStatusDependencies" + ], + "path": "src/plugins/data/server/search/session/session_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "data", + "id": "def-server.SearchSessionService.status.$2", + "type": "CompoundType", + "tags": [], + "label": "user", + "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null" + ], + "path": "src/plugins/data/server/search/session/session_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "data", + "id": "def-server.SearchSessionService.status.$3", + "type": "string", + "tags": [], + "label": "sessionId", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data/server/search/session/session_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "data", "id": "def-server.SearchSessionService.asScopedProvider", @@ -2406,7 +2515,7 @@ "label": "asScopedProvider", "description": [], "signature": [ - "({ savedObjects }: ", + "({ savedObjects, elasticsearch }: ", { "pluginId": "core", "scope": "server", @@ -2432,7 +2541,7 @@ "section": "def-common.ISearchOptions", "text": "ISearchOptions" }, - ") => Promise; trackId: (args_0: ", + ") => Promise; trackId: (searchRequest: ", { "pluginId": "data", "scope": "common", @@ -2440,7 +2549,7 @@ "section": "def-common.IKibanaSearchRequest", "text": "IKibanaSearchRequest" }, - ", args_1: string, args_2: ", + ", searchId: string, options: ", { "pluginId": "data", "scope": "common", @@ -2479,16 +2588,14 @@ ">>; find: (options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - "<", { "pluginId": "data", "scope": "common", "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionSavedObjectAttributes", - "text": "SearchSessionSavedObjectAttributes" + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" }, - ", unknown>>; update: (sessionId: string, attributes: Partial<", + ">; update: (sessionId: string, attributes: Partial<", { "pluginId": "data", "scope": "common", @@ -2526,7 +2633,15 @@ "section": "def-common.SearchSessionSavedObjectAttributes", "text": "SearchSessionSavedObjectAttributes" }, - ">>; delete: (sessionId: string) => Promise<{}>; getConfig: () => Readonly<{} & { enabled: boolean; pageSize: number; trackingInterval: moment.Duration; cleanupInterval: moment.Duration; expireInterval: moment.Duration; monitoringTaskTimeout: moment.Duration; notTouchedTimeout: moment.Duration; notTouchedInProgressTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }" + ">>; delete: (sessionId: string) => Promise<{}>; status: (sessionId: string) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionStatusResponse", + "text": "SearchSessionStatusResponse" + }, + ">; getConfig: () => Readonly<{} & { enabled: boolean; notTouchedTimeout: moment.Duration; maxUpdateRetries: number; defaultExpiration: moment.Duration; management: Readonly<{} & { refreshInterval: moment.Duration; maxSessions: number; refreshTimeout: moment.Duration; expiresSoonWarning: moment.Duration; }>; }>; }" ], "path": "src/plugins/data/server/search/session/session_service.ts", "deprecated": false, @@ -2537,7 +2652,7 @@ "id": "def-server.SearchSessionService.asScopedProvider.$1", "type": "Object", "tags": [], - "label": "{ savedObjects }", + "label": "{ savedObjects, elasticsearch }", "description": [], "signature": [ { @@ -2693,15 +2808,7 @@ "label": "attributes", "description": [], "signature": [ - "{ sessionId?: string | undefined; name?: string | undefined; appId?: string | undefined; created?: string | undefined; touched?: string | undefined; expires?: string | undefined; completed?: string | null | undefined; status?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionStatus", - "text": "SearchSessionStatus" - }, - " | undefined; locatorId?: string | undefined; initialState?: ", + "{ sessionId?: string | undefined; name?: string | undefined; appId?: string | undefined; created?: string | undefined; expires?: string | undefined; locatorId?: string | undefined; initialState?: ", "SerializableRecord", " | undefined; restoreState?: ", "SerializableRecord", @@ -2713,7 +2820,7 @@ "section": "def-common.SearchSessionRequestInfo", "text": "SearchSessionRequestInfo" }, - "> | undefined; persisted?: boolean | undefined; realmType?: string | undefined; realmName?: string | undefined; username?: string | undefined; version?: string | undefined; }" + "> | undefined; realmType?: string | undefined; realmName?: string | undefined; username?: string | undefined; version?: string | undefined; isCanceled?: boolean | undefined; }" ], "path": "src/plugins/data/server/search/session/types.ts", "deprecated": false, @@ -2770,16 +2877,14 @@ "(options: Omit<", "SavedObjectsFindOptions", ", \"type\">) => Promise<", - "SavedObjectsFindResponse", - "<", { "pluginId": "data", "scope": "common", "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionSavedObjectAttributes", - "text": "SearchSessionSavedObjectAttributes" + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" }, - ", unknown>>" + ">" ], "path": "src/plugins/data/server/search/types.ts", "deprecated": false, @@ -2868,15 +2973,7 @@ "label": "attributes", "description": [], "signature": [ - "{ sessionId?: string | undefined; name?: string | undefined; appId?: string | undefined; created?: string | undefined; touched?: string | undefined; expires?: string | undefined; completed?: string | null | undefined; status?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionStatus", - "text": "SearchSessionStatus" - }, - " | undefined; locatorId?: string | undefined; initialState?: ", + "{ sessionId?: string | undefined; name?: string | undefined; appId?: string | undefined; created?: string | undefined; expires?: string | undefined; locatorId?: string | undefined; initialState?: ", "SerializableRecord", " | undefined; restoreState?: ", "SerializableRecord", @@ -2888,7 +2985,7 @@ "section": "def-common.SearchSessionRequestInfo", "text": "SearchSessionRequestInfo" }, - "> | undefined; persisted?: boolean | undefined; realmType?: string | undefined; realmName?: string | undefined; username?: string | undefined; version?: string | undefined; }" + "> | undefined; realmType?: string | undefined; realmName?: string | undefined; username?: string | undefined; version?: string | undefined; isCanceled?: boolean | undefined; }" ], "path": "src/plugins/data/server/search/session/types.ts", "deprecated": false, @@ -3003,6 +3100,42 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "data", + "id": "def-server.IScopedSearchClient.getSessionStatus", + "type": "Function", + "tags": [], + "label": "getSessionStatus", + "description": [], + "signature": [ + "(sessionId: string) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionStatusResponse", + "text": "SearchSessionStatusResponse" + }, + ">" + ], + "path": "src/plugins/data/server/search/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-server.IScopedSearchClient.getSessionStatus.$1", + "type": "string", + "tags": [], + "label": "sessionId", + "description": [], + "path": "src/plugins/data/server/search/session/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "initialIsOpen": false @@ -26589,6 +26722,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-common.IKibanaSearchResponse.isStored", + "type": "CompoundType", + "tags": [], + "label": "isStored", + "description": [ + "\nIndicates whether the search has been saved to a search-session object and long keepAlive was set" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-common.IKibanaSearchResponse.warning", @@ -27048,6 +27197,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "data", + "id": "def-common.ISearchOptions.isSearchStored", + "type": "CompoundType", + "tags": [], + "label": "isSearchStored", + "description": [ + "\nWhether the search was successfully polled after session was saved. Search was added to a session saved object and keepAlive extended." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "data", "id": "def-common.ISearchOptions.isRestore", @@ -28215,10 +28380,10 @@ }, { "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions", + "id": "def-common.SearchSessionRequestInfo", "type": "Interface", "tags": [], - "label": "SearchSessionFindOptions", + "label": "SearchSessionRequestInfo", "description": [], "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, @@ -28226,55 +28391,12 @@ "children": [ { "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions.page", - "type": "number", - "tags": [], - "label": "page", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions.perPage", - "type": "number", - "tags": [], - "label": "perPage", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions.sortField", - "type": "string", - "tags": [], - "label": "sortField", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions.sortOrder", + "id": "def-common.SearchSessionRequestInfo.id", "type": "string", "tags": [], - "label": "sortOrder", - "description": [], - "signature": [ - "string | undefined" + "label": "id", + "description": [ + "\nID of the async search request" ], "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, @@ -28282,13 +28404,12 @@ }, { "parentPluginId": "data", - "id": "def-common.SearchSessionFindOptions.filter", + "id": "def-common.SearchSessionRequestInfo.strategy", "type": "string", "tags": [], - "label": "filter", - "description": [], - "signature": [ - "string | undefined" + "label": "strategy", + "description": [ + "\nSearch strategy used to submit the search request" ], "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, @@ -28299,10 +28420,10 @@ }, { "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo", + "id": "def-common.SearchSessionRequestStatus", "type": "Interface", "tags": [], - "label": "SearchSessionRequestInfo", + "label": "SearchSessionRequestStatus", "description": [], "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, @@ -28310,38 +28431,19 @@ "children": [ { "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "\nID of the async search request" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo.strategy", - "type": "string", - "tags": [], - "label": "strategy", - "description": [ - "\nSearch strategy used to submit the search request" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo.status", - "type": "string", + "id": "def-common.SearchSessionRequestStatus.status", + "type": "Enum", "tags": [], "label": "status", - "description": [ - "\nstatus" + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchStatus", + "text": "SearchStatus" + } ], "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, @@ -28349,7 +28451,7 @@ }, { "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo.error", + "id": "def-common.SearchSessionRequestStatus.error", "type": "string", "tags": [], "label": "error", @@ -28433,19 +28535,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionSavedObjectAttributes.touched", - "type": "string", - "tags": [], - "label": "touched", - "description": [ - "\nLast touch time of the session" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "data", "id": "def-common.SearchSessionSavedObjectAttributes.expires", @@ -28459,44 +28548,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionSavedObjectAttributes.completed", - "type": "CompoundType", - "tags": [], - "label": "completed", - "description": [ - "\nTime of transition into completed state,\n\nCan be \"null\" in case already completed session\ntransitioned into in-progress session" - ], - "signature": [ - "string | null | undefined" - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionSavedObjectAttributes.status", - "type": "Enum", - "tags": [], - "label": "status", - "description": [ - "\nstatus" - ], - "signature": [ - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSessionStatus", - "text": "SearchSessionStatus" - } - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "data", "id": "def-common.SearchSessionSavedObjectAttributes.locatorId", @@ -28571,19 +28622,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "data", - "id": "def-common.SearchSessionSavedObjectAttributes.persisted", - "type": "boolean", - "tags": [], - "label": "persisted", - "description": [ - "\nThis value is true if the session was actively stored by the user. If it is false, the session may be purged by the system." - ], - "path": "src/plugins/data/common/search/session/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "data", "id": "def-common.SearchSessionSavedObjectAttributes.realmType", @@ -28640,6 +28678,118 @@ "path": "src/plugins/data/common/search/session/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-common.SearchSessionSavedObjectAttributes.isCanceled", + "type": "CompoundType", + "tags": [], + "label": "isCanceled", + "description": [ + "\n`true` if session was cancelled" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/session/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-common.SearchSessionsFindResponse", + "type": "Interface", + "tags": [], + "label": "SearchSessionsFindResponse", + "description": [ + "\nList of search session objects with on-the-fly calculated search session statuses" + ], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionsFindResponse", + "text": "SearchSessionsFindResponse" + }, + " extends ", + "SavedObjectsFindResponse", + "<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionSavedObjectAttributes", + "text": "SearchSessionSavedObjectAttributes" + }, + ", unknown>" + ], + "path": "src/plugins/data/common/search/session/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.SearchSessionsFindResponse.statuses", + "type": "Object", + "tags": [], + "label": "statuses", + "description": [ + "\nMap containing calculated statuses of search sessions from the find response" + ], + "signature": [ + "{ [x: string]: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionStatusResponse", + "text": "SearchSessionStatusResponse" + }, + "; }" + ], + "path": "src/plugins/data/common/search/session/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-common.SearchSessionStatusResponse", + "type": "Interface", + "tags": [], + "label": "SearchSessionStatusResponse", + "description": [ + "\nOn-the-fly calculated search session status" + ], + "path": "src/plugins/data/common/search/session/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.SearchSessionStatusResponse.status", + "type": "Enum", + "tags": [], + "label": "status", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSessionStatus", + "text": "SearchSessionStatus" + } + ], + "path": "src/plugins/data/common/search/session/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -29636,6 +29786,18 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-common.SearchStatus", + "type": "Enum", + "tags": [], + "label": "SearchStatus", + "description": [], + "path": "src/plugins/data/common/search/session/status.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-common.SortDirection", @@ -32815,7 +32977,7 @@ "signature": [ "{ executionContext?: ", "KibanaExecutionContext", - " | undefined; isStored?: boolean | undefined; isRestore?: boolean | undefined; sessionId?: string | undefined; strategy?: string | undefined; legacyHitsTotal?: boolean | undefined; }" + " | undefined; isStored?: boolean | undefined; isRestore?: boolean | undefined; sessionId?: string | undefined; strategy?: string | undefined; legacyHitsTotal?: boolean | undefined; isSearchStored?: boolean | undefined; }" ], "path": "src/plugins/data/common/search/types.ts", "deprecated": false, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 37a04e66dbac3..ff7beafee5ea0 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-10-05 +date: 2022-10-06 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 | |-------------------|-----------|------------------------|-----------------| -| 3213 | 33 | 2509 | 23 | +| 3221 | 33 | 2513 | 24 | ## Client diff --git a/api_docs/data_view_editor.devdocs.json b/api_docs/data_view_editor.devdocs.json index e3bf819317755..6ecfaa57fcaa8 100644 --- a/api_docs/data_view_editor.devdocs.json +++ b/api_docs/data_view_editor.devdocs.json @@ -151,6 +151,22 @@ "path": "src/plugins/data_view_editor/public/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "dataViewEditor", + "id": "def-public.DataViewEditorProps.showManagementLink", + "type": "CompoundType", + "tags": [], + "label": "showManagementLink", + "description": [ + "\nif set to true a link to the management page is shown" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data_view_editor/public/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 0e39d5e823f16..b312c83afc6d7 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.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 | |-------------------|-----------|------------------------|-----------------| -| 15 | 0 | 7 | 0 | +| 16 | 0 | 7 | 0 | ## Client diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 18cf4e74e0683..6707ffdfaac2f 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-10-05 +date: 2022-10-06 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 1872b9e2c96df..a3f3c0a260ee3 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-10-05 +date: 2022-10-06 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 c4b9b1fb7f204..0bd2b7af8fc6b 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-10-05 +date: 2022-10-06 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 f88cb12f037a6..5a0b2da372063 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-10-05 +date: 2022-10-06 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 729f208a18b9d..d82fa6adb7ae1 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -24,8 +24,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | alerting, discover, securitySolution | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | | | actions, alerting | - | -| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | -| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | +| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, actions, alerting, enterpriseSearch, securitySolution, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | +| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, actions, alerting, enterpriseSearch, securitySolution, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | | | discover, maps, monitoring | - | | | unifiedSearch, discover, maps, infra, graph, securitySolution, stackAlerts, inputControlVis, savedObjects | - | | | data, discover, embeddable | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 5f941f2cc7ae8..cbbb60b136a47 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -654,9 +654,9 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts#:~:text=license%24) | 8.8.0 | | | [request_context_factory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [request_context_factory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [create_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts#:~:text=authc), [delete_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts#:~:text=authc), [finalize_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts#:~:text=authc), [open_close_signals_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts#:~:text=authc), [preview_rules_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts#:~:text=authc), [common.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts#:~:text=authc) | - | | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/index.tsx#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | -| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler) | 8.8.0 | -| | [saved_objects.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts#:~:text=SavedObjectAttributes), [saved_objects.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | -| | [saved_objects.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts#:~:text=SavedObjectAttributes), [saved_objects.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [use_timeline_save_prompt.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts#:~:text=AppLeaveHandler)+ 1 more | 8.8.0 | +| | [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | +| | [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index d56c8f5d43ca1..970e2198e16b2 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -167,7 +167,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | securitySolution | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [list.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.test.ts#:~:text=mode), [response_actions.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts#:~:text=mode)+ 3 more | 8.8.0 | | securitySolution | | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts#:~:text=license%24) | 8.8.0 | | securitySolution | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/index.tsx#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | -| securitySolution | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler) | 8.8.0 | +| securitySolution | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/types.ts#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [routes.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/routes.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/app/app.tsx#:~:text=AppLeaveHandler), [use_timeline_save_prompt.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts#:~:text=AppLeaveHandler)+ 1 more | 8.8.0 | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index bea56ebf8e4ad..b0bdc80f76221 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-10-05 +date: 2022-10-06 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 b8740b3f65779..1004bc000228f 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-10-05 +date: 2022-10-06 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 a69a612752a57..0a1ae92c44f65 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-10-05 +date: 2022-10-06 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 a2cba98a02695..fe1b4aaa6b3a0 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-10-05 +date: 2022-10-06 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 3d1d04c4a146f..647b063aab79f 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-10-05 +date: 2022-10-06 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 3d2e419803cd2..02f77132a6dd3 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-10-05 +date: 2022-10-06 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 91eed8ab2278f..eb9a8b2e6a08d 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-10-05 +date: 2022-10-06 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 e06c92bc8bdc5..85ceaecf956d8 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 29ec8083a7dce..2929235fc1732 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 72ec8f56b3ebc..544fd761d5cb3 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 46195d6b21e51..df2e2b21c11e6 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-10-05 +date: 2022-10-06 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 94f1bd66b8875..5ad24ba7a8bc4 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-10-05 +date: 2022-10-06 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 d9100164eeb87..b2e900dcbcd09 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-10-05 +date: 2022-10-06 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 abd46cb4e6bb8..e596984a9abf6 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-10-05 +date: 2022-10-06 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 da4f4dcc2cc9c..bc8919a722ad4 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-10-05 +date: 2022-10-06 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 38e60563d6ec5..98cce847e74ba 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-10-05 +date: 2022-10-06 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 4fc3a1f269410..0aae88922421c 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-10-05 +date: 2022-10-06 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 3d1e452447cd9..5e961ebb7bc87 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-10-05 +date: 2022-10-06 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 ffd241b28188f..45c1407b31eb7 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-10-05 +date: 2022-10-06 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 e4ba10a599435..1d59a537621ec 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-10-05 +date: 2022-10-06 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 def4ceb81ea3f..497c62b3bd6c2 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-10-05 +date: 2022-10-06 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 f5ae9cefc8173..4e47f3e415867 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index ea9df66ecb923..633c4c7b66686 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index b819785479260..24cb3aefed102 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-10-05 +date: 2022-10-06 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 14b8a2f9bd928..7fe206a6ed36c 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-10-05 +date: 2022-10-06 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 c589ca7acf558..23e1e83418399 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-10-05 +date: 2022-10-06 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 3a43a4315329b..8a784eab30a91 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-10-05 +date: 2022-10-06 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 d8df7cb3f30ed..524666d7d411d 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-10-05 +date: 2022-10-06 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 662ddfc0c096a..268fcade0bb3c 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -14119,7 +14119,7 @@ "section": "def-common.RegistryPackage", "text": "RegistryPackage" }, - ", \"internal\" | \"assets\" | \"readme\" | \"data_streams\" | \"elasticsearch\">" + ", \"elasticsearch\" | \"internal\" | \"assets\" | \"readme\" | \"data_streams\">" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 97ed812af9644..9fe27d03d10bd 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index b5d9a7f0c4fa5..661c7226b9915 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.devdocs.json b/api_docs/guided_onboarding.devdocs.json index 8b44efa700b66..a186673312896 100644 --- a/api_docs/guided_onboarding.devdocs.json +++ b/api_docs/guided_onboarding.devdocs.json @@ -242,7 +242,7 @@ "label": "guidedOnboardingApi", "description": [], "signature": [ - "ApiService", + "GuidedOnboardingApi", " | undefined" ], "path": "src/plugins/guided_onboarding/public/types.ts", diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 501baf7e6ff08..bfda154275b28 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index c1b2e8296cdc5..70e3654e91bfd 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-10-05 +date: 2022-10-06 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 787594cb2780d..8f1b666a80453 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-10-05 +date: 2022-10-06 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 9b5cf1de6cc18..cba3862d95d89 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-10-05 +date: 2022-10-06 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 6cbd7d1bc704b..61074d16d2ced 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-10-05 +date: 2022-10-06 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 4b06101a450c2..cb51aba7f2373 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-10-05 +date: 2022-10-06 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 eef5d014adb64..5a35131468ca2 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-10-05 +date: 2022-10-06 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 dc74d335defa9..6a275a46e7838 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-10-05 +date: 2022-10-06 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 c48356b123bb9..121ac90868b8a 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-10-05 +date: 2022-10-06 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 0d126e506e041..9f24723d941b5 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-10-05 +date: 2022-10-06 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 3b079931a286b..c5813b3057953 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-10-05 +date: 2022-10-06 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 08657991c2baf..98a90c24a3b9c 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-10-05 +date: 2022-10-06 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 4990225b69984..85bb5b3416f94 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-10-05 +date: 2022-10-06 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 5747971cb00f4..87f82dc11043c 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-10-05 +date: 2022-10-06 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 52e3cd8d3d712..28beb0ee61025 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-10-05 +date: 2022-10-06 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 aa9dcaf8e5a8a..5bf617a8923c8 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-10-05 +date: 2022-10-06 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 22e8f5322d119..6fb409bc10bfb 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-10-05 +date: 2022-10-06 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 a587fa5adde38..cb944dac35f65 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-10-05 +date: 2022-10-06 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 5680aa7ab9099..bf423b6db5c1c 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-10-05 +date: 2022-10-06 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 9217c2db0b335..d1f3cb0c2c14a 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-10-05 +date: 2022-10-06 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 4feead48e7fa0..fc517e3cf37c8 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-10-05 +date: 2022-10-06 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 dee2e3ea3a946..273797b541c23 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-10-05 +date: 2022-10-06 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 db9cc025d771d..0a6ac6e29bad6 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-10-05 +date: 2022-10-06 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 7599baf0a2fd7..8103b5738c6fa 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-10-05 +date: 2022-10-06 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 0a442417598ae..e825df3c46462 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-10-05 +date: 2022-10-06 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 8d971553826bc..41a2a4ed84e91 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-10-05 +date: 2022-10-06 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 4baae348047de..84e6351219af4 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-10-05 +date: 2022-10-06 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 58c92c671a577..d336ce4cc2268 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-10-05 +date: 2022-10-06 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 e3bbeedaaf3e0..3567a1afbc3c2 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-10-05 +date: 2022-10-06 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 7b09cad80cafc..d7833d02e1cfc 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index dcd9422837277..21ac5fc7aaf0c 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index ca8c80539ae42..c78d87e1aeeea 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-10-05 +date: 2022-10-06 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 be187441df102..5d171564a7abe 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-10-05 +date: 2022-10-06 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 fde3402d33d10..2fc6ceee4621f 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-10-05 +date: 2022-10-06 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 b402060a03c4c..7724103c05455 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-10-05 +date: 2022-10-06 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 e6e169ed5754d..4e0077433a2f3 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-10-05 +date: 2022-10-06 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 aecc7764088b5..612f56b417235 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-10-05 +date: 2022-10-06 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 031fc8e24e327..a30cf4db34d8a 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-10-05 +date: 2022-10-06 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 039a5742259bd..936092d30b255 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-10-05 +date: 2022-10-06 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 3e4fc51cefe6f..75a8e890239e3 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-10-05 +date: 2022-10-06 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 fcc1f47368a3d..ed06fa661c889 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 5908ce3a085c2..9c82389ccd66e 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index f5daf5f2e7483..c8acf0374937e 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 39eee9c14db25..698ec225e76fa 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 07033f9df1be6..a751549127507 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index a72958f47ac69..463654ac33aec 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-10-05 +date: 2022-10-06 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 8dba48177fb94..c846ff5981aac 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-10-05 +date: 2022-10-06 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 b8e5e03e1a724..c74c5f2d80f4a 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-10-05 +date: 2022-10-06 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 4da58098ca881..eb488d9d5170f 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-10-05 +date: 2022-10-06 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 2018325c894d1..5b86020001585 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-10-05 +date: 2022-10-06 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 0f93aa1244868..96eca9794ef00 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-10-05 +date: 2022-10-06 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 4482b1e279db1..f0709da12a248 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-10-05 +date: 2022-10-06 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 01fe3d220674b..cc7ceb4cfad68 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-10-05 +date: 2022-10-06 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 9337c921a84d4..59471cc59be49 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-10-05 +date: 2022-10-06 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 31238d8edb46d..4893dcc427556 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-10-05 +date: 2022-10-06 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 f07140499f258..a15b15de52afe 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-10-05 +date: 2022-10-06 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 1603b8e30f71a..523fa46f4be13 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-10-05 +date: 2022-10-06 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 47ca13abdaf8d..236a34150737b 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-10-05 +date: 2022-10-06 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 bb66821f8f2ab..25244e072a891 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-10-05 +date: 2022-10-06 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 4a4acb83b2806..19f3278d21947 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-10-05 +date: 2022-10-06 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 0170fbd0767f2..076140b90d41e 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-10-05 +date: 2022-10-06 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 019144200626d..eb1229f002cfc 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-10-05 +date: 2022-10-06 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 dca09e01ece90..2d22dfb843f82 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-10-05 +date: 2022-10-06 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 69f26abafd87b..7534a721a479f 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-10-05 +date: 2022-10-06 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 24968843f60ac..33eb96ac86934 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-10-05 +date: 2022-10-06 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 55162155f697e..7924bcd56c7d4 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-10-05 +date: 2022-10-06 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 292713cb04bb8..8d2a1aec28763 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-10-05 +date: 2022-10-06 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 00a59e0e24544..f7cefc24902ee 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-10-05 +date: 2022-10-06 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 cd03bcd9fb082..be6543933f307 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-10-05 +date: 2022-10-06 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 2ac0d725245e9..2ba99dd1aab20 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-10-05 +date: 2022-10-06 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 056dc9266a1f3..2f4903c7dde0e 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-10-05 +date: 2022-10-06 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 f40c592352e94..ea3c8e52207de 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-10-05 +date: 2022-10-06 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 11a3e61b73a69..d34b1627cafbc 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-10-05 +date: 2022-10-06 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 0da72ed909f31..73aa85945d91c 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-10-05 +date: 2022-10-06 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 1d48aa892d8d0..054a4ac5ba881 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-10-05 +date: 2022-10-06 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 6bf0de5c79e90..9ad9451b9960b 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-10-05 +date: 2022-10-06 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 acd208ff29b1a..b1e313346d29d 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-10-05 +date: 2022-10-06 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 3b9083ed02243..1e5210ca45b9c 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-10-05 +date: 2022-10-06 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 9e6d7159913cc..daa1f2e3cb044 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-10-05 +date: 2022-10-06 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 4f89c54cbba54..a064f8fd86b7e 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-10-05 +date: 2022-10-06 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 eca76f9786023..41c88086aeeb2 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-10-05 +date: 2022-10-06 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 b02f2b3a80b7d..acbbfe8d61a5d 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-10-05 +date: 2022-10-06 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 20edfc0fcb21c..1ed43940640b9 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-10-05 +date: 2022-10-06 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 c4e2a702ae71e..276d38ea534b5 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-10-05 +date: 2022-10-06 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 f7323b631488a..92d65732000df 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-10-05 +date: 2022-10-06 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 93e453bd35280..30f91a01b5c12 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index cd64292c7ac10..c0a010854a4a3 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 2ec8b01595c10..8411cd1c66ed6 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 5b1e449c4bd16..b918bc6d6b705 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 8c6772f5d2086..a1250845e2b0b 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index dc748666814fa..5ed760565043c 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-10-05 +date: 2022-10-06 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 3c50670643e2b..f1fe516abbd34 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-10-05 +date: 2022-10-06 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 6812449ae6cb4..aa3be72111768 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-10-05 +date: 2022-10-06 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 a3836501fb9f0..6bbe24eabfcd4 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-10-05 +date: 2022-10-06 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 0626c95396b57..658135433521f 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-10-05 +date: 2022-10-06 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 2f622a1cdf777..022dd32ee2f58 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-10-05 +date: 2022-10-06 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 c9da896e2365d..0f0453271be2d 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-10-05 +date: 2022-10-06 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 83208af11f648..93f957f1bff0f 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-10-05 +date: 2022-10-06 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 e94023817708f..dc3bb6db57760 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-10-05 +date: 2022-10-06 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 f6d8de55e9309..06ebfa4d3e362 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-10-05 +date: 2022-10-06 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 32ace0b77e78d..2f6827a5b0d22 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-10-05 +date: 2022-10-06 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 8efc2882116ba..cd821cb9e7553 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-10-05 +date: 2022-10-06 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 7967d20ecc60c..1c977c5107c9a 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-10-05 +date: 2022-10-06 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 b6e4c05837b09..c2bdd99be6230 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 3124f455cd0e8..c8edafa6020d9 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index b637381bbab33..b408b3b53eb4b 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index e77c917db42b6..95b863bf59896 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-10-05 +date: 2022-10-06 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 fae2156306337..70027166bc5cf 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-10-05 +date: 2022-10-06 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 1b5d6fa626276..cda1d082783fd 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-10-05 +date: 2022-10-06 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 28da4db687f1e..dab15edff1d0f 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-10-05 +date: 2022-10-06 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 2138d1f7af0f1..fc2ab0af80d08 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-10-05 +date: 2022-10-06 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 39072586e1ad1..d51ee1a48372e 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-10-05 +date: 2022-10-06 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 2989cf29ffd71..576df1b888eff 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-10-05 +date: 2022-10-06 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 54253f0aa8dfa..27f2773bcaf1e 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-10-05 +date: 2022-10-06 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 3b428fe0f742a..fe5c4df3d7e9d 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-10-05 +date: 2022-10-06 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 0654c35c6b428..fc5ddf823aec6 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-10-05 +date: 2022-10-06 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 74a7ede56fc50..d7fa4b1bd0cf0 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-10-05 +date: 2022-10-06 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 d813c0ac3c367..2f443063be9ba 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-10-05 +date: 2022-10-06 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 c7f4548eed742..5bcb7bca3ba2f 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-10-05 +date: 2022-10-06 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 0f13016c29166..cbda3f8cfd60d 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-10-05 +date: 2022-10-06 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 f4734047a9203..c64ab9e8eb2c0 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-10-05 +date: 2022-10-06 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 f9b76ec4efed1..3d470003a022a 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-10-05 +date: 2022-10-06 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 dec813f5a021d..79cdb7893d28b 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-10-05 +date: 2022-10-06 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 2d9f25f950378..8df3ef3916550 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 9051c8f82bf58..3aee25fa6049f 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index f2dc8b6a06d8c..3f7f5ef6918a1 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 280c79f1e9cbe..941308dd87192 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-10-05 +date: 2022-10-06 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 5cf655af07db4..b3f234c67bbb8 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-10-05 +date: 2022-10-06 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 bd8201ff0acc4..977c8dce67961 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 3d2da5c1c10b3..859521cbf716f 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 6d8af3ae22560..02b09cf82fe8e 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index e041b9e448242..f800ab175a6aa 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-10-05 +date: 2022-10-06 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 50f12e5bb152c..86c81053a0d32 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-10-05 +date: 2022-10-06 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 610ff44695f5a..ae27ed5870cc3 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-10-05 +date: 2022-10-06 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 b78c5d9cf18b5..626fb644b7ce8 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-10-05 +date: 2022-10-06 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 db339c8f7e563..17e6c27821405 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-10-05 +date: 2022-10-06 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 c475c660d0352..d37f6015cd789 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-10-05 +date: 2022-10-06 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 dfcf13ebfc854..752f4433f21be 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-10-05 +date: 2022-10-06 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 2abba8e4311cf..dd7230fe80961 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-10-05 +date: 2022-10-06 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 e2456a2c00d8f..fd80b207a1786 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-10-05 +date: 2022-10-06 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 9bb082ea3cc1d..a54399a09eae8 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-10-05 +date: 2022-10-06 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 dab8065fb6aff..b4fbd9d1a266d 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-10-05 +date: 2022-10-06 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 81755b6cd955d..a56b29c91f96f 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-10-05 +date: 2022-10-06 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 115762085f359..f7dabede7c494 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-10-05 +date: 2022-10-06 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 980c34cd56e81..82b24420b5589 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-10-05 +date: 2022-10-06 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 fbff1b20e176b..0e9e1334e3e2f 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-10-05 +date: 2022-10-06 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 bb6f6197bba8d..b6210307125ac 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-10-05 +date: 2022-10-06 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 aa36c20a4a8a6..3ca33d3256634 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-10-05 +date: 2022-10-06 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 af264e9075b88..19a4351c31f18 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 8a3b8a4f6e189..d04df59d4f356 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 7ba1976190fe2..a96c2b54c908d 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index bd3ae6f94d5d2..2534a673f1684 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 5863aaa53cc02..9bb80d23dfd14 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 35567a34412cd..e11fca267daea 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 70def2d37d1c9..27b696df00b94 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-10-05 +date: 2022-10-06 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 9300cc8a05f1a..43899ddf76a4d 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-10-05 +date: 2022-10-06 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 0ab1267bb6f7e..b267833018328 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-10-05 +date: 2022-10-06 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 676150973883a..a4f551e1ce3e0 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-10-05 +date: 2022-10-06 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 3fe29918b68f1..db77500f1c43a 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-10-05 +date: 2022-10-06 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 61c2c8b9c95a5..8891328601ee3 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-10-05 +date: 2022-10-06 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 2341a0a7dca29..eb640e052fd6a 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-10-05 +date: 2022-10-06 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 0033542fcb0f2..d1c792aa36822 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-10-05 +date: 2022-10-06 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 b588f9bc78bc3..c179f33b8e35f 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index c7d8c231294e0..b5986a472e19b 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 17252dff17e67..6d931537f7dcb 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 3927277788a24..94f5096d515fe 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 998e0dd35a472..625fe34ba1d3e 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-10-05 +date: 2022-10-06 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 e1e8516762c5d..515733770b1e8 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-10-05 +date: 2022-10-06 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 d2003862d3a45..3b3221c6d8a78 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-10-05 +date: 2022-10-06 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 3fd437900c0fd..32aeb4662b309 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-10-05 +date: 2022-10-06 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 2e0a18af16910..5f61fcd38f75e 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-10-05 +date: 2022-10-06 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 e520bcff849a5..bc19402e70392 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-10-05 +date: 2022-10-06 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 95a89822660ad..f6b9c5ed522ff 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-10-05 +date: 2022-10-06 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 abe69da3764d4..a527246dbec89 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-10-05 +date: 2022-10-06 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 f361257cb78d3..a63ad446c17ee 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-10-05 +date: 2022-10-06 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 b35bdccf97c3a..c4bdbbc95f00b 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 0cf11ce01ad03..ce723e081565b 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-10-05 +date: 2022-10-06 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 b5c569c8fdfdc..da82c011247f8 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-10-05 +date: 2022-10-06 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 29de7d07c2c8c..254af794fe507 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-10-05 +date: 2022-10-06 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 617c3e7bfbc38..447de8fbc7849 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-10-05 +date: 2022-10-06 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 015ce2965495e..4d3187aee95d4 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index d84edf5ed8652..3bd7db808950c 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -765,6 +765,47 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.buildOrFilter", + "type": "Function", + "tags": [], + "label": "buildOrFilter", + "description": [ + "\nBuilds an OR filter. An OR filter is a filter with multiple sub-filters. Each sub-filter (FilterItem) represents a\ncondition." + ], + "signature": [ + "(filters: ", + "FilterItem", + "[]) => ", + "OrFilter" + ], + "path": "packages/kbn-es-query/src/filters/build_filters/or_filter.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.buildOrFilter.$1", + "type": "Array", + "tags": [], + "label": "filters", + "description": [ + "An array of OrFilterItem" + ], + "signature": [ + "FilterItem", + "[]" + ], + "path": "packages/kbn-es-query/src/filters/build_filters/or_filter.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.buildPhraseFilter", @@ -1108,7 +1149,7 @@ "section": "def-common.DataViewBase", "text": "DataViewBase" }, - "[] | undefined, { ignoreFilterIfFieldNotInIndex, nestedIgnoreUnmapped }?: ", + "[] | undefined, options?: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -1186,7 +1227,7 @@ "id": "def-common.buildQueryFromFilters.$3", "type": "Object", "tags": [], - "label": "{ ignoreFilterIfFieldNotInIndex = false, nestedIgnoreUnmapped }", + "label": "options", "description": [], "signature": [ { @@ -2699,6 +2740,53 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isOrFilter", + "type": "Function", + "tags": [], + "label": "isOrFilter", + "description": [], + "signature": [ + "(filter: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ") => boolean" + ], + "path": "packages/kbn-es-query/src/filters/build_filters/or_filter.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isOrFilter.$1", + "type": "Object", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + } + ], + "path": "packages/kbn-es-query/src/filters/build_filters/or_filter.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.isPhraseFilter", diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 888217872f50e..c88bc07842c40 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 222 | 1 | 168 | 12 | +| 226 | 1 | 170 | 14 | ## Common diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 5a6d9c5c312a8..aa722e6cc6b08 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 9f6b3e808168f..ef72cac235fa6 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-10-05 +date: 2022-10-06 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 d258d85a40bfc..b108ca610fad9 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-10-05 +date: 2022-10-06 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 3900861f2c755..0efd1824a65e9 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 2d6b86c049e5f..c57dd51b1b47f 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 6bcbaab77880b..d1779e5e76f60 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-10-05 +date: 2022-10-06 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 2bd00377ab4e6..8a977208e5ed1 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-10-05 +date: 2022-10-06 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 a0d9668e08c3a..ce31bb8e7e81b 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-10-05 +date: 2022-10-06 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 2b07f557f982c..f3a2ae7cad264 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-10-05 +date: 2022-10-06 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 796900c1993ca..01bf80f297c5b 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-10-05 +date: 2022-10-06 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 d3cb22025a19f..c5360084d04ca 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-10-05 +date: 2022-10-06 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 06eb0be4c6e9d..ef600c2c84921 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-10-05 +date: 2022-10-06 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 1033277498dc9..a9873980a6b27 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-10-05 +date: 2022-10-06 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 706f3b412c9f3..6ad1ed47c2664 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-10-05 +date: 2022-10-06 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 c0ace53be6aaf..db7c8e6564d95 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-10-05 +date: 2022-10-06 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 c73eb157c378d..0fd30b688ee50 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index d2d290fc39b59..55b5077c93e28 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 750f182c6ef85..4e945cee6f8ba 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 04f901658fc45..1ee3f785c0026 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-10-05 +date: 2022-10-06 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 dc4d2d0568599..df7c3a246c022 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-10-05 +date: 2022-10-06 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 e5cea6347ce72..b3c1a2d04da4c 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-10-05 +date: 2022-10-06 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 2fdb499fca0f3..f812905cbecef 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-10-05 +date: 2022-10-06 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 73b7bb5df8cf1..625e06d638c33 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-10-05 +date: 2022-10-06 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 2043b5375594e..373e22799a9aa 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-10-05 +date: 2022-10-06 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 4d5c227a16402..ea07ae8cee257 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-10-05 +date: 2022-10-06 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 99648371f5af9..523635c337647 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-10-05 +date: 2022-10-06 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 6cbecbcc5043e..9c2a6d08b5b61 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-10-05 +date: 2022-10-06 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 10c8c7e687ebb..bd61c610b0922 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 2360b8fb353da..efbe102abed17 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 4c74267825fb3..c2e86d4baede3 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-10-05 +date: 2022-10-06 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 34b2c2d08fe63..68dcedaac112f 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-10-05 +date: 2022-10-06 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 e773eaf724419..5b3a980639f36 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-10-05 +date: 2022-10-06 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 8ef2712421b4f..41e10f6dd959b 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-10-05 +date: 2022-10-06 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 46116743ad8c0..bec1923dad883 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-10-05 +date: 2022-10-06 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 8f6421906ff6e..8ec82d44f0d39 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-10-05 +date: 2022-10-06 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 d33a8812a1840..d1b78fa30591c 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-10-05 +date: 2022-10-06 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 69a8f7958e817..6defc3cd40a27 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 51f5421b03e84..2b4d40afc7485 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index d3f386924f2d5..40e8bc6d7eded 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-10-05 +date: 2022-10-06 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 85a3c3c328e4b..4bc6f2284b2aa 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index d77794d5d25bf..7ab03ed0421cd 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 5b3005777d046..a0ee497d7f8cb 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-10-05 +date: 2022-10-06 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 a40e0f93af4ba..e901b9d066c3a 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index a403b0c4f8864..df181121e0239 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 2ccd4e1300150..06e747627a86a 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index fae80cea239e4..b412fd5209b24 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.devdocs.json b/api_docs/kbn_securitysolution_list_utils.devdocs.json index 0518a331879b0..39f11e812f589 100644 --- a/api_docs/kbn_securitysolution_list_utils.devdocs.json +++ b/api_docs/kbn_securitysolution_list_utils.devdocs.json @@ -1514,7 +1514,7 @@ "label": "getNewExceptionItem", "description": [], "signature": [ - "({ listId, namespaceType, ruleName, }: { listId: string; namespaceType: \"single\" | \"agnostic\"; ruleName: string; }) => ", + "({ listId, namespaceType, ruleName, }: { listId: string | undefined; namespaceType: \"single\" | \"agnostic\" | undefined; ruleName: string; }) => ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", @@ -1545,6 +1545,9 @@ "tags": [], "label": "listId", "description": [], + "signature": [ + "string | undefined" + ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, "trackAdoption": false @@ -1557,7 +1560,7 @@ "label": "namespaceType", "description": [], "signature": [ - "\"single\" | \"agnostic\"" + "\"single\" | \"agnostic\" | undefined" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -2597,7 +2600,7 @@ "label": "CreateExceptionListItemBuilderSchema", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { 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: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"meta\" | \"entries\"> & { meta: { temporaryUuid: string; }; entries: ", + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"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: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"meta\" | \"entries\" | \"list_id\" | \"namespace_type\"> & { meta: { temporaryUuid: string; }; entries: ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", @@ -2605,7 +2608,7 @@ "section": "def-common.BuilderEntry", "text": "BuilderEntry" }, - "[]; }" + "[]; list_id: string | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; }" ], "path": "packages/kbn-securitysolution-list-utils/src/types/index.ts", "deprecated": false, @@ -2782,6 +2785,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-list-utils", + "id": "def-common.ExceptionsBuilderReturnExceptionItem", + "type": "Type", + "tags": [], + "label": "ExceptionsBuilderReturnExceptionItem", + "description": [], + "signature": [ + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"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; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { 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: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"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: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; list_id?: undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })" + ], + "path": "packages/kbn-securitysolution-list-utils/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-list-utils", "id": "def-common.SavedObjectType", diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index e1430dff27fde..33ff58bd8edf9 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 191 | 0 | 149 | 0 | +| 192 | 0 | 150 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 6bd16b2922798..90713bd098ba8 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-10-05 +date: 2022-10-06 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 9e05fc653df0d..289e5355b885e 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-10-05 +date: 2022-10-06 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 0524cc14c9daf..266252a68a52d 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-10-05 +date: 2022-10-06 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 bdccce178feb5..489dabf77dd29 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-10-05 +date: 2022-10-06 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 c3288ff3462c6..24ebc24d8defd 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-10-05 +date: 2022-10-06 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 0f74b1ed20d10..5e6ec0fcb325d 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 3680059e0bb32..b1e6c344b6df5 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.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 5d9114b7fcfba..ae651814e6dd3 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-10-05 +date: 2022-10-06 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 6dcc02bebe7d7..18bad88743d5c 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-10-05 +date: 2022-10-06 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 0b2e455cd53ad..898b6c22b69a3 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-10-05 +date: 2022-10-06 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 29ab4005784a0..1967303104336 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-10-05 +date: 2022-10-06 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 97ed5c53aa6af..efa0c2201e765 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-10-05 +date: 2022-10-06 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 bf6e90ee02971..fc7e236dee26b 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-10-05 +date: 2022-10-06 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 0efc3f3002fb5..6d862dd2fc16b 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-10-05 +date: 2022-10-06 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 9559da7fcfb86..9634fb64565c8 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-10-05 +date: 2022-10-06 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 99aafea368d36..c5e4b39ae9988 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-10-05 +date: 2022-10-06 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 4ca01157d26c9..9f71b5cddd6d8 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-10-05 +date: 2022-10-06 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 246f16ec79337..934331840296b 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-10-05 +date: 2022-10-06 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 5e05cce3b8175..a08be21071aab 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-10-05 +date: 2022-10-06 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 fa7c3beb61d7e..54998fc195f1f 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-10-05 +date: 2022-10-06 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 8f46c57d69795..cdb85ac3ba572 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-10-05 +date: 2022-10-06 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 db0f60967570f..2d36d428ed486 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-10-05 +date: 2022-10-06 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 30f73a9dcef06..f6f277283ae61 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-10-05 +date: 2022-10-06 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 4a0a6c7218240..d0f56cc9a61d1 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-10-05 +date: 2022-10-06 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 1970330771198..f22cf1b967820 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-10-05 +date: 2022-10-06 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.mdx b/api_docs/kbn_shared_ux_router.mdx index ed08b8695873c..2b8daa788df8f 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index fe9ba7be266a7..6932fdb8c3515 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 7c5d6434bacdc..6f13a8ee498e4 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-10-05 +date: 2022-10-06 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 6d5b6b30ea6b2..fa0caf396263c 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-10-05 +date: 2022-10-06 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 bdbb8f89733e3..2a43bfb51834f 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-10-05 +date: 2022-10-06 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 1f56256dca604..402053e89565f 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-10-05 +date: 2022-10-06 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 564d494c01828..36f400c24b25c 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-10-05 +date: 2022-10-06 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 75218e1b9152c..33fea0c1e2299 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-10-05 +date: 2022-10-06 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 9263c4ae7239a..2eba5dc5be3ef 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-10-05 +date: 2022-10-06 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 a174ae9aa31d0..526b6c000b2a3 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-10-05 +date: 2022-10-06 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 c4e7d23f9ee8e..f324f70d512f0 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index b615096d6327f..d4ba7c7fc7970 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 5581ffbbe5e4e..0c9933569010c 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index a87f8c0d8c905..188ea7259e9d9 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index d2410615264b1..00a3f5284a161 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-10-05 +date: 2022-10-06 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 f2475d6a211a5..2e7665c6d95d8 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-10-05 +date: 2022-10-06 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 8e1879e083fd8..2b008c185e4ae 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-10-05 +date: 2022-10-06 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 3587d0729d9be..bf7280adef9c2 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-10-05 +date: 2022-10-06 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 2012b6d492ced..50ea7f126932a 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-10-05 +date: 2022-10-06 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 e836f6a148f7a..e313a22cdc174 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-10-05 +date: 2022-10-06 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 bf64118f48879..a78ffe7183c1a 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-10-05 +date: 2022-10-06 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 654d05d169d87..2ced8c278fc30 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-10-05 +date: 2022-10-06 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 4c7692f9641e5..5ae4fd4168333 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-10-05 +date: 2022-10-06 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 aec610de5a909..2bfab48ac58d5 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-10-05 +date: 2022-10-06 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 f5d8e050ab351..560988f43a860 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-10-05 +date: 2022-10-06 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 902fc381fca9a..4d8e7f8aba762 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-10-05 +date: 2022-10-06 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 d4d841a294ecc..02e87f5da1e0d 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-10-05 +date: 2022-10-06 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 af1ecf8a23441..7e6d321227846 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index d857708fd3b14..d88df8ee0e66a 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 667d7d1f9e97a..898eb33a80df7 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-10-05 +date: 2022-10-06 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 68df9af402ddd..67093dd6223c2 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-10-05 +date: 2022-10-06 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 90c4ae29e02ba..157e914e90819 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-10-05 +date: 2022-10-06 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 40c17f8b9f3ad..83be2ee9706c9 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -288,7 +288,8 @@ "label": "exceptionItems", "description": [], "signature": [ - "({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"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; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { 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: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" + "ExceptionsBuilderReturnExceptionItem", + "[]" ], "path": "x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx", "deprecated": false, diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 3eaa890dd2ef5..c5437eaa00fbd 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-10-05 +date: 2022-10-06 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 45768ee0f0e60..ab152e1e97ced 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-10-05 +date: 2022-10-06 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 f9c4152861ba4..937982b21ad25 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-10-05 +date: 2022-10-06 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 82db425319f63..e191c22670b83 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-10-05 +date: 2022-10-06 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 5941b34b4d159..73db15fb02a71 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-10-05 +date: 2022-10-06 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 3eb96b9e42def..a70f6e14d3582 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-10-05 +date: 2022-10-06 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 ead1822bd9168..e6e99fc09ca31 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-10-05 +date: 2022-10-06 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 9e694151645cb..a3dbc653112bc 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-10-05 +date: 2022-10-06 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 0846ce9a14b00..ef9b746fa56f5 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 6f51a9c520db6..dcab9933d2546 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 5150acfd6948e..d37f4c7b44ec7 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index a40f1999b7d1d..1dc8a44fa3580 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,13 +21,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 32135 | 179 | 21634 | 1018 | +| 32149 | 179 | 21640 | 1023 | ## Plugin Directory | Plugin name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 216 | 0 | 211 | 21 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 213 | 0 | 208 | 23 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 1 | 32 | 2 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 9 | 0 | 0 | 2 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 379 | 0 | 370 | 24 | @@ -50,8 +50,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 104 | 0 | 85 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 120 | 0 | 113 | 3 | | | [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. | 3213 | 33 | 2509 | 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) | 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. | 3221 | 33 | 2513 | 24 | +| | [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 | 16 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 60 | 0 | 30 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 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. | 983 | 0 | 225 | 2 | @@ -159,7 +159,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 133 | 0 | 92 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 206 | 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 | 61 | 0 | 59 | 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. | 128 | 2 | 102 | 17 | +| | [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. | 131 | 2 | 104 | 17 | | 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 | @@ -359,7 +359,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 19 | 0 | 11 | 0 | | | [Owner missing] | - | 27 | 0 | 14 | 1 | | | Kibana Core | - | 7 | 0 | 3 | 0 | -| | [Owner missing] | - | 222 | 1 | 168 | 12 | +| | [Owner missing] | - | 226 | 1 | 170 | 14 | | | Kibana Core | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 2 | 0 | 1 | 0 | | | [Owner missing] | - | 20 | 0 | 16 | 0 | @@ -406,7 +406,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | security solution list REST API | 67 | 0 | 64 | 0 | | | [Owner missing] | security solution list constants to use across plugins such lists, security_solution, cases, etc... | 33 | 0 | 17 | 0 | | | [Owner missing] | Security solution list ReactJS hooks | 58 | 0 | 47 | 0 | -| | [Owner missing] | security solution list utilities | 191 | 0 | 149 | 0 | +| | [Owner missing] | security solution list utilities | 192 | 0 | 150 | 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 | | | [Owner missing] | security solution utilities to use across plugins such lists, security_solution, cases, etc... | 31 | 0 | 29 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index eefd191f746eb..9bc47b3c02bfc 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.devdocs.json b/api_docs/profiling.devdocs.json index 1b5e38396b43d..59280e0c4a8ac 100644 --- a/api_docs/profiling.devdocs.json +++ b/api_docs/profiling.devdocs.json @@ -88,7 +88,7 @@ "label": "getRoutePaths", "description": [], "signature": [ - "() => { TopN: string; TopNContainers: string; TopNDeployments: string; TopNFunctions: string; TopNHosts: string; TopNThreads: string; TopNTraces: string; Flamechart: string; FrameInformation: string; }" + "() => { TopN: string; TopNContainers: string; TopNDeployments: string; TopNFunctions: string; TopNHosts: string; TopNThreads: string; TopNTraces: string; Flamechart: string; }" ], "path": "x-pack/plugins/profiling/common/index.ts", "deprecated": false, diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 1e04dc70632f5..a6a6cb32f36ee 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index c3a90a8c57d98..8cd446176d3d8 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-10-05 +date: 2022-10-06 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 c5ffba829e4f5..a1caaaad0e96d 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-10-05 +date: 2022-10-06 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 77b244443d8c1..646302af04ddb 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-10-05 +date: 2022-10-06 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 d8008df18edfc..8ee1694bc58ce 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-10-05 +date: 2022-10-06 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 12b365bcf6e47..59fd615fb3a4b 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-10-05 +date: 2022-10-06 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 4c74b770eccf2..2b7c4c60248f1 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-10-05 +date: 2022-10-06 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 55f7dc92e0763..5464cc8db2630 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-10-05 +date: 2022-10-06 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 3c8f636d7d5bd..e0b5d9503784e 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-10-05 +date: 2022-10-06 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 ab3dfead98715..ed7e95fe0e92d 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index f35fbaf25ca17..5f0468a0de970 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index f6f7fc886f121..7bf07eb018ac7 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-10-05 +date: 2022-10-06 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 8cba4be59b070..597fd54b3dec8 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-10-05 +date: 2022-10-06 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 11bcf322960e0..3e9f91503875a 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-10-05 +date: 2022-10-06 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 528330493abc2..f940b54c45b18 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-10-05 +date: 2022-10-06 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 9203fe6baafd2..3c11615878b52 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-10-05 +date: 2022-10-06 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 2cd8840a5b055..a225d173ca6b8 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-10-05 +date: 2022-10-06 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 3cf815ff9941d..9a8e85d0820fa 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-10-05 +date: 2022-10-06 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 10c69137d75c2..73c5b3f07a22d 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-10-05 +date: 2022-10-06 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 b9066eeb4629e..32b4747415dea 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-10-05 +date: 2022-10-06 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 4ff5487016b1b..eb9d2bd017aaf 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 47e5c2cbeb224..ba5acea2b2d08 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2022-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 29f4ab12cf09d..cf9bb2e32ea0c 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-10-05 +date: 2022-10-06 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 59cc40b520d98..f83770436f045 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-10-05 +date: 2022-10-06 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 ebe073a2dad05..bf8d0f7a6f946 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-10-05 +date: 2022-10-06 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 57d30d39c34f2..2d6b0cc3acc76 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-10-05 +date: 2022-10-06 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 e6e1e05a79c3b..9e82ce2d35784 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-10-05 +date: 2022-10-06 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 044def8490993..ae68ef29be000 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 81b0b1789f9e7..260389adca054 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-10-05 +date: 2022-10-06 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 8ee424cb6ead8..b973897f80fbb 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-10-05 +date: 2022-10-06 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 553179f8aed54..d0d49f39223d6 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-10-05 +date: 2022-10-06 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 39d505237f646..1b0567664a9fc 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-10-05 +date: 2022-10-06 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 e57f9446d9aff..99549d8c1e530 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 7d965fcc4ddcb..245e4c107d5b9 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 5678db92f00cd..69a6b1dece26f 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, onCreateDefaultAdHocDataView, isDisabled, }: ", + "({ isMissingCurrent, currentDataViewId, adHocDataViews, onChangeDataView, onEditDataView, onAddField, onDataViewCreated, trigger, selectableProps, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, onCreateDefaultAdHocDataView, 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 onCreateDefaultAdHocDataView,\n isDisabled,\n}", + "label": "{\n isMissingCurrent,\n currentDataViewId,\n adHocDataViews,\n onChangeDataView,\n onEditDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n onCreateDefaultAdHocDataView,\n isDisabled,\n}", "description": [], "signature": [ "DataViewPickerPropsExtended" @@ -553,6 +553,54 @@ ], "returnComment": [] }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.DataViewPickerProps.onEditDataView", + "type": "Function", + "tags": [], + "label": "onEditDataView", + "description": [ + "\nCallback that is called when the user edits the current data view via flyout.\nThe first parameter is the updated data view stub without fetched fields" + ], + "signature": [ + "((updatedDataViewStub: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + }, + ") => void) | undefined" + ], + "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.DataViewPickerProps.onEditDataView.$1", + "type": "Object", + "tags": [], + "label": "updatedDataViewStub", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + } + ], + "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "unifiedSearch", "id": "def-public.DataViewPickerProps.currentDataViewId", @@ -1110,6 +1158,26 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.IUnifiedSearchPluginServices.dataViewEditor", + "type": "Object", + "tags": [], + "label": "dataViewEditor", + "description": [], + "signature": [ + { + "pluginId": "dataViewEditor", + "scope": "public", + "docId": "kibDataViewEditorPluginApi", + "section": "def-public.PluginStart", + "text": "PluginStart" + } + ], + "path": "src/plugins/unified_search/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "unifiedSearch", "id": "def-public.IUnifiedSearchPluginServices.usageCollection", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index a88860acd6c96..7ab3c665f986f 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-10-05 +date: 2022-10-06 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 | |-------------------|-----------|------------------------|-----------------| -| 128 | 2 | 102 | 17 | +| 131 | 2 | 104 | 17 | ## Client diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 989827e3c0062..a34f0ec3eda4b 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-10-05 +date: 2022-10-06 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 | |-------------------|-----------|------------------------|-----------------| -| 128 | 2 | 102 | 17 | +| 131 | 2 | 104 | 17 | ## Client diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index c67daad53c29b..ac0bdd4b2b004 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-10-05 +date: 2022-10-06 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 b32c487d41333..d48cba9114566 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-10-05 +date: 2022-10-06 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 3c2dd48543e5d..c305ed9b2bf0a 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-10-05 +date: 2022-10-06 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 daeeb706148a5..295bab786c7d5 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-10-05 +date: 2022-10-06 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 44e60c7dfb71e..84e7e03c74643 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-10-05 +date: 2022-10-06 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 aece34dc1143a..0dc49ef6c3392 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-10-05 +date: 2022-10-06 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 8d3d8bb8e6fdb..37c247520e550 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-10-05 +date: 2022-10-06 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 28ec9d3db8f1c..52857476db6e0 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-10-05 +date: 2022-10-06 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 c9936e081013f..dff81b44b8dd1 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-10-05 +date: 2022-10-06 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 1dc3162b3cf59..a16d5ea13e4bf 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-10-05 +date: 2022-10-06 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 0870b882f64e7..249b503d192aa 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-10-05 +date: 2022-10-06 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 f53c14cd2fb3f..246443fccfef5 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-10-05 +date: 2022-10-06 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 6e6a3a1e58b50..0ac6f081a6579 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 9711621cbf742..194f3ebf958f5 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-10-05 +date: 2022-10-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/api-generated/README.md b/docs/api-generated/README.md new file mode 100644 index 0000000000000..4ce033e47ea0b --- /dev/null +++ b/docs/api-generated/README.md @@ -0,0 +1,36 @@ +# OpenAPI (Experimental) + +Open API specifications (OAS) exist in JSON or YAML format for some Kibana features, +though they are experimental and may be incomplete or change later. + +A preview of the API specifications can be added to the Kibana Guide by using +the following process: + +. Install [OpenAPI Generator](https://openapi-generator.tech/docs/installation), +or a similar tool that can generate HTML output from OAS. + +. Optionally validate the specifications by using the commands listed in the appropriate readmes. + +. Generate HTML output. For example: + + ``` + openapi-generator-cli generate -g html -i ~/kibana/x-pack/plugins/cases/docs/openapi/entrypoint.yaml -o ~/kibana/docs/api-generated/cases -t ~/kibana/docs/api-generated/template + + openapi-generator-cli generate -g html -i ~/kibana/x-pack/plugins/ml/common/openapi/ml_apis_v3.yaml -o ~/kibana/docs/api-generated/machine-learning -t ~/kibana/docs/api-generated/template + ``` + +. Rename the output files. For example: + ``` + mv ~/kibana/docs/api-generated/cases/index.html case-apis-passthru.asciidoc + mv ~/kibana/docs/api-generated/machine-learning/index.html ml-apis-passthru.adoc + ``` + +. If you're creating a new set of API output, you will need to have a page that incorporates the output by using passthrough blocks. For more information, refer to [Asciidoctor docs](https://docs.asciidoctor.org/asciidoc/latest/pass/pass-block/) + +. Verify the output by building the Kibana documentation. At this time, the output is added as a technical preview in the appendix. + +## Known issues + +- Some OAS 3.0 features such as `anyOf`, `oneOf`, and `allOf` might not display properly in the preview. These are on the [Short-term roadmap](https://openapi-generator.tech/docs/roadmap/) at this time. + + diff --git a/docs/api-generated/cases/case-apis-passthru.asciidoc b/docs/api-generated/cases/case-apis-passthru.asciidoc new file mode 100644 index 0000000000000..2a07283aa98e0 --- /dev/null +++ b/docs/api-generated/cases/case-apis-passthru.asciidoc @@ -0,0 +1,827 @@ +//// +This content is generated from the open API specification. +Any modifications made to this file will be overwritten. +//// + +++++ +
+

Access

+
    +
  1. APIKey KeyParamName:ApiKey KeyInQuery:false KeyInHeader:true
  2. +
  3. HTTP Basic Authentication
  4. +
+ +

Methods

+ [ Jump to Models ] + +

Table of Contents

+
+

Cases

+ + +

Cases

+
+
+ Up +
post /s/{spaceId}/api/cases/{caseId}/comments
+
Adds a comment or alert to a case. (addCaseComment)
+
You must have all privileges for the Cases feature in the Management, Observability, or Security section of the Kibana feature privileges, depending on the owner of the case you're creating.
+ +

Path parameters

+
+
caseId (required)
+ +
Path Parameter — The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. default: null
spaceId (required)
+ +
Path Parameter — An identifier for the space. If /s/ and the identifier are omitted from the path, the default space is used. default: null
+
+ +

Consumes

+ This API call consumes the following media types via the Content-Type request header: +
    +
  • application/json
  • +
+ +

Request body

+
+
add_case_comment_request add_case_comment_request (required)
+ +
Body Parameter
+ +
+ +

Request headers

+
+
kbn-xsrf (required)
+ +
Header Parameter — default: null
+ +
+ + + +

Return type

+ + + + +

Example data

+
Content-Type: application/json
+
{
+  "owner" : "cases",
+  "totalComment" : 0,
+  "settings" : {
+    "syncAlerts" : true
+  },
+  "totalAlerts" : 0,
+  "closed_at" : "2000-01-23T04:56:07.000+00:00",
+  "comments" : [ null, null ],
+  "created_at" : "2022-05-13T09:16:17.416Z",
+  "description" : "A case description.",
+  "title" : "Case title 1",
+  "created_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "version" : "WzUzMiwxXQ==",
+  "closed_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "tags" : [ "tag-1" ],
+  "duration" : 120,
+  "connector" : {
+    "name" : "none",
+    "id" : "none",
+    "fields" : {
+      "destIp" : "destIp",
+      "severity" : "severity",
+      "parent" : "parent",
+      "impact" : "impact",
+      "malwareUrl" : "malwareUrl",
+      "priority" : "priority",
+      "issueTypes" : [ 0.8008281904610115, 0.8008281904610115 ],
+      "issueType" : "issueType",
+      "sourceIp" : "sourceIp",
+      "urgency" : "urgency",
+      "malwareHash" : "malwareHash",
+      "caseId" : "caseId",
+      "severityCode" : 6.027456183070403,
+      "category" : "category",
+      "subcategory" : "subcategory"
+    },
+    "type" : ".none"
+  },
+  "updated_at" : "2000-01-23T04:56:07.000+00:00",
+  "updated_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "id" : "66b9aa00-94fa-11ea-9f74-e7e108796192",
+  "external_service" : {
+    "external_title" : "external_title",
+    "pushed_by" : {
+      "full_name" : "full_name",
+      "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+      "email" : "email",
+      "username" : "elastic"
+    },
+    "external_url" : "external_url",
+    "pushed_at" : "2000-01-23T04:56:07.000+00:00",
+    "connector_id" : "connector_id",
+    "external_id" : "external_id",
+    "connector_name" : "connector_name"
+  }
+}
+ +

Produces

+ This API call produces the following media types according to the Accept request header; + the media type will be conveyed by the Content-Type response header. +
    +
  • application/json
  • +
+ +

Responses

+

200

+ Indicates a successful call. + case_response_properties +
+
+
+
+ Up +
delete /s/{spaceId}/api/cases/{caseId}/comments
+
Deletes all comments and alerts from a case. (deleteCaseComments)
+
You must have all privileges for the Cases feature in the Management, Observability, or Security section of the Kibana feature privileges, depending on the owner of the cases you're deleting.
+ +

Path parameters

+
+
caseId (required)
+ +
Path Parameter — The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. default: null
spaceId (required)
+ +
Path Parameter — An identifier for the space. If /s/ and the identifier are omitted from the path, the default space is used. default: null
+
+ + + +

Request headers

+
+
kbn-xsrf (required)
+ +
Header Parameter — default: null
+ +
+ + + + + + + + +

Responses

+

204

+ Indicates a successful call. + +
+
+
+
+ Up +
get /s/{spaceId}/api/cases/{caseId}/comments
+
Retrieves all the comments from a case. (getAllCaseComments)
+
You must have read privileges for the Cases feature in the Management, Observability, or Security section of the Kibana feature privileges, depending on the owner of the cases with the comments you're seeking.
+ +

Path parameters

+
+
caseId (required)
+ +
Path Parameter — The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. default: null
spaceId (required)
+ +
Path Parameter — An identifier for the space. If /s/ and the identifier are omitted from the path, the default space is used. default: null
+
+ + + + + + +

Return type

+ + + + +

Example data

+
Content-Type: application/json
+
{
+  "owner" : "cases",
+  "totalComment" : 0,
+  "settings" : {
+    "syncAlerts" : true
+  },
+  "totalAlerts" : 0,
+  "closed_at" : "2000-01-23T04:56:07.000+00:00",
+  "comments" : [ null, null ],
+  "created_at" : "2022-05-13T09:16:17.416Z",
+  "description" : "A case description.",
+  "title" : "Case title 1",
+  "created_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "version" : "WzUzMiwxXQ==",
+  "closed_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "tags" : [ "tag-1" ],
+  "duration" : 120,
+  "connector" : {
+    "name" : "none",
+    "id" : "none",
+    "fields" : {
+      "destIp" : "destIp",
+      "severity" : "severity",
+      "parent" : "parent",
+      "impact" : "impact",
+      "malwareUrl" : "malwareUrl",
+      "priority" : "priority",
+      "issueTypes" : [ 0.8008281904610115, 0.8008281904610115 ],
+      "issueType" : "issueType",
+      "sourceIp" : "sourceIp",
+      "urgency" : "urgency",
+      "malwareHash" : "malwareHash",
+      "caseId" : "caseId",
+      "severityCode" : 6.027456183070403,
+      "category" : "category",
+      "subcategory" : "subcategory"
+    },
+    "type" : ".none"
+  },
+  "updated_at" : "2000-01-23T04:56:07.000+00:00",
+  "updated_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "id" : "66b9aa00-94fa-11ea-9f74-e7e108796192",
+  "external_service" : {
+    "external_title" : "external_title",
+    "pushed_by" : {
+      "full_name" : "full_name",
+      "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+      "email" : "email",
+      "username" : "elastic"
+    },
+    "external_url" : "external_url",
+    "pushed_at" : "2000-01-23T04:56:07.000+00:00",
+    "connector_id" : "connector_id",
+    "external_id" : "external_id",
+    "connector_name" : "connector_name"
+  }
+}
+ +

Produces

+ This API call produces the following media types according to the Accept request header; + the media type will be conveyed by the Content-Type response header. +
    +
  • application/json
  • +
+ +

Responses

+

200

+ Indicates a successful call. + case_response_properties +
+
+
+
+ Up +
patch /s/{spaceId}/api/cases/{caseId}/comments
+
Updates a comment or alert in a case. (updateCaseComment)
+
You must have all privileges for the Cases feature in the Management, Observability, or Security section of the Kibana feature privileges, depending on the owner of the case you're updating. NOTE: You cannot change the comment type or the owner of a comment.
+ +

Path parameters

+
+
caseId (required)
+ +
Path Parameter — The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. default: null
spaceId (required)
+ +
Path Parameter — An identifier for the space. If /s/ and the identifier are omitted from the path, the default space is used. default: null
+
+ +

Consumes

+ This API call consumes the following media types via the Content-Type request header: +
    +
  • application/json
  • +
+ +

Request body

+
+
update_case_comment_request update_case_comment_request (required)
+ +
Body Parameter
+ +
+ +

Request headers

+
+
kbn-xsrf (required)
+ +
Header Parameter — default: null
+ +
+ + + +

Return type

+ + + + +

Example data

+
Content-Type: application/json
+
{
+  "owner" : "cases",
+  "totalComment" : 0,
+  "settings" : {
+    "syncAlerts" : true
+  },
+  "totalAlerts" : 0,
+  "closed_at" : "2000-01-23T04:56:07.000+00:00",
+  "comments" : [ null, null ],
+  "created_at" : "2022-05-13T09:16:17.416Z",
+  "description" : "A case description.",
+  "title" : "Case title 1",
+  "created_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "version" : "WzUzMiwxXQ==",
+  "closed_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "tags" : [ "tag-1" ],
+  "duration" : 120,
+  "connector" : {
+    "name" : "none",
+    "id" : "none",
+    "fields" : {
+      "destIp" : "destIp",
+      "severity" : "severity",
+      "parent" : "parent",
+      "impact" : "impact",
+      "malwareUrl" : "malwareUrl",
+      "priority" : "priority",
+      "issueTypes" : [ 0.8008281904610115, 0.8008281904610115 ],
+      "issueType" : "issueType",
+      "sourceIp" : "sourceIp",
+      "urgency" : "urgency",
+      "malwareHash" : "malwareHash",
+      "caseId" : "caseId",
+      "severityCode" : 6.027456183070403,
+      "category" : "category",
+      "subcategory" : "subcategory"
+    },
+    "type" : ".none"
+  },
+  "updated_at" : "2000-01-23T04:56:07.000+00:00",
+  "updated_by" : {
+    "full_name" : "full_name",
+    "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+    "email" : "email",
+    "username" : "elastic"
+  },
+  "id" : "66b9aa00-94fa-11ea-9f74-e7e108796192",
+  "external_service" : {
+    "external_title" : "external_title",
+    "pushed_by" : {
+      "full_name" : "full_name",
+      "profile_uid" : "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
+      "email" : "email",
+      "username" : "elastic"
+    },
+    "external_url" : "external_url",
+    "pushed_at" : "2000-01-23T04:56:07.000+00:00",
+    "connector_id" : "connector_id",
+    "external_id" : "external_id",
+    "connector_name" : "connector_name"
+  }
+}
+ +

Produces

+ This API call produces the following media types according to the Accept request header; + the media type will be conveyed by the Content-Type response header. +
    +
  • application/json
  • +
+ +

Responses

+

200

+ Indicates a successful call. + case_response_properties +
+
+ +

Models

+ [ Jump to Methods ] + +

Table of Contents

+
    +
  1. Case_response_properties_for_comments_inner -
  2. +
  3. Case_response_properties_for_connectors - Case response properties for connectors
  4. +
  5. add_alert_comment_request_properties - Add case comment request properties for alerts
  6. +
  7. add_case_comment_request - Add case comment request
  8. +
  9. add_user_comment_request_properties - Add case comment request properties for user comments
  10. +
  11. alert_comment_response_properties - Add case comment response properties for alerts
  12. +
  13. alert_comment_response_properties_created_by -
  14. +
  15. alert_comment_response_properties_pushed_by -
  16. +
  17. alert_comment_response_properties_rule -
  18. +
  19. alert_identifiers - Alert identifiers
  20. +
  21. alert_indices - Alert indices
  22. +
  23. case_response_closed_by_properties - Case response properties for closed_by
  24. +
  25. case_response_connector_field_properties - Case response properties for connector fields
  26. +
  27. case_response_created_by_properties - Case response properties for created_by
  28. +
  29. case_response_properties - Case response properties
  30. +
  31. case_response_pushed_by_properties - Case response properties for pushed_by
  32. +
  33. case_response_updated_by_properties - Case response properties for updated_by
  34. +
  35. connector_types -
  36. +
  37. external_service -
  38. +
  39. owners -
  40. +
  41. rule - Alerting rule
  42. +
  43. settings -
  44. +
  45. severity_property -
  46. +
  47. status -
  48. +
  49. update_alert_comment_request_properties - Update case comment request properties for alerts
  50. +
  51. update_case_comment_request - Update case comment request
  52. +
  53. update_user_comment_request_properties - Update case comment request properties for user comments
  54. +
  55. user_comment_response_properties - Case response properties for user comments
  56. +
+ +
+

Case_response_properties_for_comments_inner - Up

+
+
+
alertId (optional)
+
created_at (optional)
Date format: date-time
+
created_by (optional)
+
id (optional)
+
index (optional)
+
owner (optional)
+
pushed_at (optional)
Date format: date-time
+
pushed_by (optional)
+
rule (optional)
+
type
+
Enum:
+
user
+
updated_at (optional)
Date format: date-time
+
updated_by (optional)
+
version (optional)
+
comment (optional)
+
+
+
+

Case_response_properties_for_connectors - Case response properties for connectors Up

+
+
+
fields (optional)
+
id (optional)
String The identifier for the connector. To create a case without a connector, use none.
+
name (optional)
String The name of the connector. To create a case without a connector, use none.
+
type (optional)
+
+
+
+

add_alert_comment_request_properties - Add case comment request properties for alerts Up

+
Defines properties for case comment requests when type is alert.
+
+
alertId
+
index
+
owner
+
rule
+
type
String The type of comment.
+
Enum:
+
alert
+
+
+
+

add_case_comment_request - Add case comment request Up

+
The add comment to case API request body varies depending on whether you are adding an alert or a comment.
+
+
alertId
+
index
+
owner
+
rule
+
type
String The type of comment.
+
Enum:
+
user
+
comment
String The new comment. It is required only when type is user.
+
+
+
+

add_user_comment_request_properties - Add case comment request properties for user comments Up

+
Defines properties for case comment requests when type is user.
+
+
comment
String The new comment. It is required only when type is user.
+
owner
+
type
String The type of comment.
+
Enum:
+
user
+
+
+
+

alert_comment_response_properties - Add case comment response properties for alerts Up

+
+
+
alertId (optional)
+
created_at (optional)
Date format: date-time
+
created_by (optional)
+
id (optional)
+
index (optional)
+
owner (optional)
+
pushed_at (optional)
Date format: date-time
+
pushed_by (optional)
+
rule (optional)
+
type
+
Enum:
+
alert
+
updated_at (optional)
Date format: date-time
+
updated_by (optional)
+
version (optional)
+
+
+
+

alert_comment_response_properties_created_by - Up

+
+
+
email (optional)
+
full_name (optional)
+
username (optional)
+
profile_uid (optional)
+
+
+
+

alert_comment_response_properties_pushed_by - Up

+
+
+
email (optional)
+
full_name (optional)
+
username (optional)
+
profile_uid (optional)
+
+
+
+

alert_comment_response_properties_rule - Up

+
+
+
id (optional)
String The rule identifier.
+
name (optional)
String The rule name.
+
+
+
+

alert_identifiers - Alert identifiers Up

+
The alert identifier. It is required only when type is alert. If it is an array, index must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+
+
+
+
+

alert_indices - Alert indices Up

+
The alert index. It is required only when type is alert. If it is an array, alertId must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+
+
+
+
+

case_response_closed_by_properties - Case response properties for closed_by Up

+
+
+
email
+
full_name
+
username
+
profile_uid (optional)
+
+
+
+

case_response_connector_field_properties - Case response properties for connector fields Up

+
An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.
+
+
caseId (optional)
String The case identifier for Swimlane connectors.
+
category (optional)
String The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.
+
destIp (optional)
String A comma-separated list of destination IPs for ServiceNow SecOps connectors.
+
impact (optional)
String The effect an incident had on business for ServiceNow ITSM connectors.
+
issueType (optional)
String The type of issue for Jira connectors.
+
issueTypes (optional)
array[BigDecimal] The type of incident for IBM Resilient connectors.
+
malwareHash (optional)
String A comma-separated list of malware hashes for ServiceNow SecOps connectors.
+
malwareUrl (optional)
String A comma-separated list of malware URLs for ServiceNow SecOps connectors.
+
parent (optional)
String The key of the parent issue, when the issue type is sub-task for Jira connectors.
+
priority (optional)
String The priority of the issue for Jira and ServiceNow SecOps connectors.
+
severity (optional)
String The severity of the incident for ServiceNow ITSM connectors.
+
severityCode (optional)
BigDecimal The severity code of the incident for IBM Resilient connectors.
+
sourceIp (optional)
String A comma-separated list of source IPs for ServiceNow SecOps connectors.
+
subcategory (optional)
String The subcategory of the incident for ServiceNow ITSM connectors.
+
urgency (optional)
String The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.
+
+
+
+

case_response_created_by_properties - Case response properties for created_by Up

+
+
+
email
+
full_name
+
username
+
profile_uid (optional)
+
+
+
+

case_response_properties - Case response properties Up

+
+
+
closed_at
Date format: date-time
+
closed_by
+
comments
array[Case_response_properties_for_comments_inner] An array of comment objects for the case.
+
connector
+
created_at
Date format: date-time
+
created_by
+
description
+
duration
Integer The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero.
+
external_service
+
id
+
owner
+
settings
+
severity
+
status
+
tags
+
title
+
totalAlerts
+
totalComment
+
updated_at
Date format: date-time
+
updated_by
+
version
+
+
+
+

case_response_pushed_by_properties - Case response properties for pushed_by Up

+
+
+
email
+
full_name
+
username
+
profile_uid (optional)
+
+
+
+

case_response_updated_by_properties - Case response properties for updated_by Up

+
+
+
email
+
full_name
+
username
+
profile_uid (optional)
+
+
+
+

connector_types - Up

+
The type of connector.
+
+
+
+
+

external_service - Up

+
+
+
connector_id (optional)
+
connector_name (optional)
+
external_id (optional)
+
external_title (optional)
+
external_url (optional)
+
pushed_at (optional)
Date format: date-time
+
pushed_by (optional)
+
+
+
+

owners - Up

+
The application that owns the cases: Stack Management, Observability, or Elastic Security.
+
+
+
+
+

rule - Alerting rule Up

+
The rule that is associated with the alert. It is required only when type is alert. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+
+
id (optional)
String The rule identifier.
+
name (optional)
String The rule name.
+
+
+
+

settings - Up

+
An object that contains the case settings.
+
+
syncAlerts (optional)
Boolean Turns alert syncing on or off.
+
+
+
+

severity_property - Up

+
The severity of the case.
+
+
+
+
+

status - Up

+
The status of the case.
+
+
+
+
+

update_alert_comment_request_properties - Update case comment request properties for alerts Up

+
Defines properties for case comment requests when type is alert.
+
+
alertId
+
id
String The identifier for the comment. To retrieve comment IDs, use the get comments API.
+
index
+
owner
+
rule
+
type
String The type of comment.
+
Enum:
+
alert
+
version
String The current comment version. To retrieve version values, use the get comments API.
+
+
+
+

update_case_comment_request - Update case comment request Up

+
The update case comment API request body varies depending on whether you are updating an alert or a comment.
+
+
alertId
+
id
String The identifier for the comment. To retrieve comment IDs, use the get comments API.
+
index
+
owner
+
rule
+
type
String The type of comment.
+
Enum:
+
user
+
version
String The current comment version. To retrieve version values, use the get comments API.
+
comment
String The new comment. It is required only when type is user.
+
+
+
+

update_user_comment_request_properties - Update case comment request properties for user comments Up

+
Defines properties for case comment requests when type is user.
+
+
comment
String The new comment. It is required only when type is user.
+
id
String The identifier for the comment. To retrieve comment IDs, use the get comments API.
+
owner
+
type
String The type of comment.
+
Enum:
+
user
+
version
String The current comment version. To retrieve version values, use the get comments API.
+
+
+
+

user_comment_response_properties - Case response properties for user comments Up

+
+
+
comment (optional)
+
created_at (optional)
Date format: date-time
+
created_by (optional)
+
id (optional)
+
owner (optional)
+
pushed_at (optional)
Date format: date-time
+
pushed_by (optional)
+
type
+
Enum:
+
user
+
updated_at (optional)
Date format: date-time
+
updated_by (optional)
+
version (optional)
+
+
+
+++++ diff --git a/docs/api-generated/cases/case-apis.asciidoc b/docs/api-generated/cases/case-apis.asciidoc new file mode 100644 index 0000000000000..fdd9a941a58e6 --- /dev/null +++ b/docs/api-generated/cases/case-apis.asciidoc @@ -0,0 +1,10 @@ +[[case-apis]] +== Case APIs + +preview::[] + +//// +This file includes content that has been generated from https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/docs/openapi. Any modifications required must be done in that open API specification. +//// + +include::case-apis-passthru.asciidoc[] \ No newline at end of file diff --git a/docs/api/cases/cases-api-add-comment.asciidoc b/docs/api/cases/cases-api-add-comment.asciidoc index 618f4a5de8842..918f579f1c0de 100644 --- a/docs/api/cases/cases-api-add-comment.asciidoc +++ b/docs/api/cases/cases-api-add-comment.asciidoc @@ -6,6 +6,12 @@ Adds a comment or alert to a case. +[NOTE] +==== +For the most up-to-date API details, refer to the +{kib-repo}/tree/{branch}/x-pack/plugins/cases/docs/openapi[open API specification]. For a preview, check out <>. +==== + === {api-request-title} `POST :/api/cases//comments` diff --git a/docs/api/cases/cases-api-delete-comments.asciidoc b/docs/api/cases/cases-api-delete-comments.asciidoc index 50866cfbe85fd..130158bd021c2 100644 --- a/docs/api/cases/cases-api-delete-comments.asciidoc +++ b/docs/api/cases/cases-api-delete-comments.asciidoc @@ -6,6 +6,12 @@ Deletes one or all comments and alerts from a case. +[NOTE] +==== +For the most up-to-date API details, refer to the +{kib-repo}/tree/{branch}/x-pack/plugins/cases/docs/openapi[open API specification]. For a preview, check out <>. +==== + === {api-request-title} `DELETE :/api/cases//comments` diff --git a/docs/api/cases/cases-api-get-comments.asciidoc b/docs/api/cases/cases-api-get-comments.asciidoc index 58c4c32acfa15..5f7bb938f588a 100644 --- a/docs/api/cases/cases-api-get-comments.asciidoc +++ b/docs/api/cases/cases-api-get-comments.asciidoc @@ -6,6 +6,12 @@ Gets a comment or all comments for a case. +[NOTE] +==== +For the most up-to-date API details, refer to the +{kib-repo}/tree/{branch}/x-pack/plugins/cases/docs/openapi[open API specification]. For a preview, check out <>. +==== + === {api-request-title} `GET :/api/cases//comments/` diff --git a/docs/api/cases/cases-api-update-comment.asciidoc b/docs/api/cases/cases-api-update-comment.asciidoc index 127d434602f84..4f2e89a7997ea 100644 --- a/docs/api/cases/cases-api-update-comment.asciidoc +++ b/docs/api/cases/cases-api-update-comment.asciidoc @@ -6,6 +6,12 @@ Updates a comment or alert in a case. +[NOTE] +==== +For the most up-to-date API details, refer to the +{kib-repo}/tree/{branch}/x-pack/plugins/cases/docs/openapi[open API specification]. For a preview, check out <>. +==== + === {api-request-title} `PATCH :/api/cases//comments` diff --git a/docs/apis.asciidoc b/docs/apis.asciidoc index 8fb4caa7d3fca..8b07e58d4f8aa 100644 --- a/docs/apis.asciidoc +++ b/docs/apis.asciidoc @@ -11,4 +11,5 @@ version of the specification is 3.0. For more information, go to https://openapi -- +include::api-generated/cases/case-apis.asciidoc[] include::api-generated/machine-learning/ml-apis.asciidoc[] \ No newline at end of file diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index ce8c7fb2011b2..1e0788cb56768 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -762,7 +762,7 @@ POST /_security/role/apm_agent_key_user "cluster": ["manage_own_api_key"], "applications": [{ "application": "apm", - "privileges": ["event:write", "sourcemap:write", "config_agent:read"], + "privileges": ["event:write", "config_agent:read"], "resources": ["*"] }] } @@ -785,7 +785,6 @@ POST /_security/role/apm_agent_key_user - `event:write`. Required for ingesting agent events. - `config_agent:read`. Required for agents to read agent configuration remotely. - - `sourcemap:write`. Required for uploading sourcemaps. [[apm-agent-key-create-example]] ===== Example @@ -795,7 +794,7 @@ POST /_security/role/apm_agent_key_user POST /api/apm/agent_keys { "name": "apm-key", - "privileges": ["event:write", "config_agent:read", "sourcemap:write"] + "privileges": ["event:write", "config_agent:read"] } -------------------------------------------------- diff --git a/docs/user/alerting/rule-types/es-query.asciidoc b/docs/user/alerting/rule-types/es-query.asciidoc index 715fabc6fdc38..c838200e637e1 100644 --- a/docs/user/alerting/rule-types/es-query.asciidoc +++ b/docs/user/alerting/rule-types/es-query.asciidoc @@ -26,7 +26,7 @@ the *time window*. Size:: Specifies the number of documents to pass to the configured actions when the threshold condition is met. {es} query:: Specifies the ES DSL query. The number of documents that -match this query is evaluated against the threshold condition. Only the `query`, `fields` and `runtime_mappings` +match this query is evaluated against the threshold condition. Only the `query`, `fields`, `_source` and `runtime_mappings` fields are used, other DSL fields are not considered. Threshold:: Defines a threshold value and a comparison operator (`is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The diff --git a/fleet_packages.json b/fleet_packages.json index 4651e86287588..94d383ff9f6e6 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -37,6 +37,6 @@ }, { "name": "synthetics", - "version": "0.10.2" + "version": "0.10.3" } -] \ No newline at end of file +] diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index b9fd0eef45c10..14e27632c33f4 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -171,6 +171,9 @@ { "label": "Contributors Newsletters", "items": [ + { + "id": "kibSeptember2022ContributorNewsletter" + }, { "id": "kibAugust2022ContributorNewsletter" }, diff --git a/package.json b/package.json index ebadbc429e35a..06b02094d6031 100644 --- a/package.json +++ b/package.json @@ -295,6 +295,7 @@ "@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-test-helpers-so-type-serializer": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-so-type-serializer", "@kbn/core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser", "@kbn/core-theme-browser-internal": "link:bazel-bin/packages/core/theme/core-theme-browser-internal", "@kbn/core-theme-browser-mocks": "link:bazel-bin/packages/core/theme/core-theme-browser-mocks", @@ -444,6 +445,7 @@ "axios": "^0.27.2", "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", + "blurhash": "^2.0.1", "brace": "0.11.1", "byte-size": "^8.1.0", "canvg": "^3.0.9", @@ -661,13 +663,13 @@ }, "devDependencies": { "@apidevtools/swagger-parser": "^10.0.3", - "@babel/cli": "^7.18.10", - "@babel/core": "^7.19.1", + "@babel/cli": "^7.19.3", + "@babel/core": "^7.19.3", "@babel/eslint-parser": "^7.19.1", "@babel/eslint-plugin": "^7.19.1", - "@babel/generator": "^7.19.0", + "@babel/generator": "^7.19.3", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/parser": "^7.19.1", + "@babel/parser": "^7.19.3", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", @@ -675,12 +677,12 @@ "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-transform-runtime": "^7.19.1", - "@babel/preset-env": "^7.19.1", + "@babel/preset-env": "^7.19.3", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@babel/register": "^7.18.9", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0", + "@babel/traverse": "^7.19.3", + "@babel/types": "^7.19.3", "@bazel/ibazel": "^0.16.2", "@bazel/typescript": "4.6.2", "@cypress/code-coverage": "^3.10.0", @@ -1025,6 +1027,7 @@ "@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-test-helpers-so-type-serializer": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-so-type-serializer/npm_module_types", "@types/kbn__core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser/npm_module_types", "@types/kbn__core-theme-browser-internal": "link:bazel-bin/packages/core/theme/core-theme-browser-internal/npm_module_types", "@types/kbn__core-theme-browser-mocks": "link:bazel-bin/packages/core/theme/core-theme-browser-mocks/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 97b7064f4cd7f..aae46a111c053 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -160,6 +160,7 @@ filegroup( "//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/test-helpers/core-test-helpers-so-type-serializer:build", "//packages/core/theme/core-theme-browser:build", "//packages/core/theme/core-theme-browser-internal:build", "//packages/core/theme/core-theme-browser-mocks:build", @@ -500,6 +501,7 @@ filegroup( "//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/test-helpers/core-test-helpers-so-type-serializer:build_types", "//packages/core/theme/core-theme-browser:build_types", "//packages/core/theme/core-theme-browser-internal:build_types", "//packages/core/theme/core-theme-browser-mocks:build_types", diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/BUILD.bazel b/packages/core/test-helpers/core-test-helpers-so-type-serializer/BUILD.bazel new file mode 100644 index 0000000000000..714250a907fb4 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/BUILD.bazel @@ -0,0 +1,111 @@ +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-test-helpers-so-type-serializer" +PKG_REQUIRE_NAME = "@kbn/core-test-helpers-so-type-serializer" + +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//semver", + "//packages/kbn-std", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/semver", + "//packages/kbn-std:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-common:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-server: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/test-helpers/core-test-helpers-so-type-serializer/README.md b/packages/core/test-helpers/core-test-helpers-so-type-serializer/README.md new file mode 100644 index 0000000000000..d562272b96499 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/README.md @@ -0,0 +1,3 @@ +# @kbn/core-test-helpers-so-type-serializer + +Utility package for savedObjects integration tests. diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/index.ts b/packages/core/test-helpers/core-test-helpers-so-type-serializer/index.ts new file mode 100644 index 0000000000000..100acc28a2001 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/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 type { SavedObjectTypeMigrationInfo } from './src/extract_migration_info'; +export { extractMigrationInfo } from './src/extract_migration_info'; +export { getMigrationHash } from './src/get_migration_hash'; diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/jest.config.js b/packages/core/test-helpers/core-test-helpers-so-type-serializer/jest.config.js new file mode 100644 index 0000000000000..66287aba24c22 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/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/test-helpers/core-test-helpers-so-type-serializer'], +}; diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/kibana.jsonc b/packages/core/test-helpers/core-test-helpers-so-type-serializer/kibana.jsonc new file mode 100644 index 0000000000000..4a4a765bbc519 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-test-helpers-so-type-serializer", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/package.json b/packages/core/test-helpers/core-test-helpers-so-type-serializer/package.json new file mode 100644 index 0000000000000..95de844e70ed9 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-test-helpers-so-type-serializer", + "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/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.test.ts b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.test.ts new file mode 100644 index 0000000000000..47620aee90b3c --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.test.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { extractMigrationInfo } from './extract_migration_info'; + +const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'multiple', + mappings: { properties: {} }, + ...parts, +}); + +const dummyMigration = jest.fn(); +const dummySchema = schema.object({}); + +describe('extractMigrationInfo', () => { + describe('simple fields', () => { + it('returns the `name` from the SO type', () => { + const type = createType({ name: 'my-type' }); + const output = extractMigrationInfo(type); + expect(output.name).toEqual('my-type'); + }); + + it('returns the `namespaceType` from the SO type', () => { + const type = createType({ namespaceType: 'multiple-isolated' }); + const output = extractMigrationInfo(type); + expect(output.namespaceType).toEqual('multiple-isolated'); + }); + + it('returns the `convertToMultiNamespaceTypeVersion` from the SO type', () => { + const type = createType({ convertToMultiNamespaceTypeVersion: '6.6.6' }); + const output = extractMigrationInfo(type); + expect(output.convertToMultiNamespaceTypeVersion).toEqual('6.6.6'); + }); + + it('returns the `convertToAliasScript` from the SO type', () => { + const type = createType({ convertToAliasScript: 'some_value' }); + const output = extractMigrationInfo(type); + expect(output.convertToAliasScript).toEqual('some_value'); + }); + + it('returns true for `hasExcludeOnUpgrade` if the SO type specifies `excludeOnUpgrade`', () => { + expect( + extractMigrationInfo(createType({ excludeOnUpgrade: jest.fn() })).hasExcludeOnUpgrade + ).toEqual(true); + expect( + extractMigrationInfo(createType({ excludeOnUpgrade: undefined })).hasExcludeOnUpgrade + ).toEqual(false); + }); + }); + + describe('migrations', () => { + it('returns the versions with registered migrations, sorted asc', () => { + const type = createType({ + migrations: { + '8.3.3': dummyMigration, + '7.17.7': dummyMigration, + '8.0.2': dummyMigration, + }, + }); + + const output = extractMigrationInfo(type); + + expect(output.migrationVersions).toEqual(['7.17.7', '8.0.2', '8.3.3']); + }); + + it('supports migration provider functions', () => { + const type = createType({ + migrations: () => ({ + '8.3.3': dummyMigration, + '7.17.7': dummyMigration, + '8.0.2': dummyMigration, + }), + }); + + const output = extractMigrationInfo(type); + + expect(output.migrationVersions).toEqual(['7.17.7', '8.0.2', '8.3.3']); + }); + + it('returns an empty list when migrations are not defined', () => { + const type = createType({ + migrations: undefined, + }); + + const output = extractMigrationInfo(type); + + expect(output.migrationVersions).toEqual([]); + }); + }); + + describe('schemas', () => { + it('returns the versions with registered schemas, sorted asc', () => { + const type = createType({ + schemas: { + '8.3.2': dummySchema, + '7.15.2': dummySchema, + '8.1.2': dummySchema, + }, + }); + + const output = extractMigrationInfo(type); + + expect(output.schemaVersions).toEqual(['7.15.2', '8.1.2', '8.3.2']); + }); + + it('supports schema provider functions', () => { + const type = createType({ + schemas: () => ({ + '8.3.2': dummySchema, + '7.15.2': dummySchema, + '8.1.2': dummySchema, + }), + }); + + const output = extractMigrationInfo(type); + + expect(output.schemaVersions).toEqual(['7.15.2', '8.1.2', '8.3.2']); + }); + + it('returns an empty list when schemas are not defined', () => { + const type = createType({ + schemas: undefined, + }); + + const output = extractMigrationInfo(type); + + expect(output.schemaVersions).toEqual([]); + }); + }); + + describe('mappings', () => { + it('returns a flattened version of the mappings', () => { + const type = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + const output = extractMigrationInfo(type); + expect(output.mappings).toEqual({ + dynamic: false, + 'properties.description.type': 'text', + 'properties.hits.doc_values': false, + 'properties.hits.index': false, + 'properties.hits.type': 'integer', + }); + }); + }); +}); diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts new file mode 100644 index 0000000000000..c2f1c0b7aa9a2 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { compare as semverCompare } from 'semver'; +import { getFlattenedObject } from '@kbn/std'; +import type { SavedObjectsNamespaceType } from '@kbn/core-saved-objects-common'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; + +export interface SavedObjectTypeMigrationInfo { + name: string; + namespaceType: SavedObjectsNamespaceType; + convertToAliasScript?: string; + convertToMultiNamespaceTypeVersion?: string; + migrationVersions: string[]; + schemaVersions: string[]; + mappings: Record; + hasExcludeOnUpgrade: boolean; +} + +/** + * Extract all migration-relevant informations bound to given type in a serializable format. + * + * @param soType + */ +export const extractMigrationInfo = (soType: SavedObjectsType): SavedObjectTypeMigrationInfo => { + const migrationMap = + typeof soType.migrations === 'function' ? soType.migrations() : soType.migrations; + const migrationVersions = Object.keys(migrationMap ?? {}); + migrationVersions.sort(semverCompare); + + const schemaMap = typeof soType.schemas === 'function' ? soType.schemas() : soType.schemas; + const schemaVersions = Object.keys(schemaMap ?? {}); + schemaVersions.sort(semverCompare); + + return { + name: soType.name, + namespaceType: soType.namespaceType, + convertToAliasScript: soType.convertToAliasScript, + convertToMultiNamespaceTypeVersion: soType.convertToMultiNamespaceTypeVersion, + migrationVersions, + schemaVersions, + mappings: getFlattenedObject(soType.mappings ?? {}), + hasExcludeOnUpgrade: !!soType.excludeOnUpgrade, + }; +}; diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.test.ts b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.test.ts new file mode 100644 index 0000000000000..2ac9e04172e86 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.test.ts @@ -0,0 +1,336 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { getMigrationHash } from './get_migration_hash'; + +const createType = (parts: Partial = {}): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'multiple', + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + ...parts, +}); + +describe('getMigrationHash', () => { + it('returns the same hash for the exact same simple type', () => { + const type = createType(); + expect(getMigrationHash(type)).toEqual(getMigrationHash(type)); + }); + + describe('simple fields', () => { + it('returns different hashes if `name` changes', () => { + expect(getMigrationHash(createType({ name: 'typeA' }))).not.toEqual( + getMigrationHash(createType({ name: 'typeB' })) + ); + }); + it('returns different hashes if `namespaceType` changes', () => { + expect(getMigrationHash(createType({ namespaceType: 'single' }))).not.toEqual( + getMigrationHash(createType({ namespaceType: 'multiple' })) + ); + }); + it('returns different hashes if `convertToMultiNamespaceTypeVersion` changes', () => { + expect( + getMigrationHash(createType({ convertToMultiNamespaceTypeVersion: undefined })) + ).not.toEqual(getMigrationHash(createType({ convertToMultiNamespaceTypeVersion: '6.6.6' }))); + }); + it('returns different hashes if `convertToAliasScript` changes', () => { + expect(getMigrationHash(createType({ convertToAliasScript: undefined }))).not.toEqual( + getMigrationHash(createType({ convertToAliasScript: 'some_script' })) + ); + }); + it('returns different hashes if `excludeOnUpgrade` is defined or not', () => { + expect(getMigrationHash(createType({ excludeOnUpgrade: undefined }))).not.toEqual( + getMigrationHash(createType({ excludeOnUpgrade: jest.fn() })) + ); + }); + }); + + describe('migrations', () => { + it('returns same hash if same migration versions are registered', () => { + const typeA = createType({ + migrations: { + '7.17.1': jest.fn(), + '8.4.2': jest.fn(), + }, + }); + const typeB = createType({ + migrations: { + '7.17.1': jest.fn(), + '8.4.2': jest.fn(), + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns same hash if same migration versions are registered in different order', () => { + const typeA = createType({ + migrations: { + '9.1.3': jest.fn(), + '7.17.1': jest.fn(), + '8.4.2': jest.fn(), + }, + }); + const typeB = createType({ + migrations: { + '8.4.2': jest.fn(), + '9.1.3': jest.fn(), + '7.17.1': jest.fn(), + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns same hash if same migration versions are registered using record + function', () => { + const typeA = createType({ + migrations: { + '9.1.3': jest.fn(), + '7.17.1': jest.fn(), + '8.4.2': jest.fn(), + }, + }); + const typeB = createType({ + migrations: () => ({ + '8.4.2': jest.fn(), + '9.1.3': jest.fn(), + '7.17.1': jest.fn(), + }), + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns different hashes if different migration versions are registered', () => { + const typeA = createType({ + migrations: { + '7.17.1': jest.fn(), + '8.4.2': jest.fn(), + }, + }); + const typeB = createType({ + migrations: { + '7.17.69': jest.fn(), + '42.0.0': jest.fn(), + }, + }); + + expect(getMigrationHash(typeA)).not.toEqual(getMigrationHash(typeB)); + }); + }); + describe('schemas', () => { + it('returns same hash if same schema versions are registered', () => { + const typeA = createType({ + schemas: { + '7.17.1': schema.object({}), + '8.4.2': schema.object({}), + }, + }); + const typeB = createType({ + schemas: { + '7.17.1': schema.object({}), + '8.4.2': schema.object({}), + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns same hash if same schema versions are registered in different order', () => { + const typeA = createType({ + schemas: { + '9.1.3': schema.object({}), + '7.17.1': schema.object({}), + '8.4.2': schema.object({}), + }, + }); + const typeB = createType({ + schemas: { + '8.4.2': schema.object({}), + '9.1.3': schema.object({}), + '7.17.1': schema.object({}), + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns same hash if same schema versions are registered using record + function', () => { + const typeA = createType({ + schemas: { + '9.1.3': schema.object({}), + '7.17.1': schema.object({}), + '8.4.2': schema.object({}), + }, + }); + const typeB = createType({ + schemas: () => ({ + '8.4.2': schema.object({}), + '9.1.3': schema.object({}), + '7.17.1': schema.object({}), + }), + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns different hashes if different schema versions are registered', () => { + const typeA = createType({ + schemas: { + '7.17.1': schema.object({}), + '8.4.2': schema.object({}), + }, + }); + const typeB = createType({ + schemas: { + '7.17.69': schema.object({}), + '42.0.0': schema.object({}), + }, + }); + + expect(getMigrationHash(typeA)).not.toEqual(getMigrationHash(typeB)); + }); + }); + + describe('mappings', () => { + it('returns same hash for the same mappings', () => { + const typeA = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + const typeB = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns same hash for the same mappings in different order', () => { + const typeA = createType({ + mappings: { + dynamic: false, + properties: { + hits: { type: 'integer', index: false, doc_values: false }, + description: { type: 'text' }, + }, + }, + }); + const typeB = createType({ + mappings: { + properties: { + description: { type: 'text' }, + hits: { index: false, type: 'integer', doc_values: false }, + }, + dynamic: false, + }, + }); + + expect(getMigrationHash(typeA)).toEqual(getMigrationHash(typeB)); + }); + + it('returns different hashes for different mappings (removing nested property)', () => { + const typeA = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + const typeB = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', doc_values: false }, + }, + }, + }); + + expect(getMigrationHash(typeA)).not.toEqual(getMigrationHash(typeB)); + }); + + it('returns different hashes for different mappings (adding nested property)', () => { + const typeA = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + const typeB = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text', boost: 42 }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + + expect(getMigrationHash(typeA)).not.toEqual(getMigrationHash(typeB)); + }); + + it('returns different hashes for different mappings (removing top-level property)', () => { + const typeA = createType({ + mappings: { + dynamic: false, + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + const typeB = createType({ + mappings: { + properties: { + description: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + }, + }, + }); + + expect(getMigrationHash(typeA)).not.toEqual(getMigrationHash(typeB)); + }); + }); + + describe('ignored fields', () => { + it('returns same hash if `hidden` changes', () => { + expect(getMigrationHash(createType({ hidden: false }))).toEqual( + getMigrationHash(createType({ hidden: true })) + ); + }); + it('returns same hash if `management` changes', () => { + expect(getMigrationHash(createType({ management: undefined }))).toEqual( + getMigrationHash(createType({ management: { visibleInManagement: false } })) + ); + }); + }); +}); diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.ts b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.ts new file mode 100644 index 0000000000000..7e23ec35bb9e7 --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createHash } from 'crypto'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { extractMigrationInfo } from './extract_migration_info'; + +type SavedObjectTypeMigrationHash = string; + +export const getMigrationHash = (soType: SavedObjectsType): SavedObjectTypeMigrationHash => { + const migInfo = extractMigrationInfo(soType); + + const hash = createHash('sha1'); + + const hashParts = [ + migInfo.name, + migInfo.namespaceType, + migInfo.convertToAliasScript ?? 'none', + migInfo.hasExcludeOnUpgrade, + migInfo.convertToMultiNamespaceTypeVersion ?? 'none', + migInfo.migrationVersions.join(','), + migInfo.schemaVersions.join(','), + JSON.stringify(migInfo.mappings, Object.keys(migInfo.mappings).sort()), + ]; + const hashFeed = hashParts.join('-'); + + return hash.update(hashFeed).digest('hex'); +}; diff --git a/packages/core/test-helpers/core-test-helpers-so-type-serializer/tsconfig.json b/packages/core/test-helpers/core-test-helpers-so-type-serializer/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/test-helpers/core-test-helpers-so-type-serializer/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/kbn-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts b/packages/kbn-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts index 4f04feb841dd4..793f57a1a778c 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts @@ -18,6 +18,7 @@ export function getSpanDestinationMetrics(events: ApmFields[]) { 'service.environment', 'service.name', 'span.destination.service.resource', + 'span.name', ]); return metricsets.map((metricset) => { diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index c4139f6423fae..b7a3326942582 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -375,6 +375,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { es_connection: '', }, responseActions: `${SECURITY_SOLUTION_DOCS}response-actions.html`, + configureEndpointIntegrationPolicy: `${SECURITY_SOLUTION_DOCS}configure-endpoint-integration-policy.html`, }, query: { eql: `${ELASTICSEARCH_DOCS}eql.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 38bf25b0aba7e..16a80e7f7bf33 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -277,6 +277,7 @@ export interface DocLinks { }; readonly threatIntelInt: string; readonly responseActions: string; + readonly configureEndpointIntegrationPolicy: string; }; readonly query: { readonly eql: string; diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts index adbffa3c728ef..6f4bc7d51052f 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -138,13 +138,13 @@ export const getNewExceptionItem = ({ namespaceType, ruleName, }: { - listId: string; - namespaceType: NamespaceType; + listId: string | undefined; + namespaceType: NamespaceType | undefined; ruleName: string; }): CreateExceptionListItemBuilderSchema => { return { comments: [], - description: `${ruleName} - exception list item`, + description: 'Exception list item', entries: addIdToEntries([ { field: '', diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts index 24de9d1b1d756..13b62fac657a2 100644 --- a/packages/kbn-securitysolution-list-utils/src/types/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/types/index.ts @@ -9,6 +9,7 @@ import { DataViewFieldBase } from '@kbn/es-query'; import type { CreateExceptionListItemSchema, + CreateRuleExceptionListItemSchema, Entry, EntryExists, EntryMatch, @@ -18,6 +19,7 @@ import type { ExceptionListItemSchema, ListOperatorEnum as OperatorEnum, ListOperatorTypeEnum as OperatorTypeEnum, + NamespaceType, } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_NAMESPACE, @@ -93,16 +95,23 @@ export type ExceptionListItemBuilderSchema = Omit & { meta: { temporaryUuid: string }; entries: BuilderEntry[]; + list_id: string | undefined; + namespace_type: NamespaceType | undefined; }; export type ExceptionsBuilderExceptionItem = | ExceptionListItemBuilderSchema | CreateExceptionListItemBuilderSchema; +export type ExceptionsBuilderReturnExceptionItem = + | ExceptionListItemSchema + | CreateExceptionListItemSchema + | CreateRuleExceptionListItemSchema; + export const exceptionListSavedObjectType = EXCEPTION_LIST_NAMESPACE; export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGNOSTIC; export type SavedObjectType = diff --git a/src/core/server/docs/kib_core_reviewing_so_type_pr.mdx b/src/core/server/docs/kib_core_reviewing_so_type_pr.mdx new file mode 100644 index 0000000000000..95dc466109543 --- /dev/null +++ b/src/core/server/docs/kib_core_reviewing_so_type_pr.mdx @@ -0,0 +1,84 @@ +--- +id: kibCoreReviewingSoPr +slug: /kibana-dev-docs/review/reviewing-so-pr +title: Reviewing SavedObject PRs +description: How to review PRs that changes savedObjects registration +date: 2022-09-30 +tags: ['kibana','dev', 'contributor', 'api docs'] +--- + +# Reviewing PRs that change Saved Object types + +## How does automatic review assignment work when SO types are changed? + +PRs modifying / adding / deleting any SO type registration will be flagged by the integration +test located at `src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts`. + +This test will fail any time a change is performed on an SO type registration that could risk having an impact on +upgrades/migrations, and will force the PR's author to update the test's snapshot, which will trigger a review +from the Core team. + +## What and how to review? + +Reviews will be triggered when one, or more, of these scenarios occur: +- a new type was added (registered) +- a type was removed (is no longer registered) +- a new migration function was added +- a mapping change was performed +- a new validation schema was added +- an `excludeOnUpgrade` function was added or removed + +Note: reviews will **not** automatically be triggered in these scenarios: +- an existing migration function is changed +- an existing validation schema is changed +- an existing `excludeOnUpgrade` function is changed + +### A new type was added + +We have another integration test detecting this scenario (`src/core/server/integration_tests/saved_objects/migrations/type_registrations.test.ts`) + +In that scenario, we should: +- check the initial mappings of the type to spot potential issues: + - fields being defined explicitly in the mappings but not directly used for search + - overall amount of fields is high + - use of `dynamic: true` (this can lead to mapping explosions) +- check if the type is registered as `hidden: true` and encourage to do so otherwise. + - this avoids polluting the global SO HTTP APIs with another type, and instead requires plugin developers to build + their own HTTP APIs to access this type of SO if they truly need to. + +### A type was removed + +The integration test mentioned in the previous section also detects those scenarios. + +Here, we need to check: +- that `REMOVED_TYPES` (`packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts`) was properly updated + - (but, again, the integration test will fail otherwise) +- that no other existing SO types may be referencing this type + - can't really be automated, will likely need to ask the owning team directly + +### A new migration function was added + +- Review the migration function + - owners are supposed to do it, but an additional review never hurts + - make sure they are typing their migration function using `SavedObjectMigrationFn` and supplying arguments for the generics + e.g. `SavedObjectMigrationFn`. +- Make sure that the migration function is properly tested + - by unit tests + - and, ideally, by integration tests using 'real' data +- If the migration function is moving/creating/deleting/mutating fields, make sure that the type's mappings and/or schemas were updated accordingly +- Please refer to [the migration section of our testing docs](https://github.com/elastic/kibana/blob/main/dev_docs/tutorials/testing_plugins.mdx#L796) for more details + +### A mapping change was performed + +- Make sure a migration function was added to reflect the changes: + - If a field was removed, ensure the migration function always removes the field from all documents + - If a field type was changed from e.g. `text` to `long`, make sure the migration function guarantees that all documents have compatible fields + - If a text type was changed to a keyword, make sure the text won't exceed Elasticsearch's 32k keyword length limit by e.g. specifying: `ignore_above: 256` +- If the migration function is present, refer to previous section. +- If the type is registering validation schemas, make sure a new schema was added reflecting the changes to the model. + +### A new validation schema was added + +- Make sure the associated mapping changes were performed, and that a migration function was added accordingly. +- Ideally schemas are validated with unit tests as well, especially for more complex ones. +- Refer to prior sections to see what to check. \ No newline at end of file diff --git a/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts new file mode 100644 index 0000000000000..d7704db87476f --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; +import { getMigrationHash } from '@kbn/core-test-helpers-so-type-serializer'; +import { Root } from '../../../root'; +import * as kbnTestServer from '../../../../test_helpers/kbn_server'; + +describe('checking migration metadata changes on all registered SO types', () => { + let esServer: kbnTestServer.TestElasticsearchUtils; + let root: Root; + let typeRegistry: ISavedObjectTypeRegistry; + + beforeAll(async () => { + const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + }); + + esServer = await startES(); + root = kbnTestServer.createRootWithCorePlugins({}, { oss: false }); + await root.preboot(); + await root.setup(); + const coreStart = await root.start(); + typeRegistry = coreStart.savedObjects.getTypeRegistry(); + }); + + afterAll(async () => { + if (root) { + await root.shutdown(); + } + if (esServer) { + await esServer.stop(); + } + }); + + // This test is meant to fail when any change is made in registered types that could potentially impact the SO migration. + // Just update the snapshot by running this test file via jest_integration with `-u` and push the update. + // The intent is to trigger a code review from the Core team to review the SO type changes. + it('detecting migration related changes in registered types', () => { + const allTypes = typeRegistry.getAllTypes(); + + const hashMap = allTypes.reduce((map, type) => { + map[type.name] = getMigrationHash(type); + return map; + }, {} as Record); + + expect(hashMap).toMatchInlineSnapshot(` + Object { + "action": "7858e6d5a9f231bf23f6f2e57328eb0095b26735", + "action_task_params": "bbd38cbfd74bf6713586fe078e3fa92db2234299", + "alert": "48461f3375d9ba22882ea23a318b62a5b0921a9b", + "api_key_pending_invalidation": "9b4bc1235337da9a87ef05a1d1f4858b2a3b77c6", + "apm-indices": "ceb0870f3a74e2ffc3a1cd3a3c73af76baca0999", + "apm-server-schema": "2bfd2998d3873872e1366458ce553def85418f91", + "apm-service-group": "07ecbf25ee4828d2b686abc98656b6665831d1a0", + "apm-telemetry": "abaa1e9469e6e0bad76938309f0ac4c66b528d58", + "app_search_telemetry": "7fc4fc08852bf0924ee29942bb394fda9aa8954d", + "application_usage_daily": "6e645e0b60ef3af2e8fde80963c2a4f09a190d61", + "application_usage_totals": "b2af3577dcd50bfae492b166a7804f69e2cc41dc", + "canvas-element": "5f32b99ba6ff9c1f17cc093591b975be65a27b9b", + "canvas-workpad": "b60252414fb6159a14f9febf98dbe41e5a8bf199", + "canvas-workpad-template": "c371cad0a8d61385f4782cab9a9063d3cf241ee0", + "cases": "7ff5ce930146a2d6fc8fbf536ce2ee16e9df296f", + "cases-comments": "8cfbad4ede637305eb6fb79db680f03dc0ce5ec4", + "cases-configure": "1afc414f5563a36e4612fa269193d3ed7277c7bd", + "cases-connector-mappings": "4b16d440af966e5d6e0fa33368bfa15d987a4b69", + "cases-telemetry": "16e261e7378a72acd0806f18df92525dd1da4f37", + "cases-user-actions": "3973dfcaacbe6ae147d7331699cfc25d2a27ca30", + "config": "e3f0408976dbdd453641f5699927b28b188f6b8c", + "connector_token": "fa5301aa5a2914795d3b1b82d0a49939444009da", + "core-usage-stats": "f40a213da2c597b0de94e364a4326a5a1baa4ca9", + "csp-rule-template": "3679c5f2431da8153878db79c78a4e695357fb61", + "csp_rule": "d2bb53ea5d2bdfba1a835ad8956dfcd2b2c32e19", + "dashboard": "0b0842b6aa40c125d64233fd81cee11080580dc2", + "endpoint:user-artifact": "f94c250a52b30d0a2d32635f8b4c5bdabd1e25c0", + "endpoint:user-artifact-manifest": "8c14d49a385d5d1307d956aa743ec78de0b2be88", + "enterprise_search_telemetry": "fafcc8318528d34f721c42d1270787c52565bad5", + "epm-packages": "c4c39f20d6bcfff40994813ee0f2bab01d34b646", + "epm-packages-assets": "9fd3d6726ac77369249e9a973902c2cd615fc771", + "event_loop_delays_daily": "d2ed39cf669577d90921c176499908b4943fb7bd", + "exception-list": "fe8cc004fd2742177cdb9300f4a67689463faf9c", + "exception-list-agnostic": "49fae8fcd1967cc4be45ba2a2c66c4afbc1e341b", + "file": "280f28bd48b3ad1f1a9f84c6c0ae6dd5ed1179da", + "file-upload-usage-collection-telemetry": "8478924cf0057bd90df737155b364f98d05420a5", + "fileShare": "3f88784b041bb8728a7f40763a08981828799a75", + "fleet-preconfiguration-deletion-record": "7b28f200513c28ae774f1b7d7d7906954e3c6e16", + "graph-workspace": "3342f2cd561afdde8f42f5fb284bf550dee8ebb5", + "guided-onboarding-guide-state": "561db8d481b131a2bbf46b1e534d6ce960255135", + "index-pattern": "48e77ca393c254e93256f11a7cdc0232dd754c08", + "infrastructure-monitoring-log-view": "e2c78c1076bd35e57d7c5fa1b410e5c126d12327", + "infrastructure-ui-source": "7c8dbbc0a608911f1b683a944f4a65383f6153ed", + "ingest-agent-policies": "5d728f483dc3b14dcfa6bbad95c2024d2da68890", + "ingest-download-sources": "1e69dabd6db5e320fe08c5bda8f35f29bafc6b54", + "ingest-outputs": "29b867bf7bfd28b1e17c84697dce5c6d078f9705", + "ingest-package-policies": "e8707a8c7821ea085e67c2d213e24efa56307393", + "ingest_manager_settings": "bb71f20e36a9ac3a2e46d9345e2caa96e7bf8c22", + "inventory-view": "bc2bd1e7ec7c186159447ab228d269f22bd39056", + "kql-telemetry": "29544cd7d3b767c5399878efae6bd724d24c03fd", + "legacy-url-alias": "7172dfd54f2e0c89fe263fd7095519b2d826a930", + "lens": "08769c789ad6d1b8a4d0cffebc9d9bb08bf01ad9", + "lens-ui-telemetry": "df2844565c9e18fed2bdb1f6cc3aadd58cf1e45b", + "map": "00ca6c4cf46ae59f70f1436262eb9f457b45eb14", + "maps-telemetry": "5adbde35bd50ec2b8e9ea5b96d4d9f886e31ecfb", + "metrics-explorer-view": "09e56993352b8ee678e88f71e4410d9aeee72f3a", + "ml-job": "2836da98a81bd220db61c0549e8e28da7a876cb2", + "ml-module": "95055522c8406afa67a554690a43506f6c040744", + "ml-trained-model": "e39dd10b2da827e194ddcaaf3db141ad1daf0201", + "monitoring-telemetry": "af508cea8e22edaa909e462069390650fbbf01b7", + "osquery-manager-usage-metric": "fbe3cbea25a96e2ca522ca436878e0162c94dcc2", + "osquery-pack": "afb3b46c5e23fc24ad438e9c4317ff37e4e5164a", + "osquery-pack-asset": "32421669c87c49dfabd4d3957f044e5eb7f7fb20", + "osquery-saved-query": "7b213b4b7a3e59350e99c50e8df9948662ed493a", + "query": "4640ef356321500a678869f24117b7091a911cb6", + "sample-data-telemetry": "8b10336d9efae6f3d5593c4cc89fb4abcdf84e04", + "search": "e7ba25ea37cb36b622db42c9590c6d8dfc838801", + "search-session": "ba383309da68a15be3765977f7a44c84f0ec7964", + "search-telemetry": "beb3fc25488c753f2a6dcff1845d667558712b66", + "security-rule": "e0dfdba5d66139d0300723b2e6672993cd4a11f3", + "security-solution-signals-migration": "e65933e32926e0ca385415bd44fc6da0b6d3d419", + "siem-detection-engine-rule-actions": "d4b5934c0c0e4ccdf509a41000eb0bee07be0c28", + "siem-detection-engine-rule-execution-info": "b92d51db7b7d591758d3e85892a91064aff01ff8", + "siem-ui-timeline": "95474f10662802e2f9ea068b45bf69212a2f5842", + "siem-ui-timeline-note": "08c71dc0b8b8018a67e80beb4659a078404c223d", + "siem-ui-timeline-pinned-event": "e2697b38751506c7fce6e8b7207a830483dc4283", + "space": "c4a0acce1bd4b9cce85154f2a350624a53111c59", + "spaces-usage-stats": "922d3235bbf519e3fb3b260e27248b1df8249b79", + "synthetics-monitor": "cffb4dfe9e0a36755a226d5cf983c21aac2b5b1e", + "synthetics-privates-locations": "dd00385f4a27ef062c3e57312eeb3799872fa4af", + "tag": "39413f4578cc2128c9a0fda97d0acd1c8862c47a", + "task": "ef53d0f070bd54957b8fe22fae3b1ff208913f76", + "telemetry": "9142dc5f18123fb6e6a9083db04e5becbfde94fd", + "ui-metric": "2fb66ccdee2d1fad52547964421629c5a485c38f", + "upgrade-assistant-ml-upgrade-operation": "408120d386c04ab25fe64a03937597aa0438c10d", + "upgrade-assistant-reindex-operation": "d9e18b3d9578ecabf09a297296dcf7e36b2481fd", + "upgrade-assistant-telemetry": "a0c80933a9f8b50a2590d19e1d1e5f97d28f7104", + "uptime-dynamic-settings": "9de35c5aeaef915c5bc3c5b1632c33fb0f6f1c55", + "uptime-synthetics-api-key": "df9d8418ddc210d832a069a0fb796f73e63d1082", + "url": "d66c1f26ed23a392be3617a8444d713571f58380", + "usage-counters": "33e2081a52215293041da1100e6602fb553ff446", + "visualization": "f45d06858a5634c9ed0367e11eb44f7f7dde0be2", + "workplace_search_telemetry": "45bd03e12b060c08381b0fd325d939f80d08c914", + } + `); + }); +}); diff --git a/src/dev/build/tasks/download_cloud_dependencies.ts b/src/dev/build/tasks/download_cloud_dependencies.ts index 25730fc88bd08..266901afb67dc 100644 --- a/src/dev/build/tasks/download_cloud_dependencies.ts +++ b/src/dev/build/tasks/download_cloud_dependencies.ts @@ -9,6 +9,7 @@ import Path from 'path'; import del from 'del'; import Axios from 'axios'; +import Fsp from 'fs/promises'; import { Task, downloadToDisk, downloadToString } from '../lib'; export const DownloadCloudDependencies: Task = { @@ -38,18 +39,41 @@ export const DownloadCloudDependencies: Task = { return Promise.all(downloads); }; + const writeManifest = async (manifestUrl: string, manifestJSON: object) => { + const destination = config.resolveFromRepo('.beats', 'beats_manifest.json'); + return Fsp.writeFile( + destination, + JSON.stringify( + { + manifest_url: manifestUrl, + ...manifestJSON, + }, + null, + 2 + ) + ); + }; + let buildId = ''; + let manifestUrl = ''; + let manifestJSON = null; const buildUrl = `https://${subdomain}.elastic.co/beats/latest/${config.getBuildVersion()}.json`; try { const latest = await Axios.get(buildUrl); buildId = latest.data.build_id; + manifestUrl = latest.data.manifest_url; + manifestJSON = (await Axios.get(manifestUrl)).data; + if (!(manifestUrl && manifestJSON)) throw new Error('Missing manifest.'); } catch (e) { log.error(`Unable to find Beats artifacts for ${config.getBuildVersion()} at ${buildUrl}.`); throw e; } + await del([config.resolveFromRepo('.beats')]); await downloadBeat('metricbeat', buildId); await downloadBeat('filebeat', buildId); + + await writeManifest(manifestUrl, manifestJSON); }, }; diff --git a/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap index 9a7a7d5a5035c..defaca87fad1c 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap @@ -26,6 +26,7 @@ Object { "as": "legacyMetricVis", "type": "render", "value": Object { + "canNavigateToLens": false, "visConfig": Object { "dimensions": Object { "metrics": Array [ diff --git a/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/metric_vis_function.ts b/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/metric_vis_function.ts index 8ec638d139bff..a7d655107fc2d 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/metric_vis_function.ts +++ b/src/plugins/chart_expressions/expression_legacy_metric/common/expression_functions/metric_vis_function.ts @@ -200,6 +200,7 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ ...(args.bucket ? { bucket: args.bucket } : {}), }, }, + canNavigateToLens: Boolean(handlers?.variables?.canNavigateToLens), }, }; }, diff --git a/src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts index 0c7c0331975e8..f0a63b012dc0a 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts @@ -38,6 +38,7 @@ export interface MetricVisRenderConfig { visType: typeof visType; visData: Datatable; visConfig: Pick; + canNavigateToLens: boolean; } export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition< diff --git a/src/plugins/chart_expressions/expression_legacy_metric/public/__stories__/metric_renderer.stories.tsx b/src/plugins/chart_expressions/expression_legacy_metric/public/__stories__/metric_renderer.stories.tsx index 36fc6cd712f83..ba2835806f4f5 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/public/__stories__/metric_renderer.stories.tsx +++ b/src/plugins/chart_expressions/expression_legacy_metric/public/__stories__/metric_renderer.stories.tsx @@ -43,6 +43,7 @@ const style: MetricStyle = { }; const config: MetricVisRenderConfig = { + canNavigateToLens: false, visType, visData: { type: 'datatable', diff --git a/src/plugins/chart_expressions/expression_legacy_metric/public/expression_renderers/metric_vis_renderer.tsx b/src/plugins/chart_expressions/expression_legacy_metric/public/expression_renderers/metric_vis_renderer.tsx index 798bc62938ca1..456a7f9cbf8a4 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/public/expression_renderers/metric_vis_renderer.tsx +++ b/src/plugins/chart_expressions/expression_legacy_metric/public/expression_renderers/metric_vis_renderer.tsx @@ -67,7 +67,7 @@ export const getMetricVisRenderer: ( name: EXPRESSION_METRIC_NAME, displayName: 'metric visualization', reuseDomNode: true, - render: async (domNode, { visData, visConfig }, handlers) => { + render: async (domNode, { visData, visConfig, canNavigateToLens }, handlers) => { const { core, plugins } = getStartDeps(); handlers.onDestroy(() => { @@ -82,9 +82,12 @@ export const getMetricVisRenderer: ( const visualizationType = extractVisualizationType(executionContext); if (containerType && visualizationType) { - plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [ + const events = [ `render_${visualizationType}_legacy_metric`, - ]); + canNavigateToLens ? `render_${visualizationType}_legacy_metric_convertable` : undefined, + ].filter((event): event is string => Boolean(event)); + + plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, events); } handlers.done(); diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 036c77fc6257c..d8c66155d8b30 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -153,13 +153,17 @@ export class DashboardContainer extends Container { + this.controlGroup.untilReady().then(() => { + if (!this.controlGroup || isErrorEmbeddable(this.controlGroup)) return; + syncDashboardControlGroup({ + dashboardContainer: this, + controlGroup: this.controlGroup, + }).then((result) => { if (!result) return; const { onDestroyControlGroup } = result; this.onDestroyControlGroup = onDestroyControlGroup; - } - ); + }); + }); } this.subscriptions.add( diff --git a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts index 4f44d0cf250d1..edca78a373132 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts @@ -10,7 +10,7 @@ import _ from 'lodash'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; -import { debounceTime, distinctUntilChanged, distinctUntilKeyChanged } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, distinctUntilKeyChanged, skip } from 'rxjs/operators'; import { ControlGroupInput, @@ -140,11 +140,10 @@ export const syncDashboardControlGroup = async ({ .pipe( distinctUntilChanged(({ filters: filtersA }, { filters: filtersB }) => compareAllFilters(filtersA, filtersB) - ) + ), + skip(1) // skip first filter output because it will have been applied in initialize ) - .subscribe(() => { - dashboardContainer.updateInput({ lastReloadRequestTime: Date.now() }); - }) + .subscribe(() => dashboardContainer.updateInput({ lastReloadRequestTime: Date.now() })) ); subscriptions.add( diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index 37a5b2b1efca9..510719d6c266e 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -99,6 +99,8 @@ const IndexPatternEditorFlyoutContentComponent = ({ services: { application, http, dataViews, uiSettings, overlays }, } = useKibana(); + const canSave = dataViews.getCanSaveSync(); + const { form } = useForm({ // Prefill with data if editData exists defaultValue: { @@ -447,6 +449,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ isEdit={!!editData} isPersisted={Boolean(editData && editData.isPersisted())} allowAdHoc={allowAdHoc} + canSave={canSave} /> diff --git a/src/plugins/data_view_editor/public/components/footer/footer.tsx b/src/plugins/data_view_editor/public/components/footer/footer.tsx index f832173b7156e..024885e91d548 100644 --- a/src/plugins/data_view_editor/public/components/footer/footer.tsx +++ b/src/plugins/data_view_editor/public/components/footer/footer.tsx @@ -24,6 +24,7 @@ interface FooterProps { isEdit: boolean; isPersisted: boolean; allowAdHoc: boolean; + canSave: boolean; } const closeButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutCloseButtonLabel', { @@ -56,6 +57,7 @@ export const Footer = ({ isEdit, allowAdHoc, isPersisted, + canSave, }: FooterProps) => { const submitPersisted = () => { onSubmit(false); @@ -96,21 +98,23 @@ export const Footer = ({ )} - - - {isEdit - ? isPersisted - ? editButtonLabel - : editUnpersistedButtonLabel - : saveButtonLabel} - - + {(canSave || (isEdit && !isPersisted)) && ( + + + {isEdit + ? isPersisted + ? editButtonLabel + : editUnpersistedButtonLabel + : saveButtonLabel} + + + )} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index de3b30810859f..274cb85cc3535 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -182,7 +182,8 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) const { dataViewFieldEditor, dataViewEditor } = services; const { availableFields$ } = props; - const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); + const canEditDataView = + Boolean(dataViewEditor?.userPermissions.editDataView()) || !selectedDataView?.isPersisted(); useEffect( () => { @@ -241,25 +242,19 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) ] ); - const createNewDataView = useMemo( - () => - canEditDataView - ? () => { - const ref = dataViewEditor.openEditor({ - onSave: async (dataView) => { - onDataViewCreated(dataView); - }, - }); - if (setDataViewEditorRef) { - setDataViewEditorRef(ref); - } - if (closeFlyout) { - closeFlyout(); - } - } - : undefined, - [canEditDataView, dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated] - ); + const createNewDataView = useCallback(() => { + const ref = dataViewEditor.openEditor({ + onSave: async (dataView) => { + onDataViewCreated(dataView); + }, + }); + if (setDataViewEditorRef) { + setDataViewEditorRef(ref); + } + if (closeFlyout) { + closeFlyout(); + } + }, [dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated]); if (!selectedDataView) { return null; diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 82f3b1aadadbf..66f06169256ab 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -68,7 +68,8 @@ export const DiscoverTopNav = ({ const services = useDiscoverServices(); const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, dataViews } = services; - const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); + const canEditDataView = + Boolean(dataViewEditor?.userPermissions.editDataView()) || !dataView.isPersisted(); const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -124,22 +125,16 @@ export const DiscoverTopNav = ({ [editField, canEditDataView] ); - const createNewDataView = useMemo( - () => - canEditDataView - ? () => { - closeDataViewEditor.current = dataViewEditor.openEditor({ - onSave: async (dataViewToSave) => { - if (dataViewToSave.id) { - onChangeDataView(dataViewToSave.id); - } - }, - allowAdHocDataView: true, - }); - } - : undefined, - [canEditDataView, dataViewEditor, onChangeDataView] - ); + const createNewDataView = useCallback(() => { + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataViewToSave) => { + if (dataViewToSave.id) { + onChangeDataView(dataViewToSave.id); + } + }, + allowAdHocDataView: true, + }); + }, [dataViewEditor, onChangeDataView]); const onCreateDefaultAdHocDataView = useCallback( async (pattern: string) => { diff --git a/src/plugins/discover/public/components/discover_grid/build_edit_field_button.tsx b/src/plugins/discover/public/components/discover_grid/build_edit_field_button.tsx index 3d7ee2c52e37c..1d9eab7e1d8e4 100644 --- a/src/plugins/discover/public/components/discover_grid/build_edit_field_button.tsx +++ b/src/plugins/discover/public/components/discover_grid/build_edit_field_button.tsx @@ -29,7 +29,8 @@ export const buildEditFieldButton = ({ } const { canEdit: canEditField } = getFieldCapabilities(dataView, field); - const canEditDataView = Boolean(services.dataViewEditor?.userPermissions?.editDataView()); + const canEditDataView = + Boolean(services.dataViewEditor?.userPermissions?.editDataView()) || !dataView.isPersisted(); if (!canEditField || !canEditDataView) { return null; diff --git a/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx index 61f45ce84e3c3..1d479bd9b4c1c 100644 --- a/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx @@ -57,7 +57,9 @@ export function ReactExpressionRenderer({ return (
{isEmpty && } - {isLoading && } + {isLoading && ( + + )} {!isLoading && error && renderError?.(error.message, error)}
diff --git a/src/plugins/guided_onboarding/common/types.ts b/src/plugins/guided_onboarding/common/types.ts index 412154ede98b0..92cc3af1ff1dd 100644 --- a/src/plugins/guided_onboarding/common/types.ts +++ b/src/plugins/guided_onboarding/common/types.ts @@ -9,7 +9,7 @@ export type GuideId = 'observability' | 'security' | 'search'; export type ObservabilityStepIds = 'add_data' | 'view_dashboard' | 'tour_observability'; -export type SecurityStepIds = 'add_data' | 'rules' | 'alerts' | 'cases'; +export type SecurityStepIds = 'add_data' | 'rules' | 'alertsCases'; export type SearchStepIds = 'add_data' | 'browse_docs' | 'search_experience'; export type GuideStepIds = ObservabilityStepIds | SecurityStepIds | SearchStepIds; diff --git a/src/plugins/guided_onboarding/public/constants/guides_config/security.ts b/src/plugins/guided_onboarding/public/constants/guides_config/security.ts index 769d95f978edc..8eafa3b51c408 100644 --- a/src/plugins/guided_onboarding/public/constants/guides_config/security.ts +++ b/src/plugins/guided_onboarding/public/constants/guides_config/security.ts @@ -37,17 +37,8 @@ export const securityConfig: GuideConfig = { ], }, { - id: 'alerts', - title: 'View Alerts', - descriptionList: [ - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - 'Nullam ligula enim, malesuada a finibus vel, cursus sed risus.', - 'Vivamus pretium, elit dictum lacinia aliquet, libero nibh dictum enim, a rhoncus leo magna in sapien.', - ], - }, - { - id: 'cases', - title: 'Cases and investigations', + id: 'alertsCases', + title: 'Alerts and cases', descriptionList: [ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'Nullam ligula enim, malesuada a finibus vel, cursus sed risus.', diff --git a/src/plugins/guided_onboarding/public/mocks.tsx b/src/plugins/guided_onboarding/public/mocks.tsx index dff111fba67c8..dcac2cfc1c0fb 100644 --- a/src/plugins/guided_onboarding/public/mocks.tsx +++ b/src/plugins/guided_onboarding/public/mocks.tsx @@ -22,6 +22,7 @@ const apiServiceMock: jest.Mocked = { completeGuideStep: jest.fn(), isGuidedOnboardingActiveForIntegration$: () => new BehaviorSubject(false), completeGuidedOnboardingForIntegration: jest.fn(), + isGuidePanelOpen$: new BehaviorSubject(false), }, }; diff --git a/src/plugins/guided_onboarding/public/types.ts b/src/plugins/guided_onboarding/public/types.ts index dbc300bf0d43d..ba7271756cbb7 100755 --- a/src/plugins/guided_onboarding/public/types.ts +++ b/src/plugins/guided_onboarding/public/types.ts @@ -52,6 +52,7 @@ export interface GuidedOnboardingApi { completeGuidedOnboardingForIntegration: ( integration?: string ) => Promise<{ state: GuideState } | undefined>; + isGuidePanelOpen$: Observable; } export interface StepConfig { diff --git a/src/plugins/share/server/url_service/short_urls/short_url_client.test.ts b/src/plugins/share/server/url_service/short_urls/short_url_client.test.ts index fe6365d498628..4578f63ca1a6e 100644 --- a/src/plugins/share/server/url_service/short_urls/short_url_client.test.ts +++ b/src/plugins/share/server/url_service/short_urls/short_url_client.test.ts @@ -44,6 +44,15 @@ const setup = () => { }; }; +const tick = (ms: number = 1) => new Promise((r) => setTimeout(r, ms)); + +const until = async (check: () => Promise, pollInterval: number = 1) => { + do { + if (await check()) return; + await tick(pollInterval); + } while (true); +}; + describe('ServerShortUrlClient', () => { describe('.create()', () => { test('can create a short URL', async () => { @@ -72,6 +81,20 @@ describe('ServerShortUrlClient', () => { }, }); }); + + test('initializes "accessDate" and "accessCount" fields on URL creation', async () => { + const { client, locator } = setup(); + const { data } = await client.create({ + locator, + slug: 'lala', + params: { + url: '/app/test#foo/bar/baz', + }, + }); + + expect(data.accessDate).toBeGreaterThan(Date.now() - 1000000); + expect(data.accessCount).toBe(0); + }); }); describe('.resolve()', () => { @@ -85,7 +108,7 @@ describe('ServerShortUrlClient', () => { }); const shortUrl2 = await client.resolve(shortUrl1.data.slug); - expect(shortUrl2.data).toMatchObject(shortUrl1.data); + expect(shortUrl2.data).toStrictEqual(shortUrl1.data); }); test('can create short URL with custom slug', async () => { @@ -128,6 +151,33 @@ describe('ServerShortUrlClient', () => { }) ).rejects.toThrowError(new UrlServiceError(`Slug "lala" already exists.`, 'SLUG_EXISTS')); }); + + test('updates "accessCount" and "accessDate" on URL resolution by slug', async () => { + const { client, locator } = setup(); + const shortUrl1 = await client.create({ + locator, + params: { + url: '/app/test#foo/bar/baz', + }, + }); + + expect(shortUrl1.data.accessDate).toBeGreaterThan(Date.now() - 1000000); + expect(shortUrl1.data.accessCount).toBe(0); + + await client.resolve(shortUrl1.data.slug); + await until(async () => (await client.get(shortUrl1.data.id)).data.accessCount === 1); + const shortUrl2 = await client.get(shortUrl1.data.id); + + expect(shortUrl2.data.accessDate).toBeGreaterThanOrEqual(shortUrl1.data.accessDate); + expect(shortUrl2.data.accessCount).toBe(1); + + await client.resolve(shortUrl1.data.slug); + await until(async () => (await client.get(shortUrl1.data.id)).data.accessCount === 2); + const shortUrl3 = await client.get(shortUrl1.data.id); + + expect(shortUrl3.data.accessDate).toBeGreaterThanOrEqual(shortUrl2.data.accessDate); + expect(shortUrl3.data.accessCount).toBe(2); + }); }); describe('.get()', () => { @@ -141,7 +191,7 @@ describe('ServerShortUrlClient', () => { }); const shortUrl2 = await client.get(shortUrl1.data.id); - expect(shortUrl2.data).toMatchObject(shortUrl1.data); + expect(shortUrl2.data).toStrictEqual(shortUrl1.data); }); test('throws when fetching non-existing short URL', async () => { diff --git a/src/plugins/share/server/url_service/short_urls/short_url_client.ts b/src/plugins/share/server/url_service/short_urls/short_url_client.ts index cecc4c3127135..096b2610b916a 100644 --- a/src/plugins/share/server/url_service/short_urls/short_url_client.ts +++ b/src/plugins/share/server/url_service/short_urls/short_url_client.ts @@ -143,12 +143,29 @@ export class ServerShortUrlClient implements IShortUrlClient { const { storage } = this.dependencies; const record = await storage.getBySlug(slug); const data = this.injectReferences(record); + this.updateAccessFields(record); return { data, }; } + /** + * Access field updates are executed in the background as we don't need to + * wait for them and confirm that they were successful. + */ + protected updateAccessFields(record: ShortUrlRecord) { + const { storage } = this.dependencies; + const { id, ...attributes } = record.data; + storage + .update(id, { + ...attributes, + accessDate: Date.now(), + accessCount: (attributes.accessCount || 0) + 1, + }) + .catch(() => {}); // We are not interested if it succeeds or not. + } + public async delete(id: string): Promise { const { storage } = this.dependencies; await storage.delete(id); diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx index 8497b599650b2..362ff4a209164 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx @@ -13,6 +13,7 @@ import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; import { ChangeDataView } from './change_dataview'; import { DataViewPickerPropsExtended, TextBasedLanguages } from '.'; @@ -44,6 +45,8 @@ describe('DataView component', () => { storageValue: boolean, uiSettingValue: boolean = false ) { + const dataViewEditorMock = dataViewEditorPluginMock.createStartContract(); + (dataViewEditorMock.userPermissions.editDataView as jest.Mock).mockReturnValue(true); let dataMock = dataPluginMock.createStartContract(); dataMock = { ...dataMock, @@ -56,6 +59,7 @@ describe('DataView component', () => { const services = { data: dataMock, storage: getStorage(storageValue), + dataViewEditor: dataViewEditorMock, uiSettings: { get: jest.fn(() => uiSettingValue), }, diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index c4be51c5c80ee..1e71da3a0b20a 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -190,31 +190,35 @@ export function ChangeDataView({ defaultMessage: 'Add a field to this data view', })} , - { - if (onEditDataView) { - const dataView = await dataViews.get(currentDataViewId!); - dataViewEditor.openEditor({ - editData: dataView, - onSave: (updatedDataView) => { - onEditDataView(updatedDataView); - }, - }); - } else { - application.navigateToApp('management', { - path: `/kibana/indexPatterns/patterns/${currentDataViewId}`, - }); - } - setPopoverIsOpen(false); - }} - > - {i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', { - defaultMessage: 'Manage this data view', - })} - , + onEditDataView || dataViewEditor.userPermissions.editDataView() ? ( + { + if (onEditDataView) { + const dataView = await dataViews.get(currentDataViewId!); + dataViewEditor.openEditor({ + editData: dataView, + onSave: (updatedDataView) => { + onEditDataView(updatedDataView); + }, + }); + } else { + application.navigateToApp('management', { + path: `/kibana/indexPatterns/patterns/${currentDataViewId}`, + }); + } + setPopoverIsOpen(false); + }} + > + {i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', { + defaultMessage: 'Manage this data view', + })} + + ) : ( + + ), ); } diff --git a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx index b47b2d81b780c..518f9d1f16e16 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import SearchBar from './search_bar'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; import { I18nProvider } from '@kbn/i18n-react'; import { coreMock } from '@kbn/core/public/mocks'; @@ -83,6 +84,9 @@ function wrapSearchBarInContext(testProps: any) { intl: null as any, }; + const dataViewEditorMock = dataViewEditorPluginMock.createStartContract(); + (dataViewEditorMock.userPermissions.editDataView as jest.Mock).mockReturnValue(true); + const services = { uiSettings: startMock.uiSettings, savedObjects: startMock.savedObjects, @@ -111,6 +115,7 @@ function wrapSearchBarInContext(testProps: any) { }), }, }, + dataViewEditor: dataViewEditorMock, dataViews: { getIdsWithTitle: jest.fn(() => []), }, diff --git a/src/plugins/vis_types/metric/kibana.json b/src/plugins/vis_types/metric/kibana.json index ab69f78430338..4d8f776d2a0bb 100644 --- a/src/plugins/vis_types/metric/kibana.json +++ b/src/plugins/vis_types/metric/kibana.json @@ -4,11 +4,20 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "visualizations", "charts", "expressions"], - "requiredBundles": ["visDefaultEditor"], + "requiredPlugins": [ + "data", + "visualizations", + "charts", + "expressions", + "dataViews" + ], + "requiredBundles": [ + "visDefaultEditor", + "kibanaUtils" + ], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" }, "description": "Registers the Metric aggregation-based visualization." -} +} \ No newline at end of file diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.test.ts b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.test.ts new file mode 100644 index 0000000000000..97fb145e74a2f --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.test.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 { ColorSchemas } from '@kbn/charts-plugin/common'; +import { getConfiguration, getPercentageModeConfig } from '.'; +import { VisParams } from '../../types'; + +const mockGetPalette = jest.fn(); + +jest.mock('./palette', () => ({ + getPalette: jest.fn(() => mockGetPalette()), +})); + +const params: VisParams = { + addTooltip: false, + addLegend: false, + dimensions: {} as VisParams['dimensions'], + metric: { + percentageMode: false, + percentageFormatPattern: '', + useRanges: true, + colorSchema: ColorSchemas.Greys, + metricColorMode: 'Labels', + colorsRange: [ + { type: 'range', from: 0, to: 100 }, + { type: 'range', from: 100, to: 200 }, + { type: 'range', from: 200, to: 300 }, + ], + labels: {}, + invertColors: false, + style: {} as VisParams['metric']['style'], + }, + type: 'metric', +}; + +describe('getPercentageModeConfig', () => { + test('should return falsy percentage mode if percentage mode is off', () => { + expect(getPercentageModeConfig(params)).toEqual({ isPercentageMode: false }); + }); + + test('should return percentage mode config', () => { + expect( + getPercentageModeConfig({ ...params, metric: { ...params.metric, percentageMode: true } }) + ).toEqual({ isPercentageMode: true, min: 0, max: 300 }); + }); +}); + +describe('getConfiguration', () => { + const palette = { name: 'custom', params: { name: 'custom' }, type: 'palette' }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetPalette.mockReturnValue(palette); + }); + + test('shourd return correct configuration', () => { + const layerId = 'layer-id'; + const metric = 'metric-id'; + const bucket = 'bucket-id'; + const collapseFn = 'sum'; + expect( + getConfiguration(layerId, params, { + metrics: [metric], + buckets: [bucket], + columnsWithoutReferenced: [], + bucketCollapseFn: { [metric]: collapseFn }, + }) + ).toEqual({ + breakdownByAccessor: bucket, + collapseFn, + layerId, + layerType: 'data', + metricAccessor: metric, + palette, + }); + expect(mockGetPalette).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.ts b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.ts new file mode 100644 index 0000000000000..39e001c1b87b8 --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/index.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Column, MetricVisConfiguration } from '@kbn/visualizations-plugin/common'; +import { PercentageModeConfig } from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { VisParams } from '../../types'; +import { getPalette } from './palette'; + +export const getPercentageModeConfig = (params: VisParams): PercentageModeConfig => { + if (!params.metric.percentageMode) { + return { isPercentageMode: false }; + } + const { colorsRange } = params.metric; + return { + isPercentageMode: true, + min: colorsRange[0].from, + max: colorsRange[colorsRange.length - 1].to, + }; +}; + +export const getConfiguration = ( + layerId: string, + params: VisParams, + { + metrics, + buckets, + columnsWithoutReferenced, + bucketCollapseFn, + }: { + metrics: string[]; + buckets: string[]; + columnsWithoutReferenced: Column[]; + bucketCollapseFn?: Record; + } +): MetricVisConfiguration => { + const [metricAccessor] = metrics; + const [breakdownByAccessor] = buckets; + return { + layerId, + layerType: 'data', + palette: getPalette(params), + metricAccessor, + breakdownByAccessor, + collapseFn: Object.values(bucketCollapseFn ?? {})[0], + }; +}; diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.test.ts b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.test.ts new file mode 100644 index 0000000000000..7458a78b7b554 --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorSchemas } from '@kbn/charts-plugin/common'; +import { VisParams } from '../../types'; +import { getPalette } from './palette'; + +describe('getPalette', () => { + const params: VisParams = { + addTooltip: false, + addLegend: false, + dimensions: {} as VisParams['dimensions'], + metric: { + percentageMode: false, + percentageFormatPattern: '', + useRanges: true, + colorSchema: ColorSchemas.Greys, + metricColorMode: 'Labels', + colorsRange: [ + { type: 'range', from: 0, to: 100 }, + { type: 'range', from: 100, to: 200 }, + { type: 'range', from: 200, to: 300 }, + ], + labels: {}, + invertColors: false, + style: {} as VisParams['metric']['style'], + }, + type: 'metric', + }; + + test('should return undefined if metricColorMode is `None`', () => { + const metricColorMode = 'None'; + const paramsWithNoneMetricColorMode: VisParams = { + ...params, + metric: { ...params.metric, metricColorMode }, + }; + expect(getPalette(paramsWithNoneMetricColorMode)).toBeUndefined(); + }); + + test('should return undefined if empty color ranges were passed', () => { + const paramsWithNoneMetricColorMode: VisParams = { + ...params, + metric: { ...params.metric, colorsRange: [] }, + }; + expect(getPalette(paramsWithNoneMetricColorMode)).toBeUndefined(); + }); + + test('should return correct palette', () => { + expect(getPalette(params)).toEqual({ + name: 'custom', + params: { + colorStops: [ + { color: '#FFFFFF', stop: 0 }, + { color: '#979797', stop: 100 }, + { color: '#000000', stop: 200 }, + ], + continuity: 'none', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 300, + rangeMin: 0, + rangeType: 'number', + reverse: false, + stops: [ + { color: '#FFFFFF', stop: 100 }, + { color: '#979797', stop: 200 }, + { color: '#000000', stop: 300 }, + ], + }, + type: 'palette', + }); + }); +}); diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.ts b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.ts new file mode 100644 index 0000000000000..360de60199efa --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/configurations/palette.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import color from 'color'; +import { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; +import { VisParams } from '../../types'; +import { getStopsWithColorsFromRanges } from '../../utils'; +import { PaletteConfig } from '../../utils/palette'; + +type ColorStopsWithMinMax = Pick< + CustomPaletteParams, + 'colorStops' | 'stops' | 'steps' | 'rangeMax' | 'rangeMin' | 'continuity' +>; + +const buildPaletteParams = ({ color: colors, stop }: PaletteConfig): ColorStopsWithMinMax => { + const colorsWithoutStartColor = colors.slice(1, colors.length); + return { + rangeMin: stop[0], + rangeMax: stop[stop.length - 1], + continuity: 'none', + colorStops: colorsWithoutStartColor.map((c, index) => ({ + color: color(c!).hex(), + stop: stop[index], + })), + stops: colorsWithoutStartColor.map((c, index) => ({ + color: color(c!).hex(), + stop: stop[index + 1], + })), + }; +}; + +const buildCustomPalette = ( + colorStopsWithMinMax: ColorStopsWithMinMax +): PaletteOutput => { + return { + name: 'custom', + params: { + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: Infinity, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + ...colorStopsWithMinMax, + }, + type: 'palette', + }; +}; + +export const getPalette = (params: VisParams): PaletteOutput | undefined => { + const { colorSchema, colorsRange, invertColors, metricColorMode } = params.metric; + + if (metricColorMode === 'None' || !(colorsRange && colorsRange.length)) { + return; + } + + const stopsWithColors = getStopsWithColorsFromRanges(colorsRange, colorSchema, invertColors); + return buildCustomPalette(buildPaletteParams(stopsWithColors)); +}; diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/index.test.ts b/src/plugins/vis_types/metric/public/convert_to_lens/index.test.ts new file mode 100644 index 0000000000000..015b19157e91a --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/index.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorSchemas } from '@kbn/charts-plugin/common'; +import { Vis } from '@kbn/visualizations-plugin/public'; +import { convertToLens } from '.'; +import { VisParams } from '../types'; + +const mockGetColumnsFromVis = jest.fn(); +const mockGetPercentageColumnFormulaColumn = jest.fn(); +const mockGetConfiguration = jest.fn().mockReturnValue({}); +const mockGetPercentageModeConfig = jest.fn(); + +jest.mock('../services', () => ({ + getDataViewsStart: jest.fn(() => ({ get: () => ({}), getDefault: () => ({}) })), +})); + +jest.mock('@kbn/visualizations-plugin/public', () => ({ + convertToLensModule: Promise.resolve({ + getColumnsFromVis: jest.fn(() => mockGetColumnsFromVis()), + getPercentageColumnFormulaColumn: jest.fn(() => mockGetPercentageColumnFormulaColumn()), + }), + getDataViewByIndexPatternId: jest.fn(() => ({ id: 'index-pattern' })), +})); + +jest.mock('./configurations', () => ({ + getConfiguration: jest.fn(() => mockGetConfiguration()), + getPercentageModeConfig: jest.fn(() => mockGetPercentageModeConfig()), +})); + +const params: VisParams = { + addTooltip: false, + addLegend: false, + dimensions: {} as VisParams['dimensions'], + metric: { + percentageMode: false, + percentageFormatPattern: '', + useRanges: false, + colorSchema: ColorSchemas.Greys, + metricColorMode: 'None', + colorsRange: [], + labels: {}, + invertColors: false, + style: { + bgFill: '', + bgColor: false, + labelColor: false, + subText: '', + fontSize: 10, + }, + }, + type: 'metric', +}; + +const vis = { + isHierarchical: () => false, + type: {}, + params, + data: {}, +} as unknown as Vis; + +const timefilter = { + getAbsoluteTime: () => {}, +} as any; + +describe('convertToLens', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should return null if getColumnsFromVis returns null', async () => { + mockGetColumnsFromVis.mockReturnValue(null); + const result = await convertToLens(vis, timefilter); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + + test('should return null if metrics count is more than 1', async () => { + mockGetColumnsFromVis.mockReturnValue({ + metrics: ['1', '2'], + columns: [{ columnId: '2' }, { columnId: '1' }], + }); + const result = await convertToLens(vis, timefilter); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + test('should return null if buckets count is more than 1', async () => { + mockGetColumnsFromVis.mockReturnValue({ + metrics: [], + buckets: ['1', '2'], + columns: [{ columnId: '2' }, { columnId: '1' }], + }); + const result = await convertToLens(vis, timefilter); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + + test('should return null if metric column data type is different from number', async () => { + mockGetColumnsFromVis.mockReturnValue({ + metrics: ['1'], + buckets: ['2'], + columns: [{ columnId: '2' }, { columnId: '1', dataType: 'string' }], + }); + const result = await convertToLens(vis, timefilter); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + test('should return correct state for valid vis', async () => { + const config = { + layerType: 'data', + metricAccessor: '1', + }; + + mockGetColumnsFromVis.mockReturnValue({ + metrics: ['1'], + buckets: ['2'], + columns: [{ columnId: '2' }, { columnId: '1', dataType: 'number' }], + columnsWithoutReferenced: [ + { columnId: '1', meta: { aggId: 'agg-1' } }, + { columnId: '2', meta: { aggId: 'agg-2' } }, + ], + }); + mockGetConfiguration.mockReturnValue(config); + + const result = await convertToLens(vis, timefilter); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(mockGetConfiguration).toBeCalledTimes(1); + + expect(result?.type).toEqual('lnsMetric'); + expect(result?.layers.length).toEqual(1); + expect(result?.layers[0]).toEqual( + expect.objectContaining({ + columnOrder: [], + columns: [{ columnId: '2' }, { columnId: '1', dataType: 'number' }], + }) + ); + expect(result?.configuration).toEqual(config); + }); +}); diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/index.ts b/src/plugins/vis_types/metric/public/convert_to_lens/index.ts new file mode 100644 index 0000000000000..7675cbcc1d714 --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/index.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Column, ColumnWithMeta } from '@kbn/visualizations-plugin/common'; +import { + convertToLensModule, + getDataViewByIndexPatternId, +} from '@kbn/visualizations-plugin/public'; +import uuid from 'uuid'; +import { getDataViewsStart } from '../services'; +import { ConvertMetricVisToLensVisualization } from './types'; + +export const isColumnWithMeta = (column: Column): column is ColumnWithMeta => { + if ((column as ColumnWithMeta).meta) { + return true; + } + return false; +}; + +export const excludeMetaFromColumn = (column: Column) => { + if (isColumnWithMeta(column)) { + const { meta, ...rest } = column; + return rest; + } + return column; +}; + +export const convertToLens: ConvertMetricVisToLensVisualization = async (vis, timefilter) => { + if (!timefilter) { + return null; + } + + const dataViews = getDataViewsStart(); + const dataView = await getDataViewByIndexPatternId(vis.data.indexPattern?.id, dataViews); + + if (!dataView) { + return null; + } + + const [{ getColumnsFromVis }, { getConfiguration, getPercentageModeConfig }] = await Promise.all([ + convertToLensModule, + import('./configurations'), + ]); + + const result = getColumnsFromVis( + vis, + timefilter, + dataView, + { + splits: ['group'], + }, + { dropEmptyRowsInDateHistogram: true, ...getPercentageModeConfig(vis.params) } + ); + + if (result === null) { + return null; + } + + // for now, multiple metrics are not supported + if (result.metrics.length > 1 || result.buckets.length > 1) { + return null; + } + + if (result.metrics[0]) { + const metric = result.columns.find(({ columnId }) => columnId === result.metrics[0]); + if (metric?.dataType !== 'number') { + return null; + } + } + + const layerId = uuid(); + const indexPatternId = dataView.id!; + + return { + type: 'lnsMetric', + layers: [ + { + indexPatternId, + layerId, + columns: result.columns.map(excludeMetaFromColumn), + columnOrder: [], + }, + ], + configuration: getConfiguration(layerId, vis.params, result), + indexPatternIds: [indexPatternId], + }; +}; diff --git a/src/plugins/vis_types/metric/public/convert_to_lens/types.ts b/src/plugins/vis_types/metric/public/convert_to_lens/types.ts new file mode 100644 index 0000000000000..3676c9dee8d9d --- /dev/null +++ b/src/plugins/vis_types/metric/public/convert_to_lens/types.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TimefilterContract } from '@kbn/data-plugin/public'; +import { NavigateToLensContext, MetricVisConfiguration } from '@kbn/visualizations-plugin/common'; +import { Vis } from '@kbn/visualizations-plugin/public'; +import { VisParams } from '../types'; + +export type ConvertMetricVisToLensVisualization = ( + vis: Vis, + timefilter?: TimefilterContract +) => Promise | null>; diff --git a/src/plugins/vis_types/metric/public/metric_vis_type.ts b/src/plugins/vis_types/metric/public/metric_vis_type.ts index 30e13e8605b6d..b39cde5e07fc4 100644 --- a/src/plugins/vis_types/metric/public/metric_vis_type.ts +++ b/src/plugins/vis_types/metric/public/metric_vis_type.ts @@ -13,6 +13,7 @@ import { AggGroupNames } from '@kbn/data-plugin/public'; import { MetricVisOptions } from './components'; import { toExpressionAst } from './to_ast'; import { VisParams } from './types'; +import { convertToLens } from './convert_to_lens'; export const createMetricVisTypeDefinition = (): VisTypeDefinition => ({ name: 'metric', @@ -103,4 +104,10 @@ export const createMetricVisTypeDefinition = (): VisTypeDefinition => ], }, requiresSearch: true, + navigateToLens: async (vis, timefilter) => (vis ? convertToLens(vis, timefilter) : null), + getExpressionVariables: async (vis, timeFilter) => { + return { + canNavigateToLens: Boolean(vis?.params ? await convertToLens(vis, timeFilter) : null), + }; + }, }); diff --git a/src/plugins/vis_types/metric/public/plugin.ts b/src/plugins/vis_types/metric/public/plugin.ts index 9e79d1d23efda..bf8b8af0bddda 100644 --- a/src/plugins/vis_types/metric/public/plugin.ts +++ b/src/plugins/vis_types/metric/public/plugin.ts @@ -8,8 +8,10 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { createMetricVisTypeDefinition } from './metric_vis_type'; import { ConfigSchema } from '../config'; +import { setDataViewsStart } from './services'; /** @internal */ export interface MetricVisPluginSetupDependencies { @@ -17,7 +19,14 @@ export interface MetricVisPluginSetupDependencies { } /** @internal */ -export class MetricVisPlugin implements Plugin { +export interface MetricVisPluginStartDependencies { + dataViews: DataViewsPublicPluginStart; +} + +/** @internal */ +export class MetricVisPlugin + implements Plugin +{ initializerContext: PluginInitializerContext; constructor(initializerContext: PluginInitializerContext) { @@ -28,5 +37,7 @@ export class MetricVisPlugin implements Plugin { visualizations.createBaseVisualization(createMetricVisTypeDefinition()); } - public start(core: CoreStart) {} + public start(core: CoreStart, { dataViews }: MetricVisPluginStartDependencies) { + setDataViewsStart(dataViews); + } } diff --git a/src/plugins/vis_types/metric/public/services.ts b/src/plugins/vis_types/metric/public/services.ts new file mode 100644 index 0000000000000..736ad70d49419 --- /dev/null +++ b/src/plugins/vis_types/metric/public/services.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; + +export const [getDataViewsStart, setDataViewsStart] = + createGetterSetter('dataViews'); diff --git a/src/plugins/vis_types/table/public/convert_to_lens/index.ts b/src/plugins/vis_types/table/public/convert_to_lens/index.ts index e236c36e82a10..1b37e36f1d982 100644 --- a/src/plugins/vis_types/table/public/convert_to_lens/index.ts +++ b/src/plugins/vis_types/table/public/convert_to_lens/index.ts @@ -54,7 +54,7 @@ export const convertToLens: ConvertTableToLensVisualization = async (vis, timefi buckets: ['bucket'], splits: ['split_row', 'split_column'], }, - { dropEmptyRowsInDateHistogram: true } + { dropEmptyRowsInDateHistogram: true, isPercentageMode: false } ); if (result === null) { diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.ts index b1ac46dd36a20..7fe98b9f7ec65 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.ts @@ -25,7 +25,7 @@ export const createColumn = ( { isBucketed = false, isSplit = false, reducedTimeRange }: ExtraColumnFields = {} ): GeneralColumnWithMeta => ({ columnId: uuid(), - dataType: (field?.type as DataType) ?? undefined, + dataType: (field?.type as DataType) ?? 'number', label: getLabel(agg), isBucketed, isSplit, diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/index.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/index.ts index b77ea6b97209a..9da33607be2a9 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/convert/index.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/index.ts @@ -20,3 +20,4 @@ export * from './terms'; export * from './types'; export * from './last_value'; export * from './range'; +export * from './percentage_mode'; diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.test.ts new file mode 100644 index 0000000000000..3b7e8ad7e797f --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToColumnInPercentageMode } from './percentage_mode'; + +const mockGetFormulaForAgg = jest.fn(); + +jest.mock('../metrics/formula', () => ({ + getFormulaForAgg: jest.fn(() => mockGetFormulaForAgg()), +})); + +describe('convertToColumnInPercentageMode', () => { + const formula = 'average(some_field)'; + const dataView = stubLogstashDataView; + + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggParams: { + field: dataView.fields[0].displayName, + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFormulaForAgg.mockReturnValue(formula); + }); + + test('should return null if it is not possible to build the valid formula', () => { + mockGetFormulaForAgg.mockReturnValue(null); + expect(convertToColumnInPercentageMode({ agg, dataView, aggs: [agg] }, {})).toBeNull(); + }); + + test('should return percentage mode over range formula if min and max was passed', () => { + const formulaColumn = { + operationType: 'formula', + params: { format: { id: 'percent' }, formula: `((${formula}) - 0) / (100 - 0)` }, + }; + expect( + convertToColumnInPercentageMode({ agg, dataView, aggs: [agg] }, { min: 0, max: 100 }) + ).toEqual(expect.objectContaining(formulaColumn)); + }); + + test('should return percentage mode formula if min and max was not passed', () => { + const formulaColumn = { + operationType: 'formula', + params: { format: { id: 'percent' }, formula: `(${formula}) / 10000` }, + }; + expect(convertToColumnInPercentageMode({ agg, dataView, aggs: [agg] }, {})).toEqual( + expect.objectContaining(formulaColumn) + ); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.ts new file mode 100644 index 0000000000000..df49635c97636 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentage_mode.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { MinMax } from '../../types'; +import { getFormulaForAgg } from '../metrics/formula'; +import { createFormulaColumn } from './formula'; +import { ExtendedColumnConverterArgs } from './types'; + +const getPercentageFormulaOverRange = (formula: string, { min, max }: MinMax) => + `((${formula}) - ${min}) / (${max} - ${min})`; + +// Lens is multiplying by 100, so, it is necessary to disable that operation. +const getPercentageFormula = (formula: string) => `(${formula}) / 10000`; + +const isMinMax = (minMax: MinMax | {}): minMax is MinMax => { + if ((minMax as MinMax).min !== undefined && (minMax as MinMax).max !== undefined) { + return true; + } + return false; +}; + +export const convertToColumnInPercentageMode = ( + columnConverterArgs: ExtendedColumnConverterArgs, + minMax: MinMax | {} +) => { + if (columnConverterArgs.agg.aggType === METRIC_TYPES.TOP_HITS) { + return null; + } + + const formula = getFormulaForAgg(columnConverterArgs); + if (formula === null) { + return null; + } + + const percentageModeFormula = isMinMax(minMax) + ? getPercentageFormulaOverRange(formula, minMax) + : getPercentageFormula(formula); + + const column = createFormulaColumn(percentageModeFormula, columnConverterArgs.agg); + if (column === null) { + return null; + } + return { + ...column, + params: { ...column?.params, format: { id: 'percent' } }, + }; +}; diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts index 659ab80ea03d5..207dedd133bc9 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts @@ -19,6 +19,7 @@ const mockConvertToSiblingPipelineColumns = jest.fn(); const mockConvertToStdDeviationFormulaColumns = jest.fn(); const mockConvertToLastValueColumn = jest.fn(); const mockConvertToCumulativeSumAggColumn = jest.fn(); +const mockConvertToColumnInPercentageMode = jest.fn(); jest.mock('../convert', () => ({ convertMetricAggregationColumnWithoutSpecialParams: jest.fn(() => @@ -33,6 +34,7 @@ jest.mock('../convert', () => ({ convertToStdDeviationFormulaColumns: jest.fn(() => mockConvertToStdDeviationFormulaColumns()), convertToLastValueColumn: jest.fn(() => mockConvertToLastValueColumn()), convertToCumulativeSumAggColumn: jest.fn(() => mockConvertToCumulativeSumAggColumn()), + convertToColumnInPercentageMode: jest.fn(() => mockConvertToColumnInPercentageMode()), })); describe('convertMetricToColumns invalid cases', () => { @@ -56,145 +58,265 @@ describe('convertMetricToColumns invalid cases', () => { test.each<[string, Parameters, null, jest.Mock | undefined]>([ [ 'null if agg is not supported', - [{ aggType: METRIC_TYPES.GEO_BOUNDS } as unknown as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.GEO_BOUNDS } as unknown as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, undefined, ], [ 'null if supported agg AVG is not valid', - [{ aggType: METRIC_TYPES.AVG } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.AVG } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg MIN is not valid', - [{ aggType: METRIC_TYPES.MIN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MIN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg MAX is not valid', - [{ aggType: METRIC_TYPES.MAX } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MAX } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg SUM is not valid', - [{ aggType: METRIC_TYPES.SUM } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SUM } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg COUNT is not valid', - [{ aggType: METRIC_TYPES.COUNT } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.COUNT } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg CARDINALITY is not valid', - [{ aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg VALUE_COUNT is not valid', - [{ aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg MEDIAN is not valid', - [{ aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'null if supported agg STD_DEV is not valid', - [{ aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToStdDeviationFormulaColumns, ], [ 'null if supported agg PERCENTILES is not valid', - [{ aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToPercentileColumn, ], [ 'null if supported agg SINGLE_PERCENTILE is not valid', - [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToPercentileColumn, ], [ 'null if supported agg PERCENTILE_RANKS is not valid', - [{ aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToPercentileRankColumn, ], [ 'null if supported agg SINGLE_PERCENTILE_RANK is not valid', - [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToPercentileRankColumn, ], [ 'null if supported agg TOP_HITS is not valid', - [{ aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToLastValueColumn, ], [ 'null if supported agg TOP_METRICS is not valid', - [{ aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToLastValueColumn, ], [ 'null if supported agg CUMULATIVE_SUM is not valid', - [{ aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToCumulativeSumAggColumn, ], [ 'null if supported agg DERIVATIVE is not valid', - [{ aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToOtherParentPipelineAggColumns, ], [ 'null if supported agg MOVING_FN is not valid', - [{ aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToOtherParentPipelineAggColumns, ], [ 'null if supported agg SUM_BUCKET is not valid', - [{ aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToSiblingPipelineColumns, ], [ 'null if supported agg MIN_BUCKET is not valid', - [{ aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToSiblingPipelineColumns, ], [ 'null if supported agg MAX_BUCKET is not valid', - [{ aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToSiblingPipelineColumns, ], [ 'null if supported agg AVG_BUCKET is not valid', - [{ aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, mockConvertToSiblingPipelineColumns, ], [ 'null if supported agg SERIAL_DIFF is not valid', - [{ aggType: METRIC_TYPES.SERIAL_DIFF } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SERIAL_DIFF } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], null, undefined, ], @@ -224,141 +346,274 @@ describe('convertMetricToColumns valid cases', () => { mockConvertToStdDeviationFormulaColumns.mockReturnValue(result); mockConvertToLastValueColumn.mockReturnValue(result); mockConvertToCumulativeSumAggColumn.mockReturnValue(result); + mockConvertToColumnInPercentageMode.mockReturnValue(result); }); test.each<[string, Parameters, Array<{}>, jest.Mock]>([ [ 'array of columns if supported agg AVG is valid', - [{ aggType: METRIC_TYPES.AVG } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.AVG } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg MIN is valid', - [{ aggType: METRIC_TYPES.MIN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MIN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg MAX is valid', - [{ aggType: METRIC_TYPES.MAX } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MAX } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg SUM is valid', - [{ aggType: METRIC_TYPES.SUM } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SUM } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg COUNT is valid', - [{ aggType: METRIC_TYPES.COUNT } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.COUNT } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg CARDINALITY is valid', - [{ aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg VALUE_COUNT is valid', - [{ aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg MEDIAN is valid', - [{ aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertMetricAggregationColumnWithoutSpecialParams, ], [ 'array of columns if supported agg STD_DEV is valid', - [{ aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToStdDeviationFormulaColumns, ], [ 'array of columns if supported agg PERCENTILES is valid', - [{ aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToPercentileColumn, ], [ 'array of columns if supported agg SINGLE_PERCENTILE is valid', - [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToPercentileColumn, ], [ 'array of columns if supported agg PERCENTILE_RANKS is valid', - [{ aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToPercentileRankColumn, ], [ 'array of columns if supported agg SINGLE_PERCENTILE_RANK is valid', - [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToPercentileRankColumn, ], [ 'array of columns if supported agg TOP_HITS is valid', - [{ aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToLastValueColumn, ], [ 'array of columns if supported agg TOP_METRICS is valid', - [{ aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToLastValueColumn, ], [ 'array of columns if supported agg CUMULATIVE_SUM is valid', - [{ aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToCumulativeSumAggColumn, ], [ 'array of columns if supported agg DERIVATIVE is valid', - [{ aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToOtherParentPipelineAggColumns, ], [ 'array of columns if supported agg MOVING_FN is valid', - [{ aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToOtherParentPipelineAggColumns, ], [ 'array of columns if supported agg SUM_BUCKET is valid', - [{ aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToSiblingPipelineColumns, ], [ 'array of columns if supported agg MIN_BUCKET is valid', - [{ aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToSiblingPipelineColumns, ], [ 'array of columns if supported agg MAX_BUCKET is valid', - [{ aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToSiblingPipelineColumns, ], [ 'array of columns if supported agg AVG_BUCKET is valid', - [{ aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, dataView, []], + [ + { aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: false }, + ], result, mockConvertToSiblingPipelineColumns, ], + [ + 'column in percentage mode without range if percentageMode is enabled ', + [ + { aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: true }, + ], + result, + mockConvertToColumnInPercentageMode, + ], + [ + 'column in percentage mode with range if percentageMode is enabled ', + [ + { aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, + dataView, + [], + { isPercentageMode: true, min: 0, max: 100 }, + ], + result, + mockConvertToColumnInPercentageMode, + ], ])('should return %s', (_, input, expected, mock) => { expect(convertMetricToColumns(...input)).toEqual(expected.map(expect.objectContaining)); if (mock) { diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.ts b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.ts index d97f81c4c8240..be4c92cd4ec7f 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.ts @@ -8,7 +8,7 @@ import { METRIC_TYPES } from '@kbn/data-plugin/common'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { SchemaConfig } from '../../..'; +import { PercentageModeConfig, SchemaConfig } from '../../..'; import { convertMetricAggregationColumnWithoutSpecialParams, convertToOtherParentPipelineAggColumns, @@ -19,20 +19,29 @@ import { convertToLastValueColumn, convertToCumulativeSumAggColumn, AggBasedColumn, + convertToColumnInPercentageMode, } from '../convert'; import { SUPPORTED_METRICS } from '../convert/supported_metrics'; import { getValidColumns } from '../utils'; export const convertMetricToColumns = ( - agg: SchemaConfig, + agg: SchemaConfig, dataView: DataView, - aggs: Array> + aggs: Array>, + percentageModeConfig: PercentageModeConfig = { isPercentageMode: false } ): AggBasedColumn[] | null => { const supportedAgg = SUPPORTED_METRICS[agg.aggType]; if (!supportedAgg) { return null; } + if (percentageModeConfig.isPercentageMode) { + const { isPercentageMode, ...minMax } = percentageModeConfig; + + const formulaColumn = convertToColumnInPercentageMode({ agg, dataView, aggs }, minMax); + return getValidColumns(formulaColumn); + } + switch (agg.aggType) { case METRIC_TYPES.AVG: case METRIC_TYPES.MIN: @@ -119,8 +128,6 @@ export const convertMetricToColumns = ( }); return getValidColumns(columns); } - case METRIC_TYPES.SERIAL_DIFF: - return null; default: return null; } diff --git a/src/plugins/visualizations/common/convert_to_lens/types/common.ts b/src/plugins/visualizations/common/convert_to_lens/types/common.ts index d792467e298d0..c526a7116877d 100644 --- a/src/plugins/visualizations/common/convert_to_lens/types/common.ts +++ b/src/plugins/visualizations/common/convert_to_lens/types/common.ts @@ -45,3 +45,12 @@ export interface NumberValueFormat { suffix?: string; }; } + +export interface MinMax { + min: number; + max: number; +} + +export type PercentageModeConfig = + | ({ isPercentageMode: true } & MinMax) + | { isPercentageMode: boolean }; diff --git a/src/plugins/visualizations/public/convert_to_lens/schemas.ts b/src/plugins/visualizations/public/convert_to_lens/schemas.ts index 56108b1a1d63f..ecfbbf34ad9c9 100644 --- a/src/plugins/visualizations/public/convert_to_lens/schemas.ts +++ b/src/plugins/visualizations/public/convert_to_lens/schemas.ts @@ -8,7 +8,7 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPES, TimefilterContract } from '@kbn/data-plugin/public'; -import { AggBasedColumn, SchemaConfig } from '../../common'; +import { AggBasedColumn, PercentageModeConfig, SchemaConfig } from '../../common'; import { convertMetricToColumns } from '../../common/convert_to_lens/lib/metrics'; import { convertBucketToColumns } from '../../common/convert_to_lens/lib/buckets'; import { getCustomBucketsFromSiblingAggs } from '../../common/convert_to_lens/lib/utils'; @@ -46,8 +46,11 @@ export const getColumnsFromVis = ( } = {}, config?: { dropEmptyRowsInDateHistogram?: boolean; - } + } & (PercentageModeConfig | void) ) => { + const { dropEmptyRowsInDateHistogram, ...percentageModeConfig } = config ?? { + isPercentageMode: false, + }; const visSchemas = getVisSchemas(vis, { timefilter, timeRange: timefilter.getAbsoluteTime(), @@ -67,8 +70,8 @@ export const getColumnsFromVis = ( const metricsWithoutDuplicates = getMetricsWithoutDuplicates(visSchemas.metric); const aggs = metricsWithoutDuplicates as Array>; - const metricColumns = metricsWithoutDuplicates.flatMap((m) => - convertMetricToColumns(m, dataView, aggs) + const metricColumns = aggs.flatMap((m) => + convertMetricToColumns(m, dataView, aggs, percentageModeConfig) ); if (metricColumns.includes(null)) { @@ -81,7 +84,7 @@ export const getColumnsFromVis = ( const customBucketColumn = convertBucketToColumns( { agg: customBuckets[0], dataView, metricColumns: metrics, aggs }, false, - config?.dropEmptyRowsInDateHistogram + dropEmptyRowsInDateHistogram ); if (!customBucketColumn) { return null; @@ -95,7 +98,7 @@ export const getColumnsFromVis = ( dataView, false, metricColumns as AggBasedColumn[], - config?.dropEmptyRowsInDateHistogram + dropEmptyRowsInDateHistogram ); if (!bucketColumns) { return null; @@ -107,7 +110,7 @@ export const getColumnsFromVis = ( dataView, true, metricColumns as AggBasedColumn[], - config?.dropEmptyRowsInDateHistogram + dropEmptyRowsInDateHistogram ); if (!splitBucketColumns) { return null; diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index 1c7a6cb857ed1..3b8553435624f 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"bytes","params":null},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"bytes","params":null},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json index 1f93a487bee2b..ef645bdb30afd 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index 78135325fac66..90d572ab720f0 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index 4ff228305a1d6..2bb96cbcb4c0a 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index 7a91e2cae2bab..2c9c785e4ab2a 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test3.json b/test/interpreter_functional/snapshots/session/combined_test3.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/session/combined_test3.json +++ b/test/interpreter_functional/snapshots/session/combined_test3.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/final_output_test.json b/test/interpreter_functional/snapshots/session/final_output_test.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/session/final_output_test.json +++ b/test/interpreter_functional/snapshots/session/final_output_test.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_all_data.json b/test/interpreter_functional/snapshots/session/metric_all_data.json index 1c7a6cb857ed1..3b8553435624f 100644 --- a/test/interpreter_functional/snapshots/session/metric_all_data.json +++ b/test/interpreter_functional/snapshots/session/metric_all_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"bytes","params":null},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"bytes","params":null},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_empty_data.json b/test/interpreter_functional/snapshots/session/metric_empty_data.json index 1f93a487bee2b..ef645bdb30afd 100644 --- a/test/interpreter_functional/snapshots/session/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/session/metric_empty_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json index 78135325fac66..90d572ab720f0 100644 --- a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json index 4ff228305a1d6..2bb96cbcb4c0a 100644 --- a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json index 7a91e2cae2bab..2c9c785e4ab2a 100644 --- a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test3.json b/test/interpreter_functional/snapshots/session/step_output_test3.json index 49daaed3c5b85..922d266f7e187 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test3.json +++ b/test/interpreter_functional/snapshots/session/step_output_test3.json @@ -1 +1 @@ -{"as":"legacyMetricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/versions.json b/versions.json index a39a1412c46f6..30514180e5c1e 100644 --- a/versions.json +++ b/versions.json @@ -14,7 +14,7 @@ "previousMinor": true }, { - "version": "8.4.3", + "version": "8.4.4", "branch": "8.4", "currentMajor": true, "previousMinor": true diff --git a/x-pack/examples/files_example/common/index.ts b/x-pack/examples/files_example/common/index.ts index b9b30fac5cb50..1586d92c4c05a 100644 --- a/x-pack/examples/files_example/common/index.ts +++ b/x-pack/examples/files_example/common/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FileKind } from '@kbn/files-plugin/common'; +import type { FileKind, FileImageMetadata } from '@kbn/files-plugin/common'; export const PLUGIN_ID = 'filesExample'; export const PLUGIN_NAME = 'filesExample'; @@ -27,3 +27,5 @@ export const exampleFileKind: FileKind = { update: httpTags, }, }; + +export type MyImageMetadata = FileImageMetadata; diff --git a/x-pack/examples/files_example/public/components/app.tsx b/x-pack/examples/files_example/public/components/app.tsx index 124bc842af5ce..cf0f4461b8b62 100644 --- a/x-pack/examples/files_example/public/components/app.tsx +++ b/x-pack/examples/files_example/public/components/app.tsx @@ -21,8 +21,9 @@ import { } from '@elastic/eui'; import { CoreStart } from '@kbn/core/public'; -import { DetailsFlyout } from './details_flyout'; +import type { MyImageMetadata } from '../../common'; import type { FileClients } from '../types'; +import { DetailsFlyout } from './details_flyout'; import { ConfirmButtonIcon } from './confirm_button'; import { Modal } from './modal'; @@ -31,7 +32,7 @@ interface FilesExampleAppDeps { notifications: CoreStart['notifications']; } -type ListResponse = FilesClientResponses['list']; +type ListResponse = FilesClientResponses['list']; export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) => { const { data, isLoading, error, refetch } = useQuery(['files'], () => @@ -39,7 +40,7 @@ export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) = ); const [showUploadModal, setShowUploadModal] = useState(false); const [isDeletingFile, setIsDeletingFile] = useState(false); - const [selectedItem, setSelectedItem] = useState(); + const [selectedItem, setSelectedItem] = useState>(); const renderToolsRight = () => { return [ @@ -55,7 +56,7 @@ export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) = const items = [...(data?.files ?? [])].reverse(); - const columns: EuiInMemoryTableProps['columns'] = [ + const columns: EuiInMemoryTableProps>['columns'] = [ { field: 'name', name: 'Name', diff --git a/x-pack/examples/files_example/public/components/details_flyout.tsx b/x-pack/examples/files_example/public/components/details_flyout.tsx index 48eaa9a6ab8e4..a417752d1a666 100644 --- a/x-pack/examples/files_example/public/components/details_flyout.tsx +++ b/x-pack/examples/files_example/public/components/details_flyout.tsx @@ -7,7 +7,6 @@ import moment from 'moment'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { css } from '@emotion/react'; import { EuiFlyout, EuiFlyoutHeader, @@ -22,11 +21,13 @@ import { EuiSpacer, } from '@elastic/eui'; import type { FileJSON } from '@kbn/files-plugin/common'; +import { css } from '@emotion/react'; +import type { MyImageMetadata } from '../../common'; import { FileClients } from '../types'; import { Image } from '../imports'; interface Props { - file: FileJSON; + file: FileJSON; files: FileClients; onDismiss: () => void; } @@ -40,8 +41,24 @@ export const DetailsFlyout: FunctionComponent = ({ files, file, onDismiss +
+ {file.alt +
+ = ({ files, file, onDismiss }, ]} /> - - {file.alt
diff --git a/x-pack/examples/files_example/public/components/modal.tsx b/x-pack/examples/files_example/public/components/modal.tsx index a4b54b694713f..9d323b240f416 100644 --- a/x-pack/examples/files_example/public/components/modal.tsx +++ b/x-pack/examples/files_example/public/components/modal.tsx @@ -8,11 +8,11 @@ import type { FunctionComponent } from 'react'; import React from 'react'; import { EuiModal, EuiModalHeader, EuiModalBody, EuiText } from '@elastic/eui'; -import { exampleFileKind } from '../../common'; +import { exampleFileKind, MyImageMetadata } from '../../common'; import { FilesClient, UploadFile } from '../imports'; interface Props { - client: FilesClient; + client: FilesClient; onDismiss: () => void; onUploaded: () => void; } diff --git a/x-pack/examples/files_example/public/plugin.ts b/x-pack/examples/files_example/public/plugin.ts index 5dd961278dbd0..98a6b6f6e4608 100644 --- a/x-pack/examples/files_example/public/plugin.ts +++ b/x-pack/examples/files_example/public/plugin.ts @@ -6,7 +6,7 @@ */ import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import { PLUGIN_ID, PLUGIN_NAME, exampleFileKind } from '../common'; +import { PLUGIN_ID, PLUGIN_NAME, exampleFileKind, MyImageMetadata } from '../common'; import { FilesExamplePluginsStart, FilesExamplePluginsSetup } from './types'; export class FilesExamplePlugin @@ -28,8 +28,8 @@ export class FilesExamplePlugin coreStart, { files: { - unscoped: deps.files.filesClientFactory.asUnscoped(), - example: deps.files.filesClientFactory.asScoped(exampleFileKind.id), + unscoped: deps.files.filesClientFactory.asUnscoped(), + example: deps.files.filesClientFactory.asScoped(exampleFileKind.id), }, }, params diff --git a/x-pack/examples/files_example/public/types.ts b/x-pack/examples/files_example/public/types.ts index b1e19b13d1b9b..fbc058d9aec30 100644 --- a/x-pack/examples/files_example/public/types.ts +++ b/x-pack/examples/files_example/public/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { MyImageMetadata } from '../common'; import type { FilesSetup, FilesStart, ScopedFilesClient, FilesClient } from './imports'; export interface FilesExamplePluginsSetup { @@ -16,9 +17,9 @@ export interface FilesExamplePluginsStart { } export interface FileClients { - unscoped: FilesClient; + unscoped: FilesClient; // Example file kind - example: ScopedFilesClient; + example: ScopedFilesClient; } export interface AppPluginStartDependencies { diff --git a/x-pack/plugins/actions/server/sub_action_framework/README.md b/x-pack/plugins/actions/server/sub_action_framework/README.md index 7c2ab0755a0ad..d3ca9c7143462 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/README.md +++ b/x-pack/plugins/actions/server/sub_action_framework/README.md @@ -351,6 +351,7 @@ plugins.actions.registerSubActionConnectorType({ minimumLicenseRequired: 'platinum' as const, schema: { config: TestConfigSchema, secrets: TestSecretsSchema }, Service: TestSubActionConnector, + renderParameterTemplates: renderTestTemplate }); ``` diff --git a/x-pack/plugins/actions/server/sub_action_framework/register.test.ts b/x-pack/plugins/actions/server/sub_action_framework/register.test.ts index d4c9290b2de25..6788ca6e5e6fb 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/register.test.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/register.test.ts @@ -18,6 +18,9 @@ import { import { register } from './register'; describe('Registration', () => { + const renderedVariables = { body: '' }; + const mockRenderParameterTemplates = jest.fn().mockReturnValue(renderedVariables); + const connector = { id: '.test', name: 'Test', @@ -28,6 +31,7 @@ describe('Registration', () => { secrets: TestSecretsSchema, }, Service: TestSubActionConnector, + renderParameterTemplates: mockRenderParameterTemplates, }; const actionTypeRegistry = actionTypeRegistryMock.create(); @@ -35,7 +39,6 @@ describe('Registration', () => { const logger = loggingSystemMock.createLogger(); beforeEach(() => { - jest.resetAllMocks(); jest.clearAllMocks(); }); @@ -54,7 +57,27 @@ describe('Registration', () => { minimumLicenseRequired: connector.minimumLicenseRequired, supportedFeatureIds: connector.supportedFeatureIds, validate: expect.anything(), - executor: expect.anything(), + executor: expect.any(Function), + renderParameterTemplates: expect.any(Function), + }); + }); + + it('registers the renderParameterTemplates correctly', async () => { + register({ + actionTypeRegistry, + connector, + configurationUtilities: mockedActionsConfig, + logger, }); + + const params = {}; + const variables = {}; + const actionId = 'action-id'; + + const { renderParameterTemplates } = actionTypeRegistry.register.mock.calls[0][0]; + const rendered = renderParameterTemplates?.(params, variables, actionId); + + expect(mockRenderParameterTemplates).toHaveBeenCalledWith(params, variables, actionId); + expect(rendered).toBe(renderedVariables); }); }); diff --git a/x-pack/plugins/actions/server/sub_action_framework/register.ts b/x-pack/plugins/actions/server/sub_action_framework/register.ts index 077339731a2af..9d5bd91a88866 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/register.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/register.ts @@ -54,5 +54,6 @@ export const register = { responseSchema, headers, ...config - }: { - url: string; - responseSchema: Type; - method?: Method; - } & AxiosRequestConfig): Promise> { + }: SubActionRequestParams): Promise> { try { this.assertURL(url); this.ensureUriAllowed(url); diff --git a/x-pack/plugins/actions/server/sub_action_framework/types.ts b/x-pack/plugins/actions/server/sub_action_framework/types.ts index f584d73d24443..26b1fd20020d2 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/types.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/types.ts @@ -6,12 +6,18 @@ */ import type { Type } from '@kbn/config-schema'; -import { Logger } from '@kbn/logging'; +import type { Logger } from '@kbn/logging'; import type { LicenseType } from '@kbn/licensing-plugin/common/types'; -import { ActionsConfigurationUtilities } from '../actions_config'; -import { ActionTypeParams, Services, ValidatorType as ValidationSchema } from '../types'; -import { SubActionConnector } from './sub_action_connector'; +import type { Method, AxiosRequestConfig } from 'axios'; +import type { ActionsConfigurationUtilities } from '../actions_config'; +import type { + ActionTypeParams, + RenderParameterTemplates, + Services, + ValidatorType as ValidationSchema, +} from '../types'; +import type { SubActionConnector } from './sub_action_connector'; export interface ServiceParams { /** @@ -26,6 +32,12 @@ export interface ServiceParams { services: Services; } +export type SubActionRequestParams = { + url: string; + responseSchema: Type; + method?: Method; +} & AxiosRequestConfig; + export type IService = new ( params: ServiceParams ) => SubActionConnector; @@ -68,6 +80,7 @@ export interface SubActionConnectorType { }; validators?: Array | SecretsValidator>; Service: IService; + renderParameterTemplates?: RenderParameterTemplates; } export interface ExecutorParams extends ActionTypeParams { diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index c92761ad0a288..3806dae00c237 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -108,6 +108,12 @@ export interface ActionValidationService { isUriAllowed(uri: string): boolean; } +export type RenderParameterTemplates = ( + params: Params, + variables: Record, + actionId?: string +) => Params; + export interface ActionType< Config extends ActionTypeConfig = ActionTypeConfig, Secrets extends ActionTypeSecrets = ActionTypeSecrets, @@ -126,11 +132,7 @@ export interface ActionType< connector?: (config: Config, secrets: Secrets) => string | null; }; - renderParameterTemplates?( - params: Params, - variables: Record, - actionId?: string - ): Params; + renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; } diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx index 6c8da64cb0e89..51f9e38704910 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx @@ -235,7 +235,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ 'xpack.aiops.explainLogRateSpikes.spikeAnalysisTableGroups.groupColumnTooltip', { defaultMessage: - 'Displays field values unique to the group. Expand row to see all values.', + 'Displays field/value pairs unique to the group. Expand row to see all field/value pairs.', } )} > @@ -280,7 +280,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ +{Object.keys(repeatedValues).length}{' '} diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 1e340f6ffc755..508d8aac06dad 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -1075,6 +1075,9 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "kuery_fields": { "type": "keyword" + }, + "total": { + "type": "long" } } }, diff --git a/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx b/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx index 84be97ab0ac56..ecef396bb0c5d 100644 --- a/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { keyBy } from 'lodash'; import React from 'react'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { useSearchServiceDestinationMetrics } from '../../../../context/time_range_metadata/use_search_service_destination_metrics'; import { useApmParams } from '../../../../hooks/use_apm_params'; import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; @@ -66,6 +67,9 @@ export function DependencyDetailOperationsList() { urlComparisonEnabled, }); + const { searchServiceDestinationMetrics } = + useSearchServiceDestinationMetrics({ rangeFrom, rangeTo, kuery }); + const primaryStatsFetch = useFetcher( (callApmApi) => { return callApmApi('GET /internal/apm/dependencies/operations', { @@ -76,11 +80,19 @@ export function DependencyDetailOperationsList() { end, environment, kuery, + searchServiceDestinationMetrics, }, }, }); }, - [dependencyName, start, end, environment, kuery] + [ + dependencyName, + start, + end, + environment, + kuery, + searchServiceDestinationMetrics, + ] ); const comparisonStatsFetch = useFetcher( @@ -99,11 +111,21 @@ export function DependencyDetailOperationsList() { offset, environment, kuery, + searchServiceDestinationMetrics, }, }, }); }, - [dependencyName, start, end, offset, environment, kuery, comparisonEnabled] + [ + dependencyName, + start, + end, + offset, + environment, + kuery, + comparisonEnabled, + searchServiceDestinationMetrics, + ] ); const columns: Array> = [ diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts index b69aa1e6e0196..bf03b999f1046 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import { ApmIndicesConfig } from '../../../routes/settings/apm_indices/get_apm_indices'; import { tasks } from './tasks'; import { @@ -444,4 +445,49 @@ describe('data telemetry collection tasks', () => { }); }); }); + + describe('service groups', () => { + const task = tasks.find((t) => t.name === 'service_groups'); + const savedObjectsClient = savedObjectsClientMock.create(); + + savedObjectsClient.find.mockResolvedValue({ + page: 1, + per_page: 500, + total: 2, + saved_objects: [ + { + type: 'apm-service-group', + id: '0b6157f0-44bd-11ed-bdb7-bffab551cd4d', + namespaces: ['default'], + attributes: { + color: '#5094C4', + kuery: 'service.environment: production', + groupName: 'production', + }, + references: [], + score: 1, + }, + { + type: 'apm-service-group', + id: '0b6157f0-44bd-11ed-bdb7-bffab551cd4d', + namespaces: ['default'], + attributes: { + color: '#5094C4', + kuery: 'agent.name: go', + groupName: 'agent', + }, + references: [], + score: 0, + }, + ], + }); + it('returns service group stats', async () => { + expect(await task?.executor({ savedObjectsClient } as any)).toEqual({ + service_groups: { + kuery_fields: ['service.environment', 'agent.name'], + total: 2, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index 430930fb3f916..12e89ef32832c 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -15,6 +15,7 @@ import { AGENT_NAMES, RUM_AGENT_NAMES } from '../../../../common/agent_name'; import { SavedServiceGroup, APM_SERVICE_GROUP_SAVED_OBJECT_TYPE, + MAX_NUMBER_OF_SERVICE_GROUPS, } from '../../../../common/service_groups'; import { getKueryFields } from '../../helpers/get_kuery_fields'; import { @@ -1134,7 +1135,7 @@ export const tasks: TelemetryTask[] = [ const response = await savedObjectsClient.find({ type: APM_SERVICE_GROUP_SAVED_OBJECT_TYPE, page: 1, - perPage: 50, + perPage: MAX_NUMBER_OF_SERVICE_GROUPS, sortField: 'updated_at', sortOrder: 'desc', }); @@ -1148,6 +1149,7 @@ export const tasks: TelemetryTask[] = [ return { service_groups: { kuery_fields: kueryFields, + total: response.total ?? 0, }, }; }, diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts index f33c9e03f6caa..4430389785e1d 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts @@ -235,6 +235,7 @@ export const apmSchema: MakeSchemaFrom = { }, service_groups: { kuery_fields: { type: 'array', items: { type: 'keyword' } }, + total: long, }, per_service: { type: 'array', items: { ...apmPerServiceSchema } }, tasks: { diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts index 16e1b926d578e..ceadcbfc1ded2 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts @@ -173,6 +173,7 @@ export interface APMUsage { }; service_groups: { kuery_fields: string[]; + total: number; }; per_service: APMPerService[]; tasks: Record< diff --git a/x-pack/plugins/apm/server/routes/dependencies/get_top_dependency_operations.ts b/x-pack/plugins/apm/server/routes/dependencies/get_top_dependency_operations.ts index 1f2e270b7d699..3c688f9aaa488 100644 --- a/x-pack/plugins/apm/server/routes/dependencies/get_top_dependency_operations.ts +++ b/x-pack/plugins/apm/server/routes/dependencies/get_top_dependency_operations.ts @@ -10,23 +10,26 @@ import { rangeQuery, termQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { isFiniteNumber } from '@kbn/observability-plugin/common/utils/is_finite_number'; import { EVENT_OUTCOME, SPAN_DESTINATION_SERVICE_RESOURCE, - SPAN_DURATION, + SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, + SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, SPAN_NAME, } from '../../../common/elasticsearch_fieldnames'; import { Environment } from '../../../common/environment_rt'; import { EventOutcome } from '../../../common/event_outcome'; import { environmentQuery } from '../../../common/utils/environment_query'; import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; -import { - calculateThroughputWithInterval, - calculateThroughputWithRange, -} from '../../lib/helpers/calculate_throughput'; -import { getMetricsDateHistogramParams } from '../../lib/helpers/metrics'; +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 { + getDocumentTypeFilterForServiceDestinationStatistics, + getLatencyFieldForServiceDestinationStatistics, + getProcessorEventForServiceDestinationStatistics, +} from '../../lib/helpers/spans/get_is_using_service_destination_metrics'; import { calculateImpactBuilder } from '../traces/calculate_impact_builder'; const MAX_NUM_OPERATIONS = 500; @@ -51,6 +54,7 @@ export async function getTopDependencyOperations({ offset, environment, kuery, + searchServiceDestinationMetrics, }: { setup: Setup; dependencyName: string; @@ -59,6 +63,7 @@ export async function getTopDependencyOperations({ offset?: string; environment: Environment; kuery: string; + searchServiceDestinationMetrics: boolean; }) { const { apmEventClient } = setup; @@ -68,10 +73,25 @@ export async function getTopDependencyOperations({ offset, }); + const { intervalString } = getBucketSizeForAggregatedTransactions({ + start: startWithOffset, + end: endWithOffset, + searchAggregatedServiceMetrics: searchServiceDestinationMetrics, + }); + + const field = getLatencyFieldForServiceDestinationStatistics( + searchServiceDestinationMetrics + ); + const aggs = { - duration: { - avg: { - field: SPAN_DURATION, + latency: { + ...(searchServiceDestinationMetrics + ? { sum: { field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM } } + : { avg: { field } }), + }, + count: { + sum: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, }, }, successful: { @@ -94,7 +114,11 @@ export async function getTopDependencyOperations({ 'get_top_dependency_operations', { apm: { - events: [ProcessorEvent.span], + events: [ + getProcessorEventForServiceDestinationStatistics( + searchServiceDestinationMetrics + ), + ], }, body: { track_total_hits: false, @@ -106,6 +130,9 @@ export async function getTopDependencyOperations({ ...environmentQuery(environment), ...kqlQuery(kuery), ...termQuery(SPAN_DESTINATION_SERVICE_RESOURCE, dependencyName), + ...getDocumentTypeFilterForServiceDestinationStatistics( + searchServiceDestinationMetrics + ), ], }, }, @@ -117,18 +144,20 @@ export async function getTopDependencyOperations({ }, aggs: { over_time: { - date_histogram: getMetricsDateHistogramParams({ - start: startWithOffset, - end: endWithOffset, - metricsInterval: 60, - }), + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { + min: startWithOffset, + max: endWithOffset, + }, + }, aggs, }, ...aggs, total_time: { - sum: { - field: SPAN_DURATION, - }, + sum: { field }, }, }, }, @@ -154,14 +183,28 @@ export async function getTopDependencyOperations({ bucket.over_time.buckets.forEach((dateBucket) => { const x = dateBucket.key + offsetInMs; + const latencyValue = isFiniteNumber(dateBucket.latency.value) + ? dateBucket.latency.value + : 0; + const count = isFiniteNumber(dateBucket.count.value) + ? dateBucket.count.value + : 1; timeseries.throughput.push({ x, - y: calculateThroughputWithInterval({ - value: dateBucket.doc_count, - bucketSize: 60, + y: calculateThroughputWithRange({ + start: startWithOffset, + end: endWithOffset, + value: searchServiceDestinationMetrics + ? dateBucket.count.value || 0 + : dateBucket.doc_count, }), }); - timeseries.latency.push({ x, y: dateBucket.duration.value }); + timeseries.latency.push({ + x, + y: searchServiceDestinationMetrics + ? latencyValue / count + : dateBucket.latency.value, + }); timeseries.failureRate.push({ x, y: @@ -174,13 +217,24 @@ export async function getTopDependencyOperations({ }); }); + const latencyValue = isFiniteNumber(bucket.latency.value) + ? bucket.latency.value + : 0; + const count = isFiniteNumber(bucket.count.value) + ? bucket.count.value + : 1; + return { spanName: bucket.key as string, - latency: bucket.duration.value, + latency: searchServiceDestinationMetrics + ? latencyValue / count + : bucket.latency.value, throughput: calculateThroughputWithRange({ start: startWithOffset, end: endWithOffset, - value: bucket.doc_count, + value: searchServiceDestinationMetrics + ? bucket.count.value || 0 + : bucket.doc_count, }), failureRate: bucket.failure.doc_count > 0 || bucket.successful.doc_count > 0 diff --git a/x-pack/plugins/apm/server/routes/dependencies/route.ts b/x-pack/plugins/apm/server/routes/dependencies/route.ts index 22a8c44e0de5a..1ba2e92eee57a 100644 --- a/x-pack/plugins/apm/server/routes/dependencies/route.ts +++ b/x-pack/plugins/apm/server/routes/dependencies/route.ts @@ -492,7 +492,10 @@ const dependencyOperationsRoute = createApmServerRoute({ environmentRt, kueryRt, offsetRt, - t.type({ dependencyName: t.string }), + t.type({ + dependencyName: t.string, + searchServiceDestinationMetrics: toBooleanRt, + }), ]), }), handler: async ( @@ -501,7 +504,15 @@ const dependencyOperationsRoute = createApmServerRoute({ const setup = await setupRequest(resources); const { - query: { dependencyName, start, end, environment, kuery, offset }, + query: { + dependencyName, + start, + end, + environment, + kuery, + offset, + searchServiceDestinationMetrics, + }, } = resources.params; const operations = await getTopDependencyOperations({ @@ -512,6 +523,7 @@ const dependencyOperationsRoute = createApmServerRoute({ offset, environment, kuery, + searchServiceDestinationMetrics, }); return { operations }; diff --git a/x-pack/plugins/apm/server/routes/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/routes/metrics/get_metrics_chart_data_by_agent.ts index 2aa5312d66b9e..b5ae2bbe093ae 100644 --- a/x-pack/plugins/apm/server/routes/metrics/get_metrics_chart_data_by_agent.ts +++ b/x-pack/plugins/apm/server/routes/metrics/get_metrics_chart_data_by_agent.ts @@ -41,14 +41,16 @@ export async function getMetricsChartDataByAgent({ start, end, }; - if (isJavaAgentName(agentName)) { + const serverlessAgent = isServerlessAgent(serviceRuntimeName); + + if (isJavaAgentName(agentName) && !serverlessAgent) { return getJavaMetricsCharts({ ...options, serviceNodeName, }); } - if (isServerlessAgent(serviceRuntimeName)) { + if (serverlessAgent) { return getServerlessAgentMetricCharts(options); } diff --git a/x-pack/plugins/cases/docs/openapi/README.md b/x-pack/plugins/cases/docs/openapi/README.md index 1ff3e24c2e91f..cf0ea6c76da7c 100644 --- a/x-pack/plugins/cases/docs/openapi/README.md +++ b/x-pack/plugins/cases/docs/openapi/README.md @@ -22,8 +22,13 @@ command in the `x-pack/plugins/cases/docs/openapi/` folder: Then you can generate the `bundled` files by running the following commands: - ``` - npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml entrypoint.yaml - npx @redocly/openapi-cli bundle --ext json --output bundled.json entrypoint.yaml - ``` + ``` + npx @redocly/cli bundle entrypoint.yaml --output bundled.yaml --ext yaml + npx @redocly/cli bundle entrypoint.yaml --output bundled.json --ext json + ``` + +You can run additional linting with the following command: + ``` + npx @redocly/cli lint bundled.json + ``` diff --git a/x-pack/plugins/cases/docs/openapi/bundled-min.json b/x-pack/plugins/cases/docs/openapi/bundled-min.json new file mode 100644 index 0000000000000..b039f5065d456 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/bundled-min.json @@ -0,0 +1,1277 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Cases", + "description": "OpenAPI schema for Cases endpoints", + "version": "0.2", + "contact": { + "name": "Cases Team" + }, + "license": { + "name": "Elastic License 2.0", + "url": "https://www.elastic.co/licensing/elastic-license" + } + }, + "tags": [ + { + "name": "cases", + "description": "Case APIs enable you to open and track issues." + } + ], + "servers": [ + { + "url": "http://localhost:5601", + "description": "local" + } + ], + "paths": { + "/s/{spaceId}/api/cases/{caseId}/comments": { + "post": { + "summary": "Adds a comment or alert to a case.", + "operationId": "addCaseComment", + "description": "You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating.\n", + "tags": [ + "cases" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/case_id" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/add_case_comment_request" + }, + "examples": { + "createCaseCommentRequest": { + "$ref": "#/components/examples/add_comment_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/case_response_properties" + }, + "examples": { + "createCaseCommentResponse": { + "$ref": "#/components/examples/add_comment_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "delete": { + "summary": "Deletes all comments and alerts from a case.", + "operationId": "deleteCaseComments", + "description": "You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting.\n", + "tags": [ + "cases" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/case_id" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "responses": { + "204": { + "description": "Indicates a successful call." + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "patch": { + "summary": "Updates a comment or alert in a case.", + "operationId": "updateCaseComment", + "description": "You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating. NOTE: You cannot change the comment type or the owner of a comment.\n", + "tags": [ + "cases" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/case_id" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/update_case_comment_request" + }, + "examples": { + "updateCaseCommentRequest": { + "$ref": "#/components/examples/update_comment_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/case_response_properties" + }, + "examples": { + "updateCaseCommentResponse": { + "$ref": "#/components/examples/update_comment_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "get": { + "summary": "Retrieves all the comments from a case.", + "operationId": "getAllCaseComments", + "description": "You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you're seeking.\n", + "deprecated": true, + "tags": [ + "cases" + ], + "parameters": [ + { + "$ref": "#/components/parameters/case_id" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/case_response_properties" + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + } + }, + "components": { + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + }, + "apiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "ApiKey" + } + }, + "parameters": { + "case_id": { + "in": "path", + "name": "caseId", + "description": "The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded.", + "required": true, + "schema": { + "type": "string", + "example": "9c235210-6834-11ea-a78c-6ffb38a34414" + } + }, + "space_id": { + "in": "path", + "name": "spaceId", + "description": "An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used.", + "required": true, + "schema": { + "type": "string", + "example": "default" + } + }, + "kbn_xsrf": { + "schema": { + "type": "string" + }, + "in": "header", + "name": "kbn-xsrf", + "required": true + } + }, + "schemas": { + "case_response_closed_by_properties": { + "title": "Case response properties for closed_by", + "type": "object", + "nullable": true, + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "required": [ + "email", + "full_name", + "username" + ] + }, + "owners": { + "type": "string", + "description": "The application that owns the cases: Stack Management, Observability, or Elastic Security.\n", + "enum": [ + "cases", + "observability", + "securitySolution" + ], + "example": "cases" + }, + "alert_comment_response_properties": { + "title": "Add case comment response properties for alerts", + "type": "object", + "required": [ + "type" + ], + "properties": { + "alertId": { + "type": "string", + "example": "6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-03-24T02:31:03.210Z" + }, + "created_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + } + }, + "id": { + "type": "string", + "example": "73362370-ab1a-11ec-985f-97e55adae8b9" + }, + "index": { + "type": "string", + "example": ".internal.alerts-security.alerts-default-000001" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "pushed_at": { + "type": "string", + "format": "date-time", + "example": null, + "nullable": true + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "nullable": true + }, + "rule": { + "type": "object", + "properties": { + "id": { + "description": "The rule identifier.", + "type": "string", + "example": "94d80550-aaf4-11ec-985f-97e55adae8b9" + }, + "name": { + "description": "The rule name.", + "type": "string", + "example": "security_rule" + } + } + }, + "type": { + "type": "string", + "example": "alert", + "enum": [ + "alert" + ] + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": null + }, + "updated_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + } + }, + "version": { + "type": "string", + "example": "WzMwNDgsMV0=" + } + } + }, + "case_response_created_by_properties": { + "title": "Case response properties for created_by", + "type": "object", + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "required": [ + "email", + "full_name", + "username" + ] + }, + "case_response_pushed_by_properties": { + "title": "Case response properties for pushed_by", + "type": "object", + "nullable": true, + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "required": [ + "email", + "full_name", + "username" + ] + }, + "case_response_updated_by_properties": { + "title": "Case response properties for updated_by", + "type": "object", + "nullable": true, + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "required": [ + "email", + "full_name", + "username" + ] + }, + "user_comment_response_properties": { + "title": "Case response properties for user comments", + "type": "object", + "required": [ + "type" + ], + "properties": { + "comment": { + "type": "string", + "example": "A new comment." + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "$ref": "#/components/schemas/case_response_created_by_properties" + }, + "id": { + "type": "string", + "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "pushed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "pushed_by": { + "$ref": "#/components/schemas/case_response_pushed_by_properties" + }, + "type": { + "type": "string", + "example": "user", + "enum": [ + "user" + ] + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "updated_by": { + "$ref": "#/components/schemas/case_response_updated_by_properties" + }, + "version": { + "type": "string", + "example": "WzIwNDMxLDFd" + } + } + }, + "case_response_connector_field_properties": { + "title": "Case response properties for connector fields", + "type": "object", + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + } + }, + "connector_types": { + "type": "string", + "description": "The type of connector.", + "enum": [ + ".cases-webhook", + ".jira", + ".none", + ".resilient", + ".servicenow", + ".servicenow-sir", + ".swimlane" + ], + "example": ".none" + }, + "external_service": { + "type": "object", + "nullable": true, + "properties": { + "connector_id": { + "type": "string" + }, + "connector_name": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "external_title": { + "type": "string" + }, + "external_url": { + "type": "string" + }, + "pushed_at": { + "type": "string", + "format": "date-time" + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": null, + "nullable": true + }, + "full_name": { + "type": "string", + "example": null, + "nullable": true + }, + "username": { + "type": "string", + "example": "elastic", + "nullable": true + }, + "profile_uid": { + "type": "string", + "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + } + }, + "nullable": true + } + } + }, + "settings": { + "type": "object", + "description": "An object that contains the case settings.", + "properties": { + "syncAlerts": { + "description": "Turns alert syncing on or off.", + "type": "boolean", + "example": true + } + } + }, + "severity_property": { + "type": "string", + "description": "The severity of the case.", + "enum": [ + "critical", + "high", + "low", + "medium" + ], + "default": "low" + }, + "status": { + "type": "string", + "description": "The status of the case.", + "enum": [ + "closed", + "in-progress", + "open" + ] + }, + "case_response_properties": { + "title": "Case response properties", + "type": "object", + "required": [ + "closed_at", + "closed_by", + "comments", + "connector", + "created_at", + "created_by", + "description", + "duration", + "external_service", + "id", + "owner", + "settings", + "severity", + "status", + "tags", + "title", + "totalAlerts", + "totalComment", + "updated_at", + "updated_by", + "version" + ], + "properties": { + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "closed_by": { + "$ref": "#/components/schemas/case_response_closed_by_properties" + }, + "comments": { + "title": "Case response properties for comments", + "description": "An array of comment objects for the case.", + "type": "array", + "items": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/alert_comment_response_properties" + }, + { + "$ref": "#/components/schemas/user_comment_response_properties" + } + ] + } + }, + "connector": { + "title": "Case response properties for connectors", + "type": "object", + "properties": { + "fields": { + "$ref": "#/components/schemas/case_response_connector_field_properties" + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string", + "example": "none" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string", + "example": "none" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "$ref": "#/components/schemas/case_response_created_by_properties" + }, + "description": { + "type": "string", + "example": "A case description." + }, + "duration": { + "type": "integer", + "description": "The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero.\n", + "nullable": true, + "example": 120 + }, + "external_service": { + "$ref": "#/components/schemas/external_service" + }, + "id": { + "type": "string", + "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "$ref": "#/components/schemas/settings" + }, + "severity": { + "$ref": "#/components/schemas/severity_property" + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "tag-1" + ] + }, + "title": { + "type": "string", + "example": "Case title 1" + }, + "totalAlerts": { + "type": "integer", + "example": 0 + }, + "totalComment": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "updated_by": { + "$ref": "#/components/schemas/case_response_updated_by_properties" + }, + "version": { + "type": "string", + "example": "WzUzMiwxXQ==" + } + } + }, + "alert_identifiers": { + "title": "Alert identifiers", + "description": "The alert identifier. It is required only when `type` is `alert`. If it is an array, `index` must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "x-technical-preview": true, + "example": "6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42" + }, + "alert_indices": { + "title": "Alert indices", + "description": "The alert index. It is required only when `type` is `alert`. If it is an array, `alertId` must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "x-technical-preview": true + }, + "rule": { + "title": "Alerting rule", + "description": "The rule that is associated with the alert. It is required only when `type` is `alert`. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "type": "object", + "x-technical-preview": true, + "properties": { + "id": { + "description": "The rule identifier.", + "type": "string", + "example": "94d80550-aaf4-11ec-985f-97e55adae8b9" + }, + "name": { + "description": "The rule name.", + "type": "string", + "example": "security_rule" + } + } + }, + "add_alert_comment_request_properties": { + "title": "Add case comment request properties for alerts", + "required": [ + "alertId", + "index", + "owner", + "rule", + "type" + ], + "description": "Defines properties for case comment requests when type is alert.", + "type": "object", + "properties": { + "alertId": { + "$ref": "#/components/schemas/alert_identifiers" + }, + "index": { + "$ref": "#/components/schemas/alert_indices" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "rule": { + "$ref": "#/components/schemas/rule" + }, + "type": { + "description": "The type of comment.", + "type": "string", + "example": "alert", + "enum": [ + "alert" + ] + } + } + }, + "add_user_comment_request_properties": { + "title": "Add case comment request properties for user comments", + "description": "Defines properties for case comment requests when type is user.", + "type": "object", + "properties": { + "comment": { + "description": "The new comment. It is required only when `type` is `user`.", + "type": "string", + "example": "A new comment." + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "type": { + "type": "string", + "description": "The type of comment.", + "example": "user", + "enum": [ + "user" + ] + } + }, + "required": [ + "comment", + "owner", + "type" + ] + }, + "add_case_comment_request": { + "title": "Add case comment request", + "description": "The add comment to case API request body varies depending on whether you are adding an alert or a comment.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/add_alert_comment_request_properties" + }, + { + "$ref": "#/components/schemas/add_user_comment_request_properties" + } + ] + }, + "update_alert_comment_request_properties": { + "title": "Update case comment request properties for alerts", + "description": "Defines properties for case comment requests when type is alert.", + "required": [ + "alertId", + "id", + "index", + "owner", + "rule", + "type", + "version" + ], + "type": "object", + "properties": { + "alertId": { + "$ref": "#/components/schemas/alert_identifiers" + }, + "id": { + "type": "string", + "description": "The identifier for the comment. To retrieve comment IDs, use the get comments API.\n", + "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + }, + "index": { + "$ref": "#/components/schemas/alert_indices" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "rule": { + "$ref": "#/components/schemas/rule" + }, + "type": { + "description": "The type of comment.", + "type": "string", + "enum": [ + "alert" + ], + "example": "alert" + }, + "version": { + "description": "The current comment version. To retrieve version values, use the get comments API.\n", + "type": "string", + "example": "Wzk1LDFd" + } + } + }, + "update_user_comment_request_properties": { + "title": "Update case comment request properties for user comments", + "description": "Defines properties for case comment requests when type is user.", + "type": "object", + "properties": { + "comment": { + "description": "The new comment. It is required only when `type` is `user`.", + "type": "string", + "example": "A new comment." + }, + "id": { + "type": "string", + "description": "The identifier for the comment. To retrieve comment IDs, use the get comments API.\n", + "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "type": { + "type": "string", + "description": "The type of comment.", + "enum": [ + "user" + ], + "example": "user" + }, + "version": { + "description": "The current comment version. To retrieve version values, use the get comments API.\n", + "type": "string", + "example": "Wzk1LDFd" + } + }, + "required": [ + "comment", + "id", + "owner", + "type", + "version" + ] + }, + "update_case_comment_request": { + "title": "Update case comment request", + "description": "The update case comment API request body varies depending on whether you are updating an alert or a comment.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/update_alert_comment_request_properties" + }, + { + "$ref": "#/components/schemas/update_user_comment_request_properties" + } + ] + } + }, + "examples": { + "add_comment_request": { + "summary": "Adds a comment to a case.", + "value": { + "type": "user", + "comment": "A new comment.", + "owner": "cases" + } + }, + "add_comment_response": { + "summary": "The add comment to case API returns a JSON object that contains details about the case and its comments.", + "value": { + "comments": [ + { + "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", + "version": "WzIwNDMxLDFd", + "type": "user", + "owner": "cases", + "comment": "A new comment.", + "created_at": "2022-06-02T00:49:47.716Z", + "created_by": { + "username": "elastic", + "email": null, + "full_name": null + } + } + ], + "totalAlerts": 0, + "id": "293f1bc0-74f6-11ea-b83a-553aecdb28b6", + "version": "WzIzMzgsMV0=", + "totalComment": 1, + "title": "Case title 1", + "tags": [ + "tag 1" + ], + "description": "A case description.", + "settings": { + "syncAlerts": false + }, + "owner": "cases", + "duration": null, + "severity": "low", + "closed_at": null, + "closed_by": null, + "created_at": "2022-03-24T00:37:03.906Z", + "created_by": { + "username": "elastic", + "full_name": null, + "email": null + }, + "status": "open", + "updated_at": "2022-06-03T00:49:47.716Z", + "updated_by": { + "username": "elastic", + "email": null, + "full_name": null + }, + "connector": { + "id": "none", + "name": "none", + "type": ".none", + "fields": null + }, + "external_service": null + } + }, + "update_comment_request": { + "summary": "Updates a comment of a case.", + "value": { + "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", + "version": "Wzk1LDFd", + "type": "user", + "comment": "An updated comment.", + "owner": "cases" + } + }, + "update_comment_response": { + "summary": "The add comment to case API returns a JSON object that contains details about the case and its comments.", + "value": { + "comments": [ + { + "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", + "version": "WzIwNjM3LDFd", + "comment": "An updated comment.", + "type": "user", + "owner": "cases", + "created_at": "2022-03-24T00:37:10.832Z", + "created_by": { + "username": "elastic", + "full_name": null, + "email": null + }, + "pushed_at": null, + "pushed_by": null, + "updated_at": "2022-03-24T01:27:06.210Z", + "updated_by": { + "username": "elastic", + "full_name": null, + "email": null + } + } + ], + "totalAlerts": 0, + "id": "293f1bc0-74f6-11ea-b83a-553aecdb28b6", + "version": "WzIwNjM2LDFd", + "totalComment": 1, + "title": "Case title 1", + "tags": [ + "tag 1" + ], + "description": "A case description.", + "settings": { + "syncAlerts": false + }, + "owner": "cases", + "duration": null, + "severity": "low", + "closed_at": null, + "closed_by": null, + "created_at": "2022-03-24T00:37:03.906Z", + "created_by": { + "username": "elastic", + "full_name": null, + "email": null + }, + "status": "open", + "updated_at": "2022-03-24T01:27:06.210Z", + "updated_by": { + "username": "elastic", + "full_name": null, + "email": null + }, + "connector": { + "id": "none", + "name": "none", + "type": ".none", + "fields": null + }, + "external_service": null + } + } + } + }, + "security": [ + { + "basicAuth": [] + }, + { + "apiKeyAuth": [] + } + ] +} \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/bundled-min.yaml b/x-pack/plugins/cases/docs/openapi/bundled-min.yaml new file mode 100644 index 0000000000000..75f982011a137 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/bundled-min.yaml @@ -0,0 +1,923 @@ +openapi: 3.0.1 +info: + title: Cases + description: OpenAPI schema for Cases endpoints + version: '0.2' + contact: + name: Cases Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: cases + description: Case APIs enable you to open and track issues. +servers: + - url: http://localhost:5601 + description: local +paths: + /s/{spaceId}/api/cases/{caseId}/comments: + post: + summary: Adds a comment or alert to a case. + operationId: addCaseComment + description: | + You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating. + tags: + - cases + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/case_id' + - $ref: '#/components/parameters/space_id' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/add_case_comment_request' + examples: + createCaseCommentRequest: + $ref: '#/components/examples/add_comment_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json: + schema: + $ref: '#/components/schemas/case_response_properties' + examples: + createCaseCommentResponse: + $ref: '#/components/examples/add_comment_response' + servers: + - url: https://localhost:5601 + delete: + summary: Deletes all comments and alerts from a case. + operationId: deleteCaseComments + description: | + You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting. + tags: + - cases + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/case_id' + - $ref: '#/components/parameters/space_id' + responses: + '204': + description: Indicates a successful call. + servers: + - url: https://localhost:5601 + patch: + summary: Updates a comment or alert in a case. + operationId: updateCaseComment + description: | + You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating. NOTE: You cannot change the comment type or the owner of a comment. + tags: + - cases + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/case_id' + - $ref: '#/components/parameters/space_id' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/update_case_comment_request' + examples: + updateCaseCommentRequest: + $ref: '#/components/examples/update_comment_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json: + schema: + $ref: '#/components/schemas/case_response_properties' + examples: + updateCaseCommentResponse: + $ref: '#/components/examples/update_comment_response' + servers: + - url: https://localhost:5601 + get: + summary: Retrieves all the comments from a case. + operationId: getAllCaseComments + description: | + You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you're seeking. + deprecated: true + tags: + - cases + parameters: + - $ref: '#/components/parameters/case_id' + - $ref: '#/components/parameters/space_id' + responses: + '200': + description: Indicates a successful call. + content: + application/json: + schema: + $ref: '#/components/schemas/case_response_properties' + servers: + - url: https://localhost:5601 + servers: + - url: https://localhost:5601 +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey + parameters: + case_id: + in: path + name: caseId + description: The identifier for the case. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. + required: true + schema: + type: string + example: 9c235210-6834-11ea-a78c-6ffb38a34414 + space_id: + in: path + name: spaceId + description: An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used. + required: true + schema: + type: string + example: default + kbn_xsrf: + schema: + type: string + in: header + name: kbn-xsrf + required: true + schemas: + case_response_closed_by_properties: + title: Case response properties for closed_by + type: object + nullable: true + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + required: + - email + - full_name + - username + owners: + type: string + description: | + The application that owns the cases: Stack Management, Observability, or Elastic Security. + enum: + - cases + - observability + - securitySolution + example: cases + alert_comment_response_properties: + title: Add case comment response properties for alerts + type: object + required: + - type + properties: + alertId: + type: string + example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 + created_at: + type: string + format: date-time + example: '2022-03-24T02:31:03.210Z' + created_by: + type: object + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + id: + type: string + example: 73362370-ab1a-11ec-985f-97e55adae8b9 + index: + type: string + example: .internal.alerts-security.alerts-default-000001 + owner: + $ref: '#/components/schemas/owners' + pushed_at: + type: string + format: date-time + example: null + nullable: true + pushed_by: + type: object + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + nullable: true + rule: + type: object + properties: + id: + description: The rule identifier. + type: string + example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + name: + description: The rule name. + type: string + example: security_rule + type: + type: string + example: alert + enum: + - alert + updated_at: + type: string + format: date-time + example: null + updated_by: + type: object + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + version: + type: string + example: WzMwNDgsMV0= + case_response_created_by_properties: + title: Case response properties for created_by + type: object + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + required: + - email + - full_name + - username + case_response_pushed_by_properties: + title: Case response properties for pushed_by + type: object + nullable: true + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + required: + - email + - full_name + - username + case_response_updated_by_properties: + title: Case response properties for updated_by + type: object + nullable: true + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + required: + - email + - full_name + - username + user_comment_response_properties: + title: Case response properties for user comments + type: object + required: + - type + properties: + comment: + type: string + example: A new comment. + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + $ref: '#/components/schemas/case_response_created_by_properties' + id: + type: string + example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + owner: + $ref: '#/components/schemas/owners' + pushed_at: + type: string + format: date-time + nullable: true + example: null + pushed_by: + $ref: '#/components/schemas/case_response_pushed_by_properties' + type: + type: string + example: user + enum: + - user + updated_at: + type: string + format: date-time + nullable: true + example: null + updated_by: + $ref: '#/components/schemas/case_response_updated_by_properties' + version: + type: string + example: WzIwNDMxLDFd + case_response_connector_field_properties: + title: Case response properties for connector fields + type: object + description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. + nullable: true + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors. + type: string + destIp: + description: A comma-separated list of destination IPs for ServiceNow SecOps connectors. + type: string + impact: + description: The effect an incident had on business for ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: A comma-separated list of malware hashes for ServiceNow SecOps connectors. + type: string + malwareUrl: + description: A comma-separated list of malware URLs for ServiceNow SecOps connectors. + type: string + parent: + description: The key of the parent issue, when the issue type is sub-task for Jira connectors. + type: string + priority: + description: The priority of the issue for Jira and ServiceNow SecOps connectors. + type: string + severity: + description: The severity of the incident for ServiceNow ITSM connectors. + type: string + severityCode: + description: The severity code of the incident for IBM Resilient connectors. + type: number + sourceIp: + description: A comma-separated list of source IPs for ServiceNow SecOps connectors. + type: string + subcategory: + description: The subcategory of the incident for ServiceNow ITSM connectors. + type: string + urgency: + description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. + type: string + connector_types: + type: string + description: The type of connector. + enum: + - .cases-webhook + - .jira + - .none + - .resilient + - .servicenow + - .servicenow-sir + - .swimlane + example: .none + external_service: + type: object + nullable: true + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + example: null + nullable: true + full_name: + type: string + example: null + nullable: true + username: + type: string + example: elastic + nullable: true + profile_uid: + type: string + example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + nullable: true + settings: + type: object + description: An object that contains the case settings. + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + example: true + severity_property: + type: string + description: The severity of the case. + enum: + - critical + - high + - low + - medium + default: low + status: + type: string + description: The status of the case. + enum: + - closed + - in-progress + - open + case_response_properties: + title: Case response properties + type: object + required: + - closed_at + - closed_by + - comments + - connector + - created_at + - created_by + - description + - duration + - external_service + - id + - owner + - settings + - severity + - status + - tags + - title + - totalAlerts + - totalComment + - updated_at + - updated_by + - version + properties: + closed_at: + type: string + format: date-time + nullable: true + closed_by: + $ref: '#/components/schemas/case_response_closed_by_properties' + comments: + title: Case response properties for comments + description: An array of comment objects for the case. + type: array + items: + discriminator: + propertyName: type + oneOf: + - $ref: '#/components/schemas/alert_comment_response_properties' + - $ref: '#/components/schemas/user_comment_response_properties' + connector: + title: Case response properties for connectors + type: object + properties: + fields: + $ref: '#/components/schemas/case_response_connector_field_properties' + id: + description: The identifier for the connector. To create a case without a connector, use `none`. + type: string + example: none + name: + description: The name of the connector. To create a case without a connector, use `none`. + type: string + example: none + type: + $ref: '#/components/schemas/connector_types' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + $ref: '#/components/schemas/case_response_created_by_properties' + description: + type: string + example: A case description. + duration: + type: integer + description: | + The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero. + nullable: true + example: 120 + external_service: + $ref: '#/components/schemas/external_service' + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: '#/components/schemas/owners' + settings: + $ref: '#/components/schemas/settings' + severity: + $ref: '#/components/schemas/severity_property' + status: + $ref: '#/components/schemas/status' + tags: + type: array + items: + type: string + example: + - tag-1 + title: + type: string + example: Case title 1 + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + updated_by: + $ref: '#/components/schemas/case_response_updated_by_properties' + version: + type: string + example: WzUzMiwxXQ== + alert_identifiers: + title: Alert identifiers + description: | + The alert identifier. It is required only when `type` is `alert`. If it is an array, `index` must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + oneOf: + - type: string + - type: array + items: + type: string + x-technical-preview: true + example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 + alert_indices: + title: Alert indices + description: | + The alert index. It is required only when `type` is `alert`. If it is an array, `alertId` must also be an array with the same length or number of elements. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + oneOf: + - type: string + - type: array + items: + type: string + x-technical-preview: true + rule: + title: Alerting rule + description: | + The rule that is associated with the alert. It is required only when `type` is `alert`. This functionality is in technical preview and may be changed or removed in a future release. Elastic will apply best effort to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. + type: object + x-technical-preview: true + properties: + id: + description: The rule identifier. + type: string + example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + name: + description: The rule name. + type: string + example: security_rule + add_alert_comment_request_properties: + title: Add case comment request properties for alerts + required: + - alertId + - index + - owner + - rule + - type + description: Defines properties for case comment requests when type is alert. + type: object + properties: + alertId: + $ref: '#/components/schemas/alert_identifiers' + index: + $ref: '#/components/schemas/alert_indices' + owner: + $ref: '#/components/schemas/owners' + rule: + $ref: '#/components/schemas/rule' + type: + description: The type of comment. + type: string + example: alert + enum: + - alert + add_user_comment_request_properties: + title: Add case comment request properties for user comments + description: Defines properties for case comment requests when type is user. + type: object + properties: + comment: + description: The new comment. It is required only when `type` is `user`. + type: string + example: A new comment. + owner: + $ref: '#/components/schemas/owners' + type: + type: string + description: The type of comment. + example: user + enum: + - user + required: + - comment + - owner + - type + add_case_comment_request: + title: Add case comment request + description: The add comment to case API request body varies depending on whether you are adding an alert or a comment. + discriminator: + propertyName: type + oneOf: + - $ref: '#/components/schemas/add_alert_comment_request_properties' + - $ref: '#/components/schemas/add_user_comment_request_properties' + update_alert_comment_request_properties: + title: Update case comment request properties for alerts + description: Defines properties for case comment requests when type is alert. + required: + - alertId + - id + - index + - owner + - rule + - type + - version + type: object + properties: + alertId: + $ref: '#/components/schemas/alert_identifiers' + id: + type: string + description: | + The identifier for the comment. To retrieve comment IDs, use the get comments API. + example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + index: + $ref: '#/components/schemas/alert_indices' + owner: + $ref: '#/components/schemas/owners' + rule: + $ref: '#/components/schemas/rule' + type: + description: The type of comment. + type: string + enum: + - alert + example: alert + version: + description: | + The current comment version. To retrieve version values, use the get comments API. + type: string + example: Wzk1LDFd + update_user_comment_request_properties: + title: Update case comment request properties for user comments + description: Defines properties for case comment requests when type is user. + type: object + properties: + comment: + description: The new comment. It is required only when `type` is `user`. + type: string + example: A new comment. + id: + type: string + description: | + The identifier for the comment. To retrieve comment IDs, use the get comments API. + example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + owner: + $ref: '#/components/schemas/owners' + type: + type: string + description: The type of comment. + enum: + - user + example: user + version: + description: | + The current comment version. To retrieve version values, use the get comments API. + type: string + example: Wzk1LDFd + required: + - comment + - id + - owner + - type + - version + update_case_comment_request: + title: Update case comment request + description: The update case comment API request body varies depending on whether you are updating an alert or a comment. + discriminator: + propertyName: type + oneOf: + - $ref: '#/components/schemas/update_alert_comment_request_properties' + - $ref: '#/components/schemas/update_user_comment_request_properties' + examples: + add_comment_request: + summary: Adds a comment to a case. + value: + type: user + comment: A new comment. + owner: cases + add_comment_response: + summary: The add comment to case API returns a JSON object that contains details about the case and its comments. + value: + comments: + - id: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNDMxLDFd + type: user + owner: cases + comment: A new comment. + created_at: '2022-06-02T00:49:47.716Z' + created_by: + username: elastic + email: null + full_name: null + totalAlerts: 0 + id: 293f1bc0-74f6-11ea-b83a-553aecdb28b6 + version: WzIzMzgsMV0= + totalComment: 1 + title: Case title 1 + tags: + - tag 1 + description: A case description. + settings: + syncAlerts: false + owner: cases + duration: null + severity: low + closed_at: null + closed_by: null + created_at: '2022-03-24T00:37:03.906Z' + created_by: + username: elastic + full_name: null + email: null + status: open + updated_at: '2022-06-03T00:49:47.716Z' + updated_by: + username: elastic + email: null + full_name: null + connector: + id: none + name: none + type: .none + fields: null + external_service: null + update_comment_request: + summary: Updates a comment of a case. + value: + id: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + version: Wzk1LDFd + type: user + comment: An updated comment. + owner: cases + update_comment_response: + summary: The add comment to case API returns a JSON object that contains details about the case and its comments. + value: + comments: + - id: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNjM3LDFd + comment: An updated comment. + type: user + owner: cases + created_at: '2022-03-24T00:37:10.832Z' + created_by: + username: elastic + full_name: null + email: null + pushed_at: null + pushed_by: null + updated_at: '2022-03-24T01:27:06.210Z' + updated_by: + username: elastic + full_name: null + email: null + totalAlerts: 0 + id: 293f1bc0-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNjM2LDFd + totalComment: 1 + title: Case title 1 + tags: + - tag 1 + description: A case description. + settings: + syncAlerts: false + owner: cases + duration: null + severity: low + closed_at: null + closed_by: null + created_at: '2022-03-24T00:37:03.906Z' + created_by: + username: elastic + full_name: null + email: null + status: open + updated_at: '2022-03-24T01:27:06.210Z' + updated_by: + username: elastic + full_name: null + email: null + connector: + id: none + name: none + type: .none + fields: null + external_service: null +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/add_comment_response.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/add_comment_response.yaml index ea825da377a3b..382629e4fab08 100644 --- a/x-pack/plugins/cases/docs/openapi/components/examples/add_comment_response.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/examples/add_comment_response.yaml @@ -1,58 +1,46 @@ summary: The add comment to case API returns a JSON object that contains details about the case and its comments. value: - { - "comments":[ - { - "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", - "version": "WzIwNDMxLDFd", - "type":"user", - "owner":"cases", - "comment":"A new comment.", - "created_at":"2022-06-02T00:49:47.716Z", - "created_by": { - "username": "elastic", - "email": null, - "full_name": null - }, - "pushed_at":null, - "pushed_by":null, - "updated_at":null, - "updated_by":null - } - ], - "totalAlerts":0, - "id":"293f1bc0-74f6-11ea-b83a-553aecdb28b6", - "version":"WzIzMzgsMV0=", - "totalComment":1, - "title": "Case title 1", - "tags": ["tag 1"], - "description": "A case description.", - "settings": { - "syncAlerts":false - }, - "owner": "cases", - "duration": null, - "severity": "low", - "closed_at": null, - "closed_by": null, - "created_at": "2022-03-24T00:37:03.906Z", - "created_by": { - "email": null, - "full_name": null, - "username": "elastic" - }, - "status": "open", - "updated_at": "2022-06-03T00:49:47.716Z", - "updated_by": { - "username": "elastic", - "email": null, - "full_name": null - }, - "connector": { - "id": "none", - "name": "none", - "type": ".none", - "fields": null - }, - "external_service": null - } \ No newline at end of file + comments: + - id: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNDMxLDFd + type: user + owner: cases + comment: A new comment. + created_at: '2022-06-02T00:49:47.716Z' + created_by: + username: elastic + email: null + full_name: null + totalAlerts: 0 + id: 293f1bc0-74f6-11ea-b83a-553aecdb28b6 + version: WzIzMzgsMV0= + totalComment: 1 + title: Case title 1 + tags: + - tag 1 + description: A case description. + settings: + syncAlerts: false + owner: cases + duration: null + severity: low + closed_at: null + closed_by: null + created_at: '2022-03-24T00:37:03.906Z' + created_by: + username: elastic + full_name: null + email: null + status: open + updated_at: '2022-06-03T00:49:47.716Z' + updated_by: + username: elastic + email: null + full_name: null + connector: + id: none + name: none + type: .none + fields: null + external_service: null + \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_request.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_request.yaml index 066830ce20777..e09bb8ad35f2d 100644 --- a/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_request.yaml @@ -4,5 +4,6 @@ value: "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", "version": "Wzk1LDFd", "type": "user", - "comment": "An updated comment." + "comment": "An updated comment.", + "owner": "cases" } \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_response.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_response.yaml index 9a3ba642d6ece..4c81e759a92f9 100644 --- a/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_response.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/examples/update_comment_response.yaml @@ -1,59 +1,52 @@ summary: The add comment to case API returns a JSON object that contains details about the case and its comments. value: - { - "comments":[{ - "id": "8af6ac20-74f6-11ea-b83a-553aecdb28b6", - "version": "WzIwNjM3LDFd", - "comment": "An updated comment.", - "type": "user", - "owner": "cases", - "created_at": "2022-03-24T00:37:10.832Z", - "created_by": { - "username": "elastic", - "full_name": null, - "email": null - }, - "pushed_at": null, - "pushed_by": null, - "updated_at": "2022-03-24T01:27:06.210Z", - "updated_by": { - "username": "elastic", - "full_name": null, - "email": null - } - } - ], - "totalAlerts": 0, - "id": "293f1bc0-74f6-11ea-b83a-553aecdb28b6", - "version": "WzIwNjM2LDFd", - "totalComment": 1, - "title": "Case title 1", - "tags": ["tag 1"], - "description": "A case description.", - "settings": {"syncAlerts":false}, - "owner": "cases", - "duration": null, - "severity": "low", - "closed_at": null, - "closed_by": null, - "created_at": "2022-03-24T00:37:03.906Z", - "created_by": { - "username": "elastic", - "full_name": null, - "email": null - }, - "status": "open", - "updated_at": "2022-03-24T01:27:06.210Z", - "updated_by": { - "username": "elastic", - "full_name": null, - "email": null - }, - "connector": { - "id": "none", - "name": "none", - "type": ".none", - "fields": null - }, - "external_service": null - } + comments: + - id: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNjM3LDFd + comment: An updated comment. + type: user + owner: cases + created_at: '2022-03-24T00:37:10.832Z' + created_by: + username: elastic + full_name: null + email: null + pushed_at: null + pushed_by: null + updated_at: '2022-03-24T01:27:06.210Z' + updated_by: + username: elastic + full_name: null + email: null + totalAlerts: 0 + id: 293f1bc0-74f6-11ea-b83a-553aecdb28b6 + version: WzIwNjM2LDFd + totalComment: 1 + title: Case title 1 + tags: + - tag 1 + description: A case description. + settings: + syncAlerts: false + owner: cases + duration: null + severity: low + closed_at: null + closed_by: null + created_at: '2022-03-24T00:37:03.906Z' + created_by: + username: elastic + full_name: null + email: null + status: open + updated_at: '2022-03-24T01:27:06.210Z' + updated_by: + username: elastic + full_name: null + email: null + connector: + id: none + name: none + type: .none + fields: null + external_service: null diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml index beae115acce08..eebde85823746 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml @@ -4,4 +4,4 @@ description: The identifier for the case. To retrieve case IDs, use the find cas required: true schema: type: string - example: '9c235210-6834-11ea-a78c-6ffb38a34414' \ No newline at end of file + example: 9c235210-6834-11ea-a78c-6ffb38a34414 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml index ec2e69f66c1e4..c99ebb19cc818 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml @@ -1,55 +1,24 @@ +title: Add case comment request properties for alerts +required: + - alertId + - index + - owner + - rule + - type +description: Defines properties for case comment requests when type is alert. type: object properties: - alertId: - description: > - The alert identifier. It is required only when `type` is `alert`. If it is - an array, `index` must also be an array. This functionality is in - technical preview and may be changed or removed in a future release. - Elastic will apply best effort to fix any issues, but features in - technical preview are not subject to the support SLA of official GA - features. - oneOf: - - type: string - - type: array - items: - type: string - x-technical-preview: true - example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 + alertId: + $ref: 'alert_identifiers.yaml' index: - description: > - The alert index. It is required only when `type` is `alert`. If it is an - array, `alertId` must also be an array. This functionality is in technical - preview and may be changed or removed in a future release. Elastic will - apply best effort to fix any issues, but features in technical preview are - not subject to the support SLA of official GA features. - oneOf: - - type: string - - type: array - items: - type: string - x-technical-preview: true + $ref: 'alert_indices.yaml' owner: $ref: 'owners.yaml' rule: - description: > - The rule that is associated with the alert. It is required only when - `type` is `alert`. This functionality is in technical preview and may be - changed or removed in a future release. Elastic will apply best effort to - fix any issues, but features in technical preview are not subject to the - support SLA of official GA features. - type: object - x-technical-preview: true - properties: - $ref: 'rule_properties.yaml' + $ref: 'rule.yaml' type: description: The type of comment. type: string - enum: - - alert example: alert -required: - - alertId - - index - - owner - - rule - - type \ No newline at end of file + enum: + - alert \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/add_case_comment_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/add_case_comment_request.yaml new file mode 100644 index 0000000000000..70ed1324fde90 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/add_case_comment_request.yaml @@ -0,0 +1,9 @@ +title: Add case comment request +description: >- + The add comment to case API request body varies depending on whether you are + adding an alert or a comment. +discriminator: + propertyName: type +oneOf: + - $ref: 'add_alert_comment_request_properties.yaml' + - $ref: 'add_user_comment_request_properties.yaml' diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml index d09958e13fec8..40efb7f945f45 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml @@ -1,3 +1,5 @@ +title: Add case comment request properties for user comments +description: Defines properties for case comment requests when type is user. type: object properties: comment: @@ -9,9 +11,9 @@ properties: type: type: string description: The type of comment. + example: user enum: - user - example: user required: - comment - owner diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml index 4fcbfe5527e96..056abad0a300b 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml @@ -1,5 +1,7 @@ - +title: Add case comment response properties for alerts type: object +required: + - type properties: alertId: type: string @@ -37,6 +39,8 @@ properties: type: type: string example: alert + enum: + - alert updated_at: type: string format: date-time diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml new file mode 100644 index 0000000000000..4a470d8ff4475 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml @@ -0,0 +1,15 @@ +title: Alert identifiers +description: > + The alert identifier. It is required only when `type` is `alert`. If it is + an array, `index` must also be an array with the same length or number of + elements. This functionality is in technical preview and may be changed or + removed in a future release. Elastic will apply best effort to fix any issues, + but features in technical preview are not subject to the support SLA of + official GA features. +oneOf: + - type: string + - type: array + items: + type: string +x-technical-preview: true +example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_indices.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_indices.yaml new file mode 100644 index 0000000000000..b265f4ee8ce3b --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_indices.yaml @@ -0,0 +1,14 @@ +title: Alert indices +description: > + The alert index. It is required only when `type` is `alert`. If it is an + array, `alertId` must also be an array with the same length or number of + elements. This functionality is in technical preview and may be changed or + removed in a future release. Elastic will apply best effort to fix any issues, + but features in technical preview are not subject to the support SLA of + official GA features. +oneOf: + - type: string + - type: array + items: + type: string +x-technical-preview: true \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml new file mode 100644 index 0000000000000..95bd14e4957a3 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml @@ -0,0 +1,9 @@ +title: Case response properties for closed_by +type: object +nullable: true +properties: + $ref: 'user_properties.yaml' +required: + - email + - full_name + - username \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml new file mode 100644 index 0000000000000..1f8aa7c90190c --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml @@ -0,0 +1,52 @@ +title: Case response properties for connector fields +type: object +description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. +nullable: true +properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors. + type: string + destIp: + description: A comma-separated list of destination IPs for ServiceNow SecOps connectors. + type: string + impact: + description: The effect an incident had on business for ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: A comma-separated list of malware hashes for ServiceNow SecOps connectors. + type: string + malwareUrl: + description: A comma-separated list of malware URLs for ServiceNow SecOps connectors. + type: string + parent: + description: The key of the parent issue, when the issue type is sub-task for Jira connectors. + type: string + priority: + description: The priority of the issue for Jira and ServiceNow SecOps connectors. + type: string + severity: + description: The severity of the incident for ServiceNow ITSM connectors. + type: string + severityCode: + description: The severity code of the incident for IBM Resilient connectors. + type: number + sourceIp: + description: A comma-separated list of source IPs for ServiceNow SecOps connectors. + type: string + subcategory: + description: The subcategory of the incident for ServiceNow ITSM connectors. + type: string + urgency: + description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. + type: string \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_created_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_created_by_properties.yaml new file mode 100644 index 0000000000000..a58d7ff573868 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_created_by_properties.yaml @@ -0,0 +1,8 @@ +title: Case response properties for created_by +type: object +properties: + $ref: 'user_properties.yaml' +required: + - email + - full_name + - username \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml index 25f6296585192..1be19845c56ae 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml @@ -1,82 +1,112 @@ -closed_at: - type: string - format: date-time - nullable: true - example: null -closed_by: - type: object - properties: - $ref: 'user_properties.yaml' - nullable: true - example: null -comments: - type: array - items: - oneOf: - - $ref: 'alert_comment_response_properties.yaml' - - $ref: 'user_comment_response_properties.yaml' - example: [] -connector: - type: object - properties: - $ref: 'connector_properties.yaml' -created_at: - type: string - format: date-time - example: 2022-05-13T09:16:17.416Z -created_by: - type: object - properties: - $ref: 'user_properties.yaml' -description: - type: string - example: "A case description." -duration: - type: integer - description: > - The elapsed time from the creation of the case to its closure (in seconds). - If the case has not been closed, the duration is set to null. If the case - was closed after less than half a second, the duration is rounded down to - zero. - example: 120 -external_service: - $ref: 'external_service.yaml' -id: - type: string - example: 66b9aa00-94fa-11ea-9f74-e7e108796192 -owner: - $ref: 'owners.yaml' -settings: - $ref: 'settings.yaml' -severity: - $ref: 'severity_property.yaml' -status: - $ref: 'status.yaml' -tags: - type: array - items: +title: Case response properties +type: object +required: + - closed_at + - closed_by + - comments + - connector + - created_at + - created_by + - description + - duration + - external_service + - id + - owner + - settings + - severity + - status + - tags + - title + - totalAlerts + - totalComment + - updated_at + - updated_by + - version +properties: + closed_at: type: string - example: ["tag-1"] -title: - type: string - example: Case title 1 -totalAlerts: - type: integer - example: 0 -totalComment: - type: integer - example: 0 -updated_at: - type: string - format: date-time - nullable: true - example: null -updated_by: - type: object - properties: - $ref: 'user_properties.yaml' - nullable: true - example: null -version: - type: string - example: WzUzMiwxXQ== + format: date-time + nullable: true + closed_by: + $ref: 'case_response_closed_by_properties.yaml' + comments: + title: Case response properties for comments + description: An array of comment objects for the case. + type: array + items: + discriminator: + propertyName: type + oneOf: + - $ref: 'alert_comment_response_properties.yaml' + - $ref: 'user_comment_response_properties.yaml' + connector: + title: Case response properties for connectors + type: object + properties: + fields: + $ref: 'case_response_connector_field_properties.yaml' + id: + description: The identifier for the connector. To create a case without a connector, use `none`. + type: string + example: none + name: + description: The name of the connector. To create a case without a connector, use `none`. + type: string + example: none + type: + $ref: 'connector_types.yaml' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + $ref: 'case_response_created_by_properties.yaml' + description: + type: string + example: A case description. + duration: + type: integer + description: > + The elapsed time from the creation of the case to its closure (in seconds). + If the case has not been closed, the duration is set to null. If the case + was closed after less than half a second, the duration is rounded down to + zero. + nullable: true + example: 120 + external_service: + $ref: 'external_service.yaml' + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: 'owners.yaml' + settings: + $ref: 'settings.yaml' + severity: + $ref: 'severity_property.yaml' + status: + $ref: 'status.yaml' + tags: + type: array + items: + type: string + example: + - tag-1 + title: + type: string + example: Case title 1 + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + updated_by: + $ref: 'case_response_updated_by_properties.yaml' + version: + type: string + example: WzUzMiwxXQ== diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml new file mode 100644 index 0000000000000..c59a5565c98b9 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml @@ -0,0 +1,9 @@ +title: Case response properties for pushed_by +type: object +nullable: true +properties: + $ref: 'user_properties.yaml' +required: + - email + - full_name + - username \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml new file mode 100644 index 0000000000000..cd1bae033f2ff --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml @@ -0,0 +1,9 @@ +title: Case response properties for updated_by +type: object +nullable: true +properties: + $ref: 'user_properties.yaml' +required: + - email + - full_name + - username \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml index 950a2bab05603..b3b3182b8c964 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml @@ -1,4 +1,5 @@ type: object +nullable: true properties: connector_id: type: string diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml new file mode 100644 index 0000000000000..8f18d420ae910 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml @@ -0,0 +1,18 @@ +title: Alerting rule +description: > + The rule that is associated with the alert. It is required only when + `type` is `alert`. This functionality is in technical preview and may be + changed or removed in a future release. Elastic will apply best effort to + fix any issues, but features in technical preview are not subject to the + support SLA of official GA features. +type: object +x-technical-preview: true +properties: + id: + description: The rule identifier. + type: string + example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + name: + description: The rule name. + type: string + example: security_rule \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml index 2d91b007d4310..2c7bd5dcc1215 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml @@ -1,20 +1,17 @@ +title: Update case comment request properties for alerts +description: Defines properties for case comment requests when type is alert. +required: + - alertId + - id + - index + - owner + - rule + - type + - version type: object properties: - alertId: - description: > - The alert identifier. It is required only when `type` is `alert`. If it is - an array, `index` must also be an array. This functionality is in - technical preview and may be changed or removed in a future release. - Elastic will apply best effort to fix any issues, but features in - technical preview are not subject to the support SLA of official GA - features. - oneOf: - - type: string - - type: array - items: - type: string - x-technical-preview: true - example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 + alertId: + $ref: 'alert_identifiers.yaml' id: type: string description: > @@ -22,31 +19,11 @@ properties: get comments API. example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 index: - description: > - The alert index. It is required only when `type` is `alert`. If it is an - array, `alertId` must also be an array. This functionality is in technical - preview and may be changed or removed in a future release. Elastic will - apply best effort to fix any issues, but features in technical preview are - not subject to the support SLA of official GA features. - oneOf: - - type: string - - type: array - items: - type: string - x-technical-preview: true + $ref: 'alert_indices.yaml' owner: $ref: 'owners.yaml' rule: - description: > - The rule that is associated with the alert. It is required only when - `type` is `alert`. This functionality is in technical preview and may be - changed or removed in a future release. Elastic will apply best effort to - fix any issues, but features in technical preview are not subject to the - support SLA of official GA features. - type: object - x-technical-preview: true - properties: - $ref: 'rule_properties.yaml' + $ref: 'rule.yaml' type: description: The type of comment. type: string @@ -58,12 +35,4 @@ properties: The current comment version. To retrieve version values, use the get comments API. type: string - example: Wzk1LDFd -required: - - alertId - - id - - index - - owner - - rule - - type - - version \ No newline at end of file + example: Wzk1LDFd \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_comment_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_comment_request.yaml new file mode 100644 index 0000000000000..cc3974c29194f --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_comment_request.yaml @@ -0,0 +1,9 @@ +title: Update case comment request +description: >- + The update case comment API request body varies depending on whether you are + updating an alert or a comment. +discriminator: + propertyName: type +oneOf: + - $ref: 'update_alert_comment_request_properties.yaml' + - $ref: 'update_user_comment_request_properties.yaml' diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml index 83d7f0715da21..22fb76d9bba74 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml @@ -1,3 +1,5 @@ +title: Update case comment request properties for user comments +description: Defines properties for case comment requests when type is user. type: object properties: comment: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml index 0df26aee07587..b1727d3279abe 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml @@ -1,4 +1,7 @@ +title: Case response properties for user comments type: object +required: + - type properties: comment: type: string @@ -8,9 +11,7 @@ properties: format: date-time example: 2022-05-13T09:16:17.416Z created_by: - type: object - properties: - $ref: 'user_properties.yaml' + $ref: 'case_response_created_by_properties.yaml' id: type: string example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 @@ -22,25 +23,19 @@ properties: nullable: true example: null pushed_by: - type: object - properties: - $ref: 'user_properties.yaml' - nullable: true - example: null + $ref: 'case_response_pushed_by_properties.yaml' type: type: string example: user + enum: + - user updated_at: type: string format: date-time nullable: true example: null updated_by: - type: object - properties: - $ref: 'user_properties.yaml' - nullable: true - example: null + $ref: 'case_response_updated_by_properties.yaml' version: type: string example: WzIwNDMxLDFd \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml index 1c3596dc6f9b9..19b76a6000c02 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml @@ -1,12 +1,15 @@ email: type: string example: null + nullable: true full_name: type: string example: null + nullable: true username: type: string example: elastic + nullable: true profile_uid: type: string example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 diff --git a/x-pack/plugins/cases/docs/openapi/entrypoint-min.yaml b/x-pack/plugins/cases/docs/openapi/entrypoint-min.yaml new file mode 100644 index 0000000000000..595d33a7465d2 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/entrypoint-min.yaml @@ -0,0 +1,59 @@ +openapi: 3.0.1 +info: + title: Cases + description: OpenAPI schema for Cases endpoints + version: '0.2' + contact: + name: Cases Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: cases + description: Case APIs enable you to open and track issues. +servers: + - url: 'http://localhost:5601' + description: local +paths: +# '/s/{spaceId}/api/cases': +# $ref: 'paths/s@{spaceid}@api@cases.yaml' +# '/s/{spaceId}/api/cases/_find': +# $ref: 'paths/s@{spaceid}@api@cases@_find.yaml' +# '/s/{spaceId}/api/cases/alerts/{alertId}': +# $ref: 'paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml' +# '/s/{spaceId}/api/cases/configure': +# $ref: paths/s@{spaceid}@api@cases@configure.yaml +# '/s/{spaceId}/api/cases/configure/{configurationId}': +# $ref: paths/s@{spaceid}@api@cases@configure@{configurationid}.yaml +# '/s/{spaceId}/api/cases/configure/connectors/_find': +# $ref: paths/s@{spaceid}@api@cases@configure@connectors@_find.yaml +# '/s/{spaceId}/api/cases/reporters': +# $ref: 'paths/s@{spaceid}@api@cases@reporters.yaml' +# '/s/{spaceId}/api/cases/status': +# $ref: 'paths/s@{spaceid}@api@cases@status.yaml' +# '/s/{spaceId}/api/cases/tags': +# $ref: 'paths/s@{spaceid}@api@cases@tags.yaml' +# '/s/{spaceId}/api/cases/{caseId}': +# $ref: 'paths/s@{spaceid}@api@cases@{caseid}.yaml' +# '/s/{spaceId}/api/cases/{caseId}/alerts': +# $ref: 'paths/s@{spaceid}@api@cases@{caseid}@alerts.yaml' + '/s/{spaceId}/api/cases/{caseId}/comments': + $ref: 'paths/s@{spaceid}@api@cases@{caseid}@comments.yaml' +# '/s/{spaceId}/api/cases/{caseId}/comments/{commentId}': +# $ref: 'paths/s@{spaceid}@api@cases@{caseid}@comments@{commentid}.yaml' +# '/s/{spaceId}/api/cases/{caseId}/connector/{connectorId}/_push': +# $ref: 'paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml' +# '/s/{spaceId}/api/cases/{caseId}/user_actions': +# $ref: 'paths/s@{spaceid}@api@cases@{caseid}@user_actions.yaml' +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/entrypoint.yaml b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml index 6995d1482e0a9..6f467f77ef93c 100644 --- a/x-pack/plugins/cases/docs/openapi/entrypoint.yaml +++ b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml @@ -11,8 +11,6 @@ info: tags: - name: cases description: Case APIs enable you to open and track issues. - - name: kibana - description: Kibana APIs enable you to interact with Kibana features. servers: - url: 'http://localhost:5601' description: local diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml index 59abb58531821..80092f32cbe60 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml @@ -57,11 +57,9 @@ post: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: createCaseResponse: $ref: '../components/examples/create_case_response.yaml' @@ -160,11 +158,9 @@ patch: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: updateCaseResponse: $ref: '../components/examples/update_case_response.yaml' diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@_find.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@_find.yaml index a260321248357..6d5bb0c2812cd 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@_find.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@_find.yaml @@ -138,9 +138,7 @@ get: cases: type: array items: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' count_closed_cases: type: integer count_in_progress_cases: diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}.yaml index 9e8ca4660c44d..d32d24b9fa01b 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}.yaml @@ -21,11 +21,9 @@ get: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: getCaseResponse: $ref: '../components/examples/get_case_response.yaml' diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments.yaml index c8a045267a492..2246ff1d16a9d 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments.yaml @@ -12,12 +12,11 @@ post: - $ref: '../components/parameters/case_id.yaml' - $ref: '../components/parameters/space_id.yaml' requestBody: + required: true content: application/json: schema: - oneOf: - - $ref: '../components/schemas/add_alert_comment_request_properties.yaml' - - $ref: '../components/schemas/add_user_comment_request_properties.yaml' + $ref: '../components/schemas/add_case_comment_request.yaml' examples: createCaseCommentRequest: $ref: '../components/examples/add_comment_request.yaml' @@ -25,11 +24,9 @@ post: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: createCaseCommentResponse: $ref: '../components/examples/add_comment_response.yaml' @@ -70,12 +67,11 @@ patch: - $ref: '../components/parameters/case_id.yaml' - $ref: '../components/parameters/space_id.yaml' requestBody: + required: true content: application/json: schema: - oneOf: - - $ref: '../components/schemas/update_alert_comment_request_properties.yaml' - - $ref: '../components/schemas/update_user_comment_request_properties.yaml' + $ref: '../components/schemas/update_case_comment_request.yaml' examples: updateCaseCommentRequest: $ref: '../components/examples/update_comment_request.yaml' @@ -83,11 +79,9 @@ patch: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: updateCaseCommentResponse: $ref: '../components/examples/update_comment_response.yaml' @@ -111,14 +105,10 @@ get: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: array - items: - anyOf: - - $ref: '../components/schemas/alert_comment_response_properties.yaml' - - $ref: '../components/schemas/user_comment_response_properties.yaml' - examples: {} + $ref: '../components/schemas/case_response_properties.yaml' + servers: - url: https://localhost:5601 diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml index 32caad2bc4086..37587abd760e1 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml @@ -21,11 +21,9 @@ post: '200': description: Indicates a successful call. content: - application/json; charset=utf-8: + application/json: schema: - type: object - properties: - $ref: '../components/schemas/case_response_properties.yaml' + $ref: '../components/schemas/case_response_properties.yaml' examples: pushCaseResponse: $ref: '../components/examples/push_case_response.yaml' diff --git a/x-pack/plugins/cases/public/components/all_cases/table.tsx b/x-pack/plugins/cases/public/components/all_cases/table.tsx index b85f4ae1826d5..1f7382351e802 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table.tsx @@ -82,7 +82,7 @@ export const CasesTable: FunctionComponent = ({
) : ( -
+ <> = ({ sorting={sorting} hasActions={false} /> -
+ ); }; CasesTable.displayName = 'CasesTable'; diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 415472574f25b..6daf9cb665116 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -6,18 +6,18 @@ */ import React, { FunctionComponent, useCallback, useState } from 'react'; -import { EuiContextMenu } from '@elastic/eui'; import { - UtilityBar, - UtilityBarAction, - UtilityBarGroup, - UtilityBarSection, - UtilityBarText, -} from '../utility_bar'; + EuiButtonEmpty, + EuiContextMenu, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiText, + useEuiTheme, +} from '@elastic/eui'; import * as i18n from './translations'; import { Case } from '../../../common/ui/types'; import { useRefreshCases } from './use_on_refresh_cases'; -import { UtilityBarBulkActions } from '../utility_bar/utility_bar_bulk_actions'; import { useBulkActions } from './use_bulk_actions'; import { useCasesContext } from '../cases_context/use_cases_context'; @@ -30,6 +30,7 @@ interface Props { export const CasesTableUtilityBar: FunctionComponent = React.memo( ({ isSelectorView, totalCases, selectedCases, deselectCases }) => { + const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); const closePopover = useCallback(() => setIsPopoverOpen(false), []); @@ -56,47 +57,82 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( return ( <> - - - - - {i18n.SHOWING_CASES(totalCases)} - - - + + + + {i18n.SHOWING_CASES(totalCases)} + + + + {!isSelectorView && showBulkActions && ( <> - - {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} - - - - + + + {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} + + + + + {i18n.BULK_ACTIONS} + + } + > + + + )} - - {i18n.REFRESH} - - - - + + + {i18n.REFRESH} + + + + + {modals} ); diff --git a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar.test.tsx.snap b/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar.test.tsx.snap deleted file mode 100644 index 83c8a16ea0290..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar.test.tsx.snap +++ /dev/null @@ -1,29 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UtilityBar it renders 1`] = ` - - - - - Test text - - - - - Test action - - - - - - - Test action - - - - -`; diff --git a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap b/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap deleted file mode 100644 index 8ef7ee1cfe842..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UtilityBarGroup it renders 1`] = ` - - - Test text - - -`; diff --git a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap b/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap deleted file mode 100644 index 2fe3b8ac5c7aa..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UtilityBarSection it renders 1`] = ` - - - - Test text - - - -`; diff --git a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap b/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap deleted file mode 100644 index cf635ffa49c4c..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UtilityBarText it renders 1`] = ` - - Test text - -`; diff --git a/x-pack/plugins/cases/public/components/utility_bar/index.ts b/x-pack/plugins/cases/public/components/utility_bar/index.ts deleted file mode 100644 index 830f3cb043ba9..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { UtilityBar } from './utility_bar'; -export { UtilityBarAction } from './utility_bar_action'; -export { UtilityBarGroup } from './utility_bar_group'; -export { UtilityBarSection } from './utility_bar_section'; -export { UtilityBarSpacer } from './utility_bar_spacer'; -export { UtilityBarText } from './utility_bar_text'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/styles.tsx b/x-pack/plugins/cases/public/components/utility_bar/styles.tsx deleted file mode 100644 index 4c4427ba23471..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/styles.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import styled, { css } from 'styled-components'; - -/** - * UTILITY BAR - */ - -export interface BarProps { - border?: boolean; -} - -export interface BarSectionProps { - grow?: boolean; -} - -export interface BarGroupProps { - grow?: boolean; -} - -export const Bar = styled.aside.attrs({ - className: 'casesUtilityBar', -})` - ${({ border, theme }) => css` - ${border && - css` - border-bottom: ${theme.eui.euiBorderThin}; - padding-bottom: ${theme.eui.euiSizeS}; - `} - - @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) { - display: flex; - justify-content: space-between; - } - `} -`; -Bar.displayName = 'Bar'; - -export const BarSection = styled.div.attrs({ - className: 'casesUtilityBar__section', -})` - ${({ grow, theme }) => css` - & + & { - margin-top: ${theme.eui.euiSizeS}; - } - - @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) { - display: flex; - flex-wrap: wrap; - } - - @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) { - & + & { - margin-top: 0; - margin-left: ${theme.eui.euiSize}; - } - } - ${grow && - css` - flex: 1; - `} - `} -`; -BarSection.displayName = 'BarSection'; - -export const BarGroup = styled.div.attrs({ - className: 'casesUtilityBar__group', -})` - ${({ grow, theme }) => css` - align-items: flex-start; - display: flex; - flex-wrap: wrap; - - & + & { - margin-top: ${theme.eui.euiSizeS}; - } - - @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) { - border-right: ${theme.eui.euiBorderThin}; - flex-wrap: nowrap; - margin-right: ${theme.eui.euiSizeM}; - padding-right: ${theme.eui.euiSizeM}; - - & + & { - margin-top: 0; - } - - &:last-child { - border-right: none; - margin-right: 0; - padding-right: 0; - } - } - - & > * { - margin-right: ${theme.eui.euiSize}; - - &:last-child { - margin-right: 0; - } - } - ${grow && - css` - flex: 1; - `} - `} -`; -BarGroup.displayName = 'BarGroup'; - -export const BarText = styled.p.attrs({ - className: 'casesUtilityBar__text', -})` - ${({ theme }) => css` - color: ${theme.eui.euiTextSubduedColor}; - font-size: ${theme.eui.euiFontSizeXS}; - line-height: ${theme.eui.euiLineHeight}; - white-space: nowrap; - `} -`; -BarText.displayName = 'BarText'; - -export const BarAction = styled.div.attrs({ - className: 'casesUtilityBar__action', -})` - ${({ theme }) => css` - font-size: ${theme.eui.euiFontSizeXS}; - line-height: ${theme.eui.euiLineHeight}; - `} -`; -BarAction.displayName = 'BarAction'; - -export const BarSpacer = styled.div.attrs({ - className: 'casesUtilityBar__spacer', -})` - ${() => css` - flex: 1; - `} -`; -BarSpacer.displayName = 'BarSpacer'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar.test.tsx deleted file mode 100644 index 52486e32905db..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar.test.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { euiDarkVars } from '@kbn/ui-theme'; -import { mount, shallow } from 'enzyme'; -import React from 'react'; -import { TestProviders } from '../../common/mock'; - -import { - UtilityBar, - UtilityBarAction, - UtilityBarGroup, - UtilityBarSection, - UtilityBarText, -} from '.'; - -describe('UtilityBar', () => { - test('it renders', () => { - const wrapper = shallow( - - - - - {'Test text'} - - - - {'Test action'} - - - - - - {'Test action'} - - - - - ); - - expect(wrapper.find('UtilityBar')).toMatchSnapshot(); - }); - - test('it applies border styles when border is true', () => { - const wrapper = mount( - - - - - {'Test text'} - - - - {'Test action'} - - - - - - {'Test action'} - - - - - ); - const casesUtilityBar = wrapper.find('.casesUtilityBar').first(); - - expect(casesUtilityBar).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); - expect(casesUtilityBar).toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeS); - }); - - test('it DOES NOT apply border styles when border is false', () => { - const wrapper = mount( - - - - - {'Test text'} - - - - {'Test action'} - - - - - - {'Test action'} - - - - - ); - const casesUtilityBar = wrapper.find('.casesUtilityBar').first(); - - expect(casesUtilityBar).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); - expect(casesUtilityBar).not.toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeS); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar.tsx deleted file mode 100644 index ff47459d437be..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { Bar, BarProps } from './styles'; - -interface UtilityBarProps extends BarProps { - children: React.ReactNode; -} - -export const UtilityBar = React.memo(({ border, children }) => ( - {children} -)); - -UtilityBar.displayName = 'UtilityBar'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.test.tsx deleted file mode 100644 index 881f4e922bcab..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; -import { UtilityBarAction } from '.'; - -describe('UtilityBarAction', () => { - let appMockRenderer: AppMockRenderer; - const dataTestSubj = 'test-bar-action'; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRenderer = createAppMockRenderer(); - }); - - test('it renders', () => { - const res = appMockRenderer.render( - - {'Test action'} - - ); - - expect(res.getByTestId(dataTestSubj)).toBeInTheDocument(); - expect(res.getByText('Test action')).toBeInTheDocument(); - }); - - test('it renders a popover', () => { - const res = appMockRenderer.render( - - {'Test action'} - - ); - - expect(res.getByTestId(`${dataTestSubj}-link-icon`)).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.tsx deleted file mode 100644 index b0748f1dd7c9f..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { LinkIcon, LinkIconProps } from '../link_icon'; -import { BarAction } from './styles'; - -export interface UtilityBarActionProps extends LinkIconProps { - dataTestSubj?: string; -} - -export const UtilityBarAction = React.memo( - ({ dataTestSubj, children, color, disabled, href, iconSide, iconSize, iconType, onClick }) => { - return ( - - - {children} - - - ); - } -); - -UtilityBarAction.displayName = 'UtilityBarAction'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.test.tsx deleted file mode 100644 index fa3372cf52331..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { act } from 'react-dom/test-utils'; - -import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; -import { UtilityBarBulkActions } from './utility_bar_bulk_actions'; - -describe('UtilityBarBulkActions', () => { - let appMockRenderer: AppMockRenderer; - const closePopover = jest.fn(); - const onButtonClick = jest.fn(); - const dataTestSubj = 'test-bar-action'; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRenderer = createAppMockRenderer(); - }); - - it('renders', () => { - const res = appMockRenderer.render( - - - {'Test bulk actions'} - - - ); - - expect(res.getByTestId(dataTestSubj)).toBeInTheDocument(); - expect(res.getByText('button title')).toBeInTheDocument(); - }); - - it('renders a popover', async () => { - const res = appMockRenderer.render( - - - {'Test bulk actions'} - - - ); - - expect(res.getByText('Test bulk actions')).toBeInTheDocument(); - }); - - it('calls onButtonClick', async () => { - const res = appMockRenderer.render( - - - {'Test bulk actions'} - - - ); - - expect(res.getByText('Test bulk actions')).toBeInTheDocument(); - - act(() => { - userEvent.click(res.getByText('button title')); - }); - - expect(onButtonClick).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.tsx deleted file mode 100644 index afeb93cc221ea..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_bulk_actions.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 { EuiPopover } from '@elastic/eui'; -import React from 'react'; -import { LinkIcon, LinkIconProps } from '../link_icon'; - -import { BarAction } from './styles'; - -export interface UtilityBarActionProps extends Omit { - isPopoverOpen: boolean; - buttonTitle: string; - closePopover: () => void; - onButtonClick: () => void; - dataTestSubj?: string; -} - -export const UtilityBarBulkActions = React.memo( - ({ - dataTestSubj, - children, - color, - disabled, - href, - iconSide, - iconSize, - iconType, - isPopoverOpen, - onButtonClick, - buttonTitle, - closePopover, - }) => { - return ( - - - {buttonTitle} - - } - > - {children} - - - ); - } -); - -UtilityBarBulkActions.displayName = 'UtilityBarBulkActions'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.test.tsx deleted file mode 100644 index bf7bb34ab5b64..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.test.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import { TestProviders } from '../../common/mock'; -import { UtilityBarGroup, UtilityBarText } from '.'; - -describe('UtilityBarGroup', () => { - test('it renders', () => { - const wrapper = shallow( - - - {'Test text'} - - - ); - - expect(wrapper.find('UtilityBarGroup')).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.tsx deleted file mode 100644 index ef83d6effc8a3..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_group.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { BarGroup, BarGroupProps } from './styles'; - -export interface UtilityBarGroupProps extends BarGroupProps { - children: React.ReactNode; -} - -export const UtilityBarGroup = React.memo(({ grow, children }) => ( - {children} -)); - -UtilityBarGroup.displayName = 'UtilityBarGroup'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.test.tsx deleted file mode 100644 index 142cca8fc9e32..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import { TestProviders } from '../../common/mock'; -import { UtilityBarGroup, UtilityBarSection, UtilityBarText } from '.'; - -describe('UtilityBarSection', () => { - test('it renders', () => { - const wrapper = shallow( - - - - {'Test text'} - - - - ); - - expect(wrapper.find('UtilityBarSection')).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.tsx deleted file mode 100644 index c84219cc63488..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_section.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { BarSection, BarSectionProps } from './styles'; - -export interface UtilityBarSectionProps extends BarSectionProps { - children: React.ReactNode; -} - -export const UtilityBarSection = React.memo(({ grow, children }) => ( - {children} -)); - -UtilityBarSection.displayName = 'UtilityBarSection'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_spacer.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_spacer.tsx deleted file mode 100644 index 11b3be8d656e4..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_spacer.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { BarSpacer } from './styles'; - -export interface UtilityBarSpacerProps { - dataTestSubj?: string; -} - -export const UtilityBarSpacer = React.memo(({ dataTestSubj }) => ( - -)); - -UtilityBarSpacer.displayName = 'UtilityBarSpacer'; diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.test.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.test.tsx deleted file mode 100644 index afeae82a19e85..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import { TestProviders } from '../../common/mock'; -import { UtilityBarText } from '.'; - -describe('UtilityBarText', () => { - test('it renders', () => { - const wrapper = shallow( - - {'Test text'} - - ); - - expect(wrapper.find('UtilityBarText')).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.tsx b/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.tsx deleted file mode 100644 index c0be3cbfbe202..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_text.tsx +++ /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 React from 'react'; - -import { BarText } from './styles'; - -export interface UtilityBarTextProps { - children: string | JSX.Element; - dataTestSubj?: string; -} - -export const UtilityBarText = React.memo(({ children, dataTestSubj }) => ( - {children} -)); - -UtilityBarText.displayName = 'UtilityBarText'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_requests_panel/crawl_requests_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_requests_panel/crawl_requests_panel.tsx index f5474d41aff81..75ff689819ff8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_requests_panel/crawl_requests_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_requests_panel/crawl_requests_panel.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiCode, EuiLink, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiCode, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DataPanel } from '../../../../../shared/data_panel/data_panel'; @@ -45,14 +45,6 @@ export const CrawlRequestsPanel: React.FC = () => { 'Requests originating from the crawler can be identified by the following User Agent. This is configured in your enterprise-search.yml file.', } )}{' '} - - {i18n.translate( - 'xpack.enterpriseSearch.crawler.crawlRequestsPanel.userAgentDocumentationLink', - { - defaultMessage: 'Learn more about Elastic crawler user agents', - } - )} - {data ? data.userAgent : ''} diff --git a/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.test.ts b/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.test.ts new file mode 100644 index 0000000000000..d9939afedaa5e --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; + +import { getMlInferenceErrors } from './get_inference_errors'; + +describe('getMlInferenceErrors', () => { + const indexName = 'my-index'; + + const mockClient = { + search: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should fetch aggregations and transform them', async () => { + mockClient.search.mockImplementation(() => + Promise.resolve({ + aggregations: { + errors: { + buckets: [ + { + key: 'Error message 1', + doc_count: 100, + max_error_timestamp: { + value: 1664977836100, + value_as_string: '2022-10-05T13:50:36.100Z', + }, + }, + { + key: 'Error message 2', + doc_count: 200, + max_error_timestamp: { + value: 1664977836200, + value_as_string: '2022-10-05T13:50:36.200Z', + }, + }, + ], + }, + }, + }) + ); + + const actualResult = await getMlInferenceErrors( + indexName, + mockClient as unknown as ElasticsearchClient + ); + + expect(actualResult).toEqual([ + { + message: 'Error message 1', + doc_count: 100, + timestamp: '2022-10-05T13:50:36.100Z', + }, + { + message: 'Error message 2', + doc_count: 200, + timestamp: '2022-10-05T13:50:36.200Z', + }, + ]); + expect(mockClient.search).toHaveBeenCalledTimes(1); + }); + + it('should return an empty array if there are no aggregates', async () => { + mockClient.search.mockImplementation(() => + Promise.resolve({ + aggregations: { + errors: [], + }, + }) + ); + + const actualResult = await getMlInferenceErrors( + indexName, + mockClient as unknown as ElasticsearchClient + ); + + expect(actualResult).toEqual([]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.ts b/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.ts new file mode 100644 index 0000000000000..1ced837f42f22 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/ml_inference_pipeline/get_inference_errors.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + AggregationsMultiBucketAggregateBase, + AggregationsStringRareTermsBucketKeys, +} from '@elastic/elasticsearch/lib/api/types'; + +import { ElasticsearchClient } from '@kbn/core/server'; + +export interface MlInferenceError { + message: string; + doc_count: number; + timestamp: string | undefined; // Date string +} + +export interface ErrorAggregationBucket extends AggregationsStringRareTermsBucketKeys { + max_error_timestamp: { + value: number | null; + value_as_string?: string; + }; +} + +/** + * Fetches an aggregate of distinct ML inference errors from the target index, along with the most + * recent error's timestamp and affected document count for each bucket. + * @param indexName the index to get the errors from. + * @param esClient the Elasticsearch Client to use to fetch the errors. + */ +export const getMlInferenceErrors = async ( + indexName: string, + esClient: ElasticsearchClient +): Promise => { + const searchResult = await esClient.search< + unknown, + { + errors: AggregationsMultiBucketAggregateBase; + } + >({ + index: indexName, + body: { + aggs: { + errors: { + terms: { + field: '_ingest.inference_errors.message.enum', + order: { + max_error_timestamp: 'desc', + }, + size: 20, + }, + aggs: { + max_error_timestamp: { + max: { + field: '_ingest.inference_errors.timestamp', + }, + }, + }, + }, + }, + size: 0, + }, + }); + + const errorBuckets = searchResult.aggregations?.errors.buckets; + if (!errorBuckets) { + return []; + } + + // Buckets are either in an array or in a Record, we transform them to an array + const buckets = Array.isArray(errorBuckets) ? errorBuckets : Object.values(errorBuckets); + + return buckets.map((bucket) => ({ + message: bucket.key, + doc_count: bucket.doc_count, + timestamp: bucket.max_error_timestamp?.value_as_string, + })); +}; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts index 435fb0892019f..64711a6ac20be 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts @@ -23,10 +23,14 @@ jest.mock('../../lib/indices/delete_ml_inference_pipeline', () => ({ jest.mock('../../lib/indices/exists_index', () => ({ indexOrAliasExists: jest.fn(), })); +jest.mock('../../lib/ml_inference_pipeline/get_inference_errors', () => ({ + getMlInferenceErrors: jest.fn(), +})); import { deleteMlInferencePipeline } from '../../lib/indices/delete_ml_inference_pipeline'; import { indexOrAliasExists } from '../../lib/indices/exists_index'; import { fetchMlInferencePipelineProcessors } from '../../lib/indices/fetch_ml_inference_pipeline_processors'; +import { getMlInferenceErrors } from '../../lib/ml_inference_pipeline/get_inference_errors'; import { createAndReferenceMlInferencePipeline } from '../../utils/create_ml_inference_pipeline'; import { ElasticsearchResponseError } from '../../utils/identify_exceptions'; @@ -40,6 +44,7 @@ describe('Enterprise Search Managed Indices', () => { putPipeline: jest.fn(), simulate: jest.fn(), }, + search: jest.fn(), }, }; @@ -47,6 +52,64 @@ describe('Enterprise Search Managed Indices', () => { elasticsearch: { client: mockClient }, }; + describe('GET /internal/enterprise_search/indices/{indexName}/ml_inference/errors', () => { + beforeEach(() => { + const context = { + core: Promise.resolve(mockCore), + } as unknown as jest.Mocked; + + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/indices/{indexName}/ml_inference/errors', + }); + + registerIndexRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('fails validation without index_name', () => { + const request = { + params: {}, + }; + mockRouter.shouldThrow(request); + }); + + it('fetches ML inference errors', async () => { + const errorsResult = [ + { + message: 'Error message 1', + doc_count: 100, + timestamp: '2022-10-05T13:50:36.100Z', + }, + { + message: 'Error message 2', + doc_count: 200, + timestamp: '2022-10-05T13:50:36.200Z', + }, + ]; + + (getMlInferenceErrors as jest.Mock).mockImplementationOnce(() => { + return Promise.resolve(errorsResult); + }); + + await mockRouter.callRoute({ + params: { indexName: 'my-index-name' }, + }); + + expect(getMlInferenceErrors).toHaveBeenCalledWith('my-index-name', mockClient.asCurrentUser); + + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: { + errors: errorsResult, + }, + headers: { 'content-type': 'application/json' }, + }); + }); + }); + describe('GET /internal/enterprise_search/indices/{indexName}/ml_inference/pipeline_processors', () => { beforeEach(() => { const context = { 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 ef6f8131ee2c1..38aef14fdaa81 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 @@ -9,6 +9,7 @@ import { IngestPutPipelineRequest, IngestSimulateRequest, } from '@elastic/elasticsearch/lib/api/types'; + import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; @@ -27,6 +28,7 @@ import { fetchIndex } from '../../lib/indices/fetch_index'; import { fetchIndices } from '../../lib/indices/fetch_indices'; import { fetchMlInferencePipelineProcessors } from '../../lib/indices/fetch_ml_inference_pipeline_processors'; import { generateApiKey } from '../../lib/indices/generate_api_key'; +import { getMlInferenceErrors } from '../../lib/ml_inference_pipeline/get_inference_errors'; import { createIndexPipelineDefinitions } from '../../lib/pipelines/create_pipeline_definitions'; import { getCustomPipelines } from '../../lib/pipelines/get_custom_pipelines'; import { getPipeline } from '../../lib/pipelines/get_pipeline'; @@ -524,6 +526,30 @@ export function registerIndexRoutes({ }) ); + router.get( + { + path: '/internal/enterprise_search/indices/{indexName}/ml_inference/errors', + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const indexName = decodeURIComponent(request.params.indexName); + const { client } = (await context.core).elasticsearch; + + const errors = await getMlInferenceErrors(indexName, client.asCurrentUser); + + return response.ok({ + body: { + errors, + }, + headers: { 'content-type': 'application/json' }, + }); + }) + ); + router.put( { path: '/internal/enterprise_search/indices/{indexName}/ml_inference/pipeline_processors/{pipelineName}', diff --git a/x-pack/plugins/files/common/index.ts b/x-pack/plugins/files/common/index.ts index 8a97030efff6f..be06c5708ce06 100755 --- a/x-pack/plugins/files/common/index.ts +++ b/x-pack/plugins/files/common/index.ts @@ -21,6 +21,7 @@ export type { FileSavedObject, BaseFileMetadata, FileShareOptions, + FileImageMetadata, FileUnshareOptions, BlobStorageSettings, UpdatableFileMetadata, diff --git a/x-pack/plugins/files/common/types.ts b/x-pack/plugins/files/common/types.ts index fd1948e6333ea..53f97962597a8 100644 --- a/x-pack/plugins/files/common/types.ts +++ b/x-pack/plugins/files/common/types.ts @@ -536,3 +536,22 @@ export interface FilesMetrics { */ countByExtension: Record; } + +/** + * Set of metadata captured for every image uploaded via the file services' + * public components. + */ +export interface FileImageMetadata { + /** + * The blurhash that can be displayed while the image is loading + */ + blurhash?: string; + /** + * Width, in px, of the original image + */ + width: number; + /** + * Height, in px, of the original image + */ + height: number; +} diff --git a/x-pack/plugins/files/public/components/context.tsx b/x-pack/plugins/files/public/components/context.tsx index ceed14b52abbd..e55c0c45e4da6 100644 --- a/x-pack/plugins/files/public/components/context.tsx +++ b/x-pack/plugins/files/public/components/context.tsx @@ -21,7 +21,6 @@ export const useFilesContext = () => { } return ctx; }; - export const FilesContext: FunctionComponent = ({ children }) => { return ( = ({ + visible, + hash, + width, + height, + isContainerWidth, +}) => { + const ref = useRef(null); + const { euiTheme } = useEuiTheme(); + useEffect(() => { + try { + const { width: blurWidth, height: blurHeight } = fitToBox(width, height); + const canvas = document.createElement('canvas'); + canvas.width = blurWidth; + canvas.height = blurHeight; + const ctx = canvas.getContext('2d')!; + const imageData = ctx.createImageData(blurWidth, blurHeight); + imageData.data.set(decode(hash, blurWidth, blurHeight)); + ctx.putImageData(imageData, 0, 0); + ref.current!.src = canvas.toDataURL(); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + }, [hash, width, height]); + return ( + + ); +}; diff --git a/x-pack/plugins/files/public/components/image/components/img.tsx b/x-pack/plugins/files/public/components/image/components/img.tsx new file mode 100644 index 0000000000000..295b062ca1fd8 --- /dev/null +++ b/x-pack/plugins/files/public/components/image/components/img.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import type { ImgHTMLAttributes, MutableRefObject } from 'react'; +import type { EuiImageSize } from '@elastic/eui/src/components/image/image_types'; +import { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { sizes } from '../styles'; + +export interface Props extends ImgHTMLAttributes { + hidden: boolean; + size?: EuiImageSize; + observerRef: (el: null | HTMLImageElement) => void; +} + +export const Img = React.forwardRef( + ({ observerRef, src, hidden, size, ...rest }, ref) => { + const { euiTheme } = useEuiTheme(); + const styles = [ + css` + transition: opacity ${euiTheme.animation.extraFast}; + `, + hidden + ? css` + visibility: hidden; + ` + : undefined, + size ? sizes[size] : undefined, + ]; + return ( + { + observerRef(element); + if (ref) { + if (typeof ref === 'function') ref(element); + else (ref as MutableRefObject).current = element; + } + }} + /> + ); + } +); diff --git a/x-pack/plugins/files/public/components/image/components/index.ts b/x-pack/plugins/files/public/components/image/components/index.ts new file mode 100644 index 0000000000000..7fee8f7fd63fb --- /dev/null +++ b/x-pack/plugins/files/public/components/image/components/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 { Img } from './img'; +export type { Props as ImgProps } from './img'; +export { Blurhash } from './blurhash'; diff --git a/x-pack/plugins/files/public/components/image/image.constants.stories.tsx b/x-pack/plugins/files/public/components/image/image.constants.stories.tsx index 9b4c2d3b64aff..cc5ff23cd2810 100644 --- a/x-pack/plugins/files/public/components/image/image.constants.stories.tsx +++ b/x-pack/plugins/files/public/components/image/image.constants.stories.tsx @@ -5,4 +5,11 @@ * 2.0. */ +export function getImageData(): Blob { + const byteChars = window.atob(base64dLogo); + const charpoints = new Array(byteChars.length); + for (let x = 0; x < byteChars.length; x++) charpoints[x] = byteChars.charCodeAt(x); + return new Blob([new Uint8Array(charpoints)], { type: 'image/png' }); +} + export const base64dLogo = `iVBORw0KGgoAAAANSUhEUgAAA4QAAAH0BAMAAACX3f7gAAAAMFBMVEWUyD/g4+JSUlKtqasXqODvUZim2+P154Zty9I2krMHeaFUtdL+0Qoku7EAAAD////6ku7MAAAUTUlEQVR4XuzRoREAIQwEwO/kyzxJg/QTGSQWm5ndFvbr4VCoEIUoVIhCFKJQIQpRiEKFKEQhChWiEIUoVIhCFKJQIQpRiEKFKEQhChWiEIUoVIhCFKJQIQpRiEKFKEQhChWiEIUoVIhCFKJQIQpRiMIXClGIQoUoRCEKFaIQhShUiEIUolAhClGIQoUoRCEKFaIQhShUiEIUolAhClGIQoUoRCEKFaIQhShUiEIUolAhClGIQoUorEqyVo+ksLKv/D2MwsPO/aRIEQRRGD9D09ROKNADiAy1tc/QDLn2GG7qcq5mE+AZvMBAWhS9EVBoh6dSaY4Dbb2A713hR0RGRP+5zPF7HkxrEcI+oBAByEP4MbaTpBAhvMgsVyFCKMGchhBKMKchhBLMaQihBFMbQlgjchtCOMdz8hkAS8K+oP9+COG3eG5GAPwINcokfQ4hVBvN+RxCqDaa9jmEsIonZyuFcI5IXYYQXiKSliGEKsKcZQihijBpGUKoIkxdhhBe4iUBwIZQRehfhhAe7sqP3I2NInR/DSEcylPux0YRWpchhFNRyq+GEe5lCKEElXeN66jtQANh+TOnRh/1/MACQr2Dyqhhxr+TQriWjTT6qONAA2EtWzlpmHHvpBAOm4SjCM07KYRL6RC6d1IIp21CTTPmnRTCpXQI3TsphEOLUAOp9XYPYW0InkXo3UkhXDuE9p0UwvJXwjncOymEtUX4QYTWawWEa4/Q+jGEUEth+77m/hhCWFoZcxBCuLQJtdk7zzMQrqV0jjPm8wyEQ5fQfJ6BsCl4r80+52MI4TkHIYS1QziH+TwD4VJ6m735PAPhmp8Qwt5xxnwkhXC41XFG+bQnAIQ1wnwkhXDqbvY7jaQEQgj7xxmPf9WDsH+cybhVUIUifJWVEEJt9v9lMSQQLq+vOUHYOM7YE359vOYtq/1NCB92IYRwzEII4U03ewjr4zVfbke47kwIIYQQLr3jjPuRFMLaIXStQgiVvxLOCaoQwmPjOONPCKEQp6SEECqH5mZvTgihUicIX5Djm2sMCIX4L79Mg/D9T6B9CZU6NDZ7/6UCQiGKMFUVQqjUY+fLT95VCKEWxXNCQgiVwxPh/J29O8psIIrCOL6JgRCi3Un5VBh0CVVGBJgNFDDmNfraBQS3r1WRTXQtl7pJJag4ZtBm3ENOOKPffws/xznDZcZISEJBfBwJIQmX5WIA0ZLw8z8T3ko31yFMDYCyHSbUGpFQlT1hrAAI4jgJSVihr51lfsHs8TU3CQtIw4g714Qk3ONXbT0qQhIm/O2jzhB6WoUkfMe5Nj2i94fAJDwAyCM+u92EJIwYbv7UEx7dDiEJC0CDuPM6hCRMAFSIXm8ZElbIVVotw6uAkTAhn3zpT30OIQkL5FvJMrS6ZZaWXiSMUHQnhEa3zD68zey8SHiAorlwGw3hNlgakrCBJiGcmgzhdwiGhiRMANTL8Ghyy7yGU2sjLhJ+QdXD5cuwA5gE6cWGi4QNVN0LgMEQxtBVW2iRMELZxR/3HcAPMffv4sYRxQH8b3DWxBCSG24WXO4hLVwpNSIE8mf4KoOjMs01aVIETP4E4ybk3BiDi3QmlUt3xhC20AquXLG7YXNBycX2WbqvRvNr31vN5HFgjNDuMZ979+Y7K+7hLeEFVyh5kIxOqYTJzbtGufpP74lc37xJxCGs7yQj0Zdw3ZdwjmE4KFB0Gz/ur9J6LDc+MhUUwu8qY5Vy5Pdrx1m1LSnT9HQUjrAby83NyjQXPQjP+hK+wjAcFCh+2iG8EHRABUCK/oSTylalBzGRRno5vpVMTvOPX1vCT//D1w7E5qWpDbDFvTaMIx8hjEIOQzTh+tluPSUKJtn+QqYDCFGpS3BcWWu5AcgqZ037fhS4Nt5LghGEeqQIfNL9O1I9iryj+cy5+nxCWOhVu3jKwxK2mf/bAyFGIXsY8gMF6he+IFafPwtRxw7BWF2YwIxAiFEYchgiUNykeuY0bC0A94d2IbR1QbwreBe2FYGQnApx0l0P2st8ATxyvq9dKzScsBSWmBCrC+uMQYhUSDjpPj9IoGBkwwlh9RmE1bF5pkbrwknFIuxmhBJcwtdI9Xr9bOIiruaCNgv9bYjGiNaFTcUjxAEp47EvP1D4NzTE1RSDuhDe2v4pShf6L7HwEoY96X6tBwpvvCeu5uIQhEtDE0brwqZiEp7N6MPwHjdQ/P3MWK/8fv5FEnxC1Fxb1nhdmHEJX84ohY+TDgoUnGjY7B9uSdX0eOAsVMEBE6sL28pZKzvhjFRzliEChbkuqNvRdL45jNpRLPs8qTjN97/yVCogjmWVaZrnaZpKGaILj1QyKfP847cnpY+wphLCkBsoeHvSTkkQptw9NRPSjl1VwusKpT4U+fwDppTZkC50NbxyJHonGcvMTtjNiNsZGLKfUOj1hLKZWQpzc67ohPrB5NTS+SUMlKq3a/3A86TCR9j5zhishFcUwW+V69A/d2itXwmbmVLYXuARYg7p4hAUof9oSaOM12CEezvH86GBAkU4W5tbl1+wCTGIFqosbhqMUG94QSNcc5sQhvxAgZr33o8e25f/hE9YGzugAWxwQuV3OYnwT9LZjGZIDBT8YThxnIFhodmEuL5xNyPCEQIWtAEIkes1Q36goD27z8Bk5V3yCdFw5gsHJ8QoFFTCu6RAodf3lEDB3s9gKgnn8vMJcQODx0lwQjT85X/hCL/etvxvqiHtCQUr3AOkNAJgilAI/dfIwBqacIKBHooQTfhV8U5phXuEQMHezxzp2c/ULSRC/zXQ3cEJM9yLSnhGeGJ/U3VR7Bn+RQgUvPMZIM3dwGEIywiEyIThunCL8aj4UH/M+xkKJVCwtqT+xQTXcgDhtU5YYzwFJkSoWYUiRKrvik+lGnIDBep5r9i2dL9csglBvtKT/SI8YYtRHIgQqf5xYTKsCZ875Dxvaj0/o1lQwlV4woYQKVi5EE14VdzWD4rhuTdQsFOF3wNbRyqhs+W6eITXmBSBCL9BE6qGqHN/oOCniiMQOV+fDu/C5f/ShUeBCREo/i1260fVkBMoUIMWBnsRfhdGJzTJLxiEa1KqL9R66zJEoBhMiFlnqeZwhJcG1fCEGRqeTHjFbkLd8Ev7X0bwl+hBuPQRrg7ahV18whMGYUdK9Vq9Uwz/cQcK/vGMbyi1XEKnVx0v2hMyBeuzMwKpXi3XQQ0CxWBCCBFyI78LUVWsM9KaQYjyC75AqjcZmkP+GwSKAxB6OQ5HeGkgPIlGKDiEL+mpHmU4bLMHCv4haeP9GQ3ThVms54VtBXk64Rk11etlMESgiEKILSt/FjoIKxGJsGQR3iWner3U5bcFCj7hNYuQ34XwgGtQwoZMSMn2W571e+bOYLXJIIrC72CLuPLHBrpssN0nj+A7aAFRbB7BTTZFJPgE0k1pKVCEIrQgfQY34kYkCXQrf1tCVeioFB3qF3tz/zNO5iwLJdCPO6fn3pkbgLuhUYNAYas7A8KQAeHJ1JvArZIRXsyW6kPkZTN8jkChVeFdE2EncRXybmeVA+G4EcJ61lk9dEPIR6Cw1RMMJrpWU4QLD1cjwqlPKkZrxSIMB47WmoMhAoWOcGRD9iOsH/1+eUKEtWO9kITw0jFc9m68qOwiZKMmBgqHKi9CvQrrtT+LsqJOUNyOSlQRDpshPJ+htcZUbzRqEChMubrcehX+pGc89I0mjCdHpSGc2KmerTWjUYNAYStkOEi5UY0aW882R60qJULe3/IjDI1Svd2o+eoiuJuvCs+MbU78ZJZiYQjX/a01o1GDQGFrz0Zoy0Zo77A4QZyZpqVuCQhtMxygCG0NYhG6tJ0L4ar1dn7GjRetqiCEE6u19o6k7JB/4UO4lQGhZ2+JXbOjbjkIw4E5q2/C0IdwX0RodGdAxEZoM79XDsJFJdVTb/my3tZxFoRn5EAvFBjODeGFlOqpq9/85kLYy4Aw4nAtgDIYFoEwHCipnhqIyT49QhYhl7wQob3WedQtBeG58QzGqffxG2FcsVBHOPYv8FpqLd/HpGLmJDkuBWGtpHrqo98Mt3NUYf33gvvWWnfqvJC69S+I7UIQhnUt1etmuP9/q5C7nMbLkd5Ml0bXloxPnDPCiZLqqYF71jTIgXAFcyNj5MsvG6G6hSAMfSnV62ZY5UCIv7uN0KY4TDOp0BFe6KmeZvhE7JCyze0QEZ5OI2h4IbWwisKfy7CJ6gupnqqcbdKtHAgvUaseL4yaXIdYDMKJ48JMejM8zoGwg1uhPi+MqpdghgUgDIvGMxiXPjjN0MDRkRDybraAkL3W9ryuP1EHU5/BZDHDnZChCmtEOZcXkjhQzAshO6UP0Frzq+cyw9cZqhArupp5IYfB43IQhnUh1Wtm2HNcC5MRjoJykHIYPJrfbW6qL7TWFDPcCRkQwm9EhGEjIcKzVAjrPp7BNBY2WArnKBZeuBAye0leyJg5V4RkiGcwOcwQZIyXTRrCdtC8kG+A5/m+kOrHVK/p81VdC1MK4BARdiJCyQsRUro4Xt0IawEhhNaaZoZHSoubOIpC2IkI+cPKgRAVrSH0FqFuhnshL8Ku7oVcOUXbzrXxgkKq18zwu9Rc48YLHaHuhTBXAaG8d4ZCqlfN8FAoQmGrJBBG6+qJXkiHLgghi1A3w3D7UClCLrTQD9LUCNvG6erZwfZFRohULyiW9KYQ69EkFatQ90JWoYwwkh+rCNOkeq7BqF806q3RKgrxQn4CI2yGfaR2EepmGFU/nd5reyX8/y5UYWIv1BGeJkYososDJxPijnMs0M6UC/UqbDfczd3TEGJWrwkgAHG3cvIYpkCoeKFZcKciwnYShM9SIaQeX4fY8/IYCQg3EC5FL+zoCJnth0kQvkx3kFJ3jjx5guNaP0LjrqbeYKuM5baub4sZSwh5d1QPhlQMim+CKa6VFCcV+ryQheM68omQd7N0hJ8SIyTEzV8+OAgOrfiD9w/m7lg5ahgIAKi6lCRxb03k3je2PsDfAu5z0NDyARR8CB/BN9AwdJnIMyl90XkGOgTjwG3CnndnJc+dts/JytPK9tqWlh75OjEhbe6on5HvnLZZgdCvRPiVauPj522QRAmDNJqQeXdGerq9Jgld7P6FQyaEUCR9r0NC4B7u0l9/6ujT7SZtYpASAr3g3lcJStzJ1RmlLlIQcQ918kuI9zSh07Ij2vHP8nlC+LtxyI/wD+I2mdBDD2MJw8GI2dredYIX2KAB9Mwhdkftm2wIn9zUU7yTqzE9lBPWxFTVotWdmGi5r9MehIS/RsFUqmQV0qRPY7xSiYh4GaYmkrBcTOSiPv658GS3rOC4NNa0jHAaIZpMCGeAn0qlIuIeVgtqPf9xGrBCTOABhIfMMMb2xKwL4wF9PyUjDNTSUr6w3WkJoTjzqJ5HyuVpTawLWvTtvLIoTTiBUMetiuhe5q0z7QHSW0N9bL8/gns1CzCEty/ar5p/dq2Z29vlQZhyeVqitQ6qyswBADRh+H+Y+x40jhDCpAiSBoF3xHRhrLWtcSCACLE9BDR2JsJviDANcRr50CQhiODgCQlrPCHi4AkD3dD9aQmhOHOp1FqINQ/Q0YR7KeEtqYfnUcL9nicscyKE4sxbpdZC3CcT+vWykKgDlLGEU26EX+C2EMerCELPp+GWIJTNpANOfFqGy/WBJuQP7+H0hJ8owosQEWVaFgJrehaSxbg6gpBPw+H01ZktRagSz4ZyQv4XXC3Pwg0x1OSEocyQ0K9KOMUT8mk46L2YECuAEU5rntDXmRDi4gwOHWW4jyHkTjdQ8XkjJBzoGV9OGKY6O8LHJcIPISpaZl5jCX+Q64pe1xLCQXMXXjwhNsyH8DtHGG0oz0J+85Dnm8jMccMTEjuOFAJCzJ9O6NcvzsjvKrj/Dg4zVxVZwlCILKxZYHSN8CgZQgh7tHdWCwjDioSXqxMGjxcjdMY2kvtL4U5a1qLKqLNa0shoKi3ooa3R4DzHS4h3qYT0hmeHQnBrXxMA5JKUzlQgwUXRV38hjbFd4KM3MMB0EMbV/ODFGVPZ5myvAt/9Zu8OUhoGAigM21WXVQTrpswZikcSRJChRyju3ARv4654jUIv0WxioJTnsgUtFTtvMgP/f4WPlOQxdA77WgLCOrudPzzOgwydI4zGceZASKUTLgYkhHDmHWf8hBDuUxCGAQkh7L37mp8Qws4/zlwtZQzCNgPhQsYglHlf8z+FEDauw0+HgpxBGO3jzJWsQbj3Hn7yE0LY2/e1kaxB2Na+r0Go6scZCKN7nAlKEITOoXtzZpyROQh78zgzkjkIW/vhJ3MQqvEefgpyB2H0jjOyB2FvJZzIHoSddZwJsgehVsZxZqwMQTgzEk6UIQh7476mHEEo3742UZYgjLZxRnmCcOciXCpPELamfW2sTEGoePFnYcu2Nixhf+kLqfTFMDMooRJcb7DlzMyghLME195thxSEsEtx08/U+TIKoetQ96eOujn594f+IOzT3Jg2vR4KEEIluOjHG4Seg/lBxQRh++9lxh+Evu+KhQoKwrb2hxBC7Wp/CCFUya+jEDrG7o3yBaFlonlXcUHY1f4uA6F2tX/VQ6im9p9RCDt2mQIJLYZrlRqEuv+TYFCxQaiX2mcZCBVrF4RQsXZBCBVrF4RQz6cB11UIQqi7U4IfQVUEodrm10fwVbUEodQ1PwCfVFMQSt3b6gjwuz06pgEABgEApmES9yBlwTUke9BA0lpo3lpD4TiR31v6p3CgEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSiEIUKUYhCFCpEIQpRqBCFKEShQhSikAYWVezJjtqBOgAAAABJRU5ErkJggg==`; diff --git a/x-pack/plugins/files/public/components/image/image.stories.tsx b/x-pack/plugins/files/public/components/image/image.stories.tsx index 400b7eb963207..02daf7badb329 100644 --- a/x-pack/plugins/files/public/components/image/image.stories.tsx +++ b/x-pack/plugins/files/public/components/image/image.stories.tsx @@ -5,38 +5,78 @@ * 2.0. */ import React from 'react'; -import { ComponentStory } from '@storybook/react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { css } from '@emotion/react'; +import { FilesContext } from '../context'; +import { getImageMetadata } from '../util'; import { Image, Props } from './image'; -import { base64dLogo } from './image.constants.stories'; +import { getImageData as getBlob, base64dLogo } from './image.constants.stories'; -const defaultArgs = { alt: 'my alt text', src: `data:image/png;base64,${base64dLogo}` }; +const defaultArgs: Props = { alt: 'test', src: `data:image/png;base64,${base64dLogo}` }; export default { title: 'components/Image', component: Image, args: defaultArgs, -}; - -const baseStyle = css` - width: 400px; -`; + decorators: [ + (Story) => ( + + + + ), + ], +} as ComponentMeta; -const Template: ComponentStory = (props: Props) => ( - +const Template: ComponentStory = (props: Props, { loaded: { meta } }) => ( + ); export const Basic = Template.bind({}); +export const WithBlurhash = Template.bind({}); +WithBlurhash.storyName = 'With blurhash'; +WithBlurhash.args = { + style: { visibility: 'hidden' }, +}; +WithBlurhash.loaders = [ + async () => ({ + meta: await getImageMetadata(getBlob()), + }), +]; +WithBlurhash.decorators = [ + (Story) => { + const alwaysShowBlurhash = `img:nth-of-type(1) { opacity: 1 !important; }`; + return ( + <> + + + + ); + }, +]; + export const BrokenSrc = Template.bind({}); +BrokenSrc.storyName = 'Broken src'; BrokenSrc.args = { - src: 'broken', + src: 'foo', }; +export const WithBlurhashAndBrokenSrc = Template.bind({}); +WithBlurhashAndBrokenSrc.storyName = 'With blurhash and broken src'; +WithBlurhashAndBrokenSrc.args = { + src: 'foo', +}; +WithBlurhashAndBrokenSrc.loaders = [ + async () => ({ + blurhash: await getImageMetadata(getBlob()), + }), +]; + export const OffScreen = Template.bind({}); -OffScreen.args = { ...defaultArgs, onFirstVisible: action('visible') }; +OffScreen.storyName = 'Offscreen'; +OffScreen.args = { onFirstVisible: action('visible') }; OffScreen.decorators = [ (Story) => ( <> diff --git a/x-pack/plugins/files/public/components/image/image.tsx b/x-pack/plugins/files/public/components/image/image.tsx index 96ac1a2eee78c..915f45c828f66 100644 --- a/x-pack/plugins/files/public/components/image/image.tsx +++ b/x-pack/plugins/files/public/components/image/image.tsx @@ -4,13 +4,30 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { MutableRefObject } from 'react'; -import type { ImgHTMLAttributes } from 'react'; +import React, { HTMLAttributes } from 'react'; +import { type ImgHTMLAttributes, useState, useEffect } from 'react'; +import { css } from '@emotion/react'; +import type { FileImageMetadata } from '../../../common'; import { useViewportObserver } from './use_viewport_observer'; +import { Img, type ImgProps, Blurhash } from './components'; +import { sizes } from './styles'; export interface Props extends ImgHTMLAttributes { src: string; alt: string; + /** + * Image metadata + */ + meta?: FileImageMetadata; + + /** + * @default original + */ + size?: ImgProps['size']; + /** + * Props to pass to the wrapper element + */ + wrapperProps?: HTMLAttributes; /** * Emits when the image first becomes visible */ @@ -28,22 +45,65 @@ export interface Props extends ImgHTMLAttributes { * ``` */ export const Image = React.forwardRef( - ({ src, alt, onFirstVisible, ...rest }, ref) => { + ( + { src, alt, onFirstVisible, onLoad, onError, meta, wrapperProps, size = 'original', ...rest }, + ref + ) => { + const [isLoaded, setIsLoaded] = useState(false); + const [blurDelayExpired, setBlurDelayExpired] = useState(false); const { isVisible, ref: observerRef } = useViewportObserver({ onFirstVisible }); + + useEffect(() => { + let unmounted = false; + const id = window.setTimeout(() => { + if (!unmounted) setBlurDelayExpired(true); + }, 200); + return () => { + unmounted = true; + window.clearTimeout(id); + }; + }, []); + + const knownSize = size ? sizes[size] : undefined; + return ( - { - observerRef(element); - if (ref) { - if (typeof ref === 'function') ref(element); - else (ref as MutableRefObject).current = element; - } - }} - // TODO: We should have a lower resolution alternative to display - src={isVisible ? src : undefined} - alt={alt} - /> +
+ {blurDelayExpired && meta?.blurhash && ( + + )} + { + setIsLoaded(true); + onLoad?.(ev); + }} + onError={(ev) => { + setIsLoaded(true); + onError?.(ev); + }} + {...rest} + /> +
); } ); diff --git a/x-pack/plugins/files/public/components/image/styles.ts b/x-pack/plugins/files/public/components/image/styles.ts new file mode 100644 index 0000000000000..b14121c667a50 --- /dev/null +++ b/x-pack/plugins/files/public/components/image/styles.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/react'; + +// Values taken from @elastic/eui/src/components/image +export const sizes = { + s: css` + width: 100px; + `, + m: css` + width: 200px; + `, + l: css` + width: 360px; + `, + xl: css` + width: 600px; + `, + original: css` + width: auto; + `, + fullWidth: css` + width: 100%; + `, +}; diff --git a/x-pack/plugins/files/public/components/upload_file/upload_file.tsx b/x-pack/plugins/files/public/components/upload_file/upload_file.tsx index 8e0d8ed59392b..e85460ca7c1e3 100644 --- a/x-pack/plugins/files/public/components/upload_file/upload_file.tsx +++ b/x-pack/plugins/files/public/components/upload_file/upload_file.tsx @@ -40,7 +40,7 @@ export interface Props { /** * A files client that will be used process uploads. */ - client: FilesClient; + client: FilesClient; /** * Allow users to clear a file after uploading. * diff --git a/x-pack/plugins/files/public/components/upload_file/upload_state.test.ts b/x-pack/plugins/files/public/components/upload_file/upload_state.test.ts index 8fc0c02e0f60e..3a4e19adf8114 100644 --- a/x-pack/plugins/files/public/components/upload_file/upload_state.test.ts +++ b/x-pack/plugins/files/public/components/upload_file/upload_state.test.ts @@ -11,6 +11,7 @@ import { TestScheduler } from 'rxjs/testing'; import type { FileKind, FileJSON } from '../../../common'; import { createMockFilesClient } from '../../mocks'; import type { FilesClient } from '../../types'; +import { ImageMetadataFactory } from '../util/image_metadata'; import { UploadState } from './upload_state'; @@ -21,6 +22,7 @@ describe('UploadState', () => { let filesClient: DeeplyMockedKeys; let uploadState: UploadState; let testScheduler: TestScheduler; + const imageMetadataFactory = (() => of(undefined)) as unknown as ImageMetadataFactory; beforeEach(() => { filesClient = createMockFilesClient(); @@ -28,7 +30,9 @@ describe('UploadState', () => { filesClient.upload.mockReturnValue(of(undefined) as any); uploadState = new UploadState( { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKind, - filesClient + filesClient, + {}, + imageMetadataFactory ); testScheduler = getTestScheduler(); }); @@ -189,7 +193,8 @@ describe('UploadState', () => { uploadState = new UploadState( { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKind, filesClient, - { allowRepeatedUploads: true } + { allowRepeatedUploads: true }, + imageMetadataFactory ); const file1 = { name: 'test' } as File; const file2 = { name: 'test 2.png' } as File; diff --git a/x-pack/plugins/files/public/components/upload_file/upload_state.ts b/x-pack/plugins/files/public/components/upload_file/upload_state.ts index d5fbc04512fdc..dd03eb7aee56a 100644 --- a/x-pack/plugins/files/public/components/upload_file/upload_state.ts +++ b/x-pack/plugins/files/public/components/upload_file/upload_state.ts @@ -29,6 +29,7 @@ import { } from 'rxjs'; import type { FileKind, FileJSON } from '../../../common/types'; import type { FilesClient } from '../../types'; +import { ImageMetadataFactory, getImageMetadata, isImage } from '../util'; import { i18nTexts } from './i18n_texts'; import { createStateSubject, type SimpleStateSubject, parseFileName } from './util'; @@ -68,7 +69,8 @@ export class UploadState { constructor( private readonly fileKind: FileKind, private readonly client: FilesClient, - private readonly opts: UploadOptions = { allowRepeatedUploads: false } + private readonly opts: UploadOptions = { allowRepeatedUploads: false }, + private readonly loadImageMetadata: ImageMetadataFactory = getImageMetadata ) { const latestFiles$ = this.files$$.pipe(switchMap((files$) => combineLatest(files$))); this.subscriptions = [ @@ -171,15 +173,17 @@ export class UploadState { const { name } = parseFileName(file.name); const mime = file.type || undefined; - - return from( - this.client.create({ - kind: this.fileKind.id, - name, - mimeType: mime, - meta: meta as Record, - }) - ).pipe( + const _meta = meta as Record; + + return from(isImage(file) ? this.loadImageMetadata(file) : of(undefined)).pipe( + mergeMap((imageMetadata) => + this.client.create({ + kind: this.fileKind.id, + name, + mimeType: mime, + meta: imageMetadata ? { ...imageMetadata, ..._meta } : _meta, + }) + ), mergeMap((result) => { uploadTarget = result.file; return race( @@ -240,10 +244,12 @@ export class UploadState { export const createUploadState = ({ fileKind, client, + imageMetadataFactory, ...options }: { fileKind: FileKind; client: FilesClient; + imageMetadataFactory?: ImageMetadataFactory; } & UploadOptions) => { - return new UploadState(fileKind, client, options); + return new UploadState(fileKind, client, options, imageMetadataFactory); }; diff --git a/x-pack/plugins/files/public/components/util/image_metadata.test.ts b/x-pack/plugins/files/public/components/util/image_metadata.test.ts new file mode 100644 index 0000000000000..16980aee00296 --- /dev/null +++ b/x-pack/plugins/files/public/components/util/image_metadata.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { fitToBox } from './image_metadata'; +describe('util', () => { + describe('fitToBox', () => { + test('300x300', () => { + expect(fitToBox(300, 300)).toMatchInlineSnapshot(` + Object { + "height": 300, + "width": 300, + } + `); + }); + + test('300x150', () => { + expect(fitToBox(300, 150)).toMatchInlineSnapshot(` + Object { + "height": 150, + "width": 300, + } + `); + }); + + test('4500x9000', () => { + expect(fitToBox(4500, 9000)).toMatchInlineSnapshot(` + Object { + "height": 300, + "width": 150, + } + `); + }); + + test('1000x300', () => { + expect(fitToBox(1000, 300)).toMatchInlineSnapshot(` + Object { + "height": 90, + "width": 300, + } + `); + }); + + test('0x0', () => { + expect(fitToBox(0, 0)).toMatchInlineSnapshot(` + Object { + "height": 0, + "width": 0, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/files/public/components/util/image_metadata.ts b/x-pack/plugins/files/public/components/util/image_metadata.ts new file mode 100644 index 0000000000000..9358dda9d05ad --- /dev/null +++ b/x-pack/plugins/files/public/components/util/image_metadata.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as bh from 'blurhash'; +import type { FileImageMetadata } from '../../../common'; + +export function isImage(file: Blob | File): boolean { + return file.type?.startsWith('image/'); +} + +export const boxDimensions = { + width: 300, + height: 300, +}; + +/** + * Calculate the size of an image, fitting to our limits see {@link boxDimensions}, + * while preserving the aspect ratio. + */ +export function fitToBox(width: number, height: number): { width: number; height: number } { + const offsetRatio = Math.abs( + Math.min( + // Find the aspect at which our box is smallest, if less than 1, it means we exceed the limits + Math.min(boxDimensions.width / width, boxDimensions.height / height), + // Values greater than 1 are within our limits + 1 + ) - 1 // Get the percentage we are exceeding. E.g., 0.3 - 1 = -0.7 means the image needs to shrink by 70% to fit + ); + return { + width: Math.floor(width - offsetRatio * width), + height: Math.floor(height - offsetRatio * height), + }; +} + +/** + * Get the native size of the image + */ +function loadImage(src: string): Promise { + return new Promise((res, rej) => { + const image = new window.Image(); + image.src = src; + image.onload = () => res(image); + image.onerror = rej; + }); +} + +/** + * Extract image metadata, assumes that file or blob as an image! + */ +export async function getImageMetadata(file: File | Blob): Promise { + const imgUrl = window.URL.createObjectURL(file); + try { + const image = await loadImage(imgUrl); + const canvas = document.createElement('canvas'); + const { width, height } = fitToBox(image.width, image.height); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + if (!ctx) throw new Error('Could not get 2d canvas context!'); + ctx.drawImage(image, 0, 0, width, height); + const imgData = ctx.getImageData(0, 0, width, height); + return { + blurhash: bh.encode(imgData.data, imgData.width, imgData.height, 4, 4), + width: image.width, + height: image.height, + }; + } catch (e) { + // Don't error out if we cannot generate the blurhash + return undefined; + } finally { + window.URL.revokeObjectURL(imgUrl); + } +} + +export type ImageMetadataFactory = typeof getImageMetadata; diff --git a/x-pack/plugins/files/public/components/util/index.ts b/x-pack/plugins/files/public/components/util/index.ts new file mode 100644 index 0000000000000..e3e30fdb17bb3 --- /dev/null +++ b/x-pack/plugins/files/public/components/util/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 { getImageMetadata, isImage, fitToBox } from './image_metadata'; +export type { ImageMetadataFactory } from './image_metadata'; diff --git a/x-pack/plugins/files/public/plugin.ts b/x-pack/plugins/files/public/plugin.ts index 8ebbd71cdbe1f..2f5481f8f2511 100644 --- a/x-pack/plugins/files/public/plugin.ts +++ b/x-pack/plugins/files/public/plugin.ts @@ -11,9 +11,10 @@ import { setFileKindsRegistry, FileKindsRegistryImpl, } from '../common/file_kinds_registry'; -import type { FilesClientFactory } from './types'; +import type { FilesClient, FilesClientFactory } from './types'; import { createFilesClient } from './files_client'; import { FileKind } from '../common'; +import { ScopedFilesClient } from '.'; /** * Public setup-phase contract @@ -50,11 +51,11 @@ export class FilesPlugin implements Plugin { setup(core: CoreSetup): FilesSetup { this.filesClientFactory = { - asScoped(fileKind: string) { - return createFilesClient({ fileKind, http: core.http }); + asScoped(fileKind: string) { + return createFilesClient({ fileKind, http: core.http }) as ScopedFilesClient; }, - asUnscoped() { - return createFilesClient({ http: core.http }); + asUnscoped() { + return createFilesClient({ http: core.http }) as FilesClient; }, }; return { diff --git a/x-pack/plugins/files/public/types.ts b/x-pack/plugins/files/public/types.ts index 25aab6e787b6b..1cc69ac4ed23e 100644 --- a/x-pack/plugins/files/public/types.ts +++ b/x-pack/plugins/files/public/types.ts @@ -60,13 +60,13 @@ interface GlobalEndpoints { /** * A client that can be used to manage a specific {@link FileKind}. */ -export interface FilesClient extends GlobalEndpoints { +export interface FilesClient extends GlobalEndpoints { /** * Create a new file object with the provided metadata. * * @param args - create file args */ - create: ClientMethodFrom; + create: ClientMethodFrom>; /** * Delete a file object and all associated share and content objects. * @@ -78,19 +78,19 @@ export interface FilesClient extends GlobalEndpoints { * * @param args - get file by ID args */ - getById: ClientMethodFrom; + getById: ClientMethodFrom>; /** * List all file objects, of a given {@link FileKind}. * * @param args - list files args */ - list: ClientMethodFrom; + list: ClientMethodFrom>; /** * Update a set of of metadata values of the file object. * * @param args - update file args */ - update: ClientMethodFrom; + update: ClientMethodFrom>; /** * Stream the contents of the file to Kibana server for storage. * @@ -151,8 +151,8 @@ export interface FilesClient extends GlobalEndpoints { listShares: ClientMethodFrom; } -export type FilesClientResponses = { - [K in keyof FilesClient]: Awaited>; +export type FilesClientResponses = { + [K in keyof FilesClient]: Awaited[K]>>; }; /** @@ -161,10 +161,10 @@ export type FilesClientResponses = { * More convenient if you want to re-use the same client for the same file kind * and not specify the kind every time. */ -export type ScopedFilesClient = { +export type ScopedFilesClient = { [K in keyof FilesClient]: K extends 'list' - ? (arg?: Omit[0], 'kind'>) => ReturnType - : (arg: Omit[0], 'kind'>) => ReturnType; + ? (arg?: Omit[K]>[0], 'kind'>) => ReturnType[K]> + : (arg: Omit[K]>[0], 'kind'>) => ReturnType[K]>; }; /** @@ -174,11 +174,11 @@ export interface FilesClientFactory { /** * Create a files client. */ - asUnscoped(): FilesClient; + asUnscoped(): FilesClient; /** * Create a {@link ScopedFileClient} for a given {@link FileKind}. * * @param fileKind - The {@link FileKind} to create a client for. */ - asScoped(fileKind: string): ScopedFilesClient; + asScoped(fileKind: string): ScopedFilesClient; } diff --git a/x-pack/plugins/files/server/blob_storage_service/adapters/es/es.ts b/x-pack/plugins/files/server/blob_storage_service/adapters/es/es.ts index adc707f4b141a..96421a06e95c5 100644 --- a/x-pack/plugins/files/server/blob_storage_service/adapters/es/es.ts +++ b/x-pack/plugins/files/server/blob_storage_service/adapters/es/es.ts @@ -49,10 +49,6 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { */ private readonly uploadSemaphore = ElasticsearchBlobStorageClient.defaultSemaphore ) { - assert( - this.index.startsWith('.kibana'), - `Elasticsearch blob store index name must start with ".kibana", got ${this.index}.` - ); assert(this.uploadSemaphore, `No default semaphore provided and no semaphore was passed in.`); } diff --git a/x-pack/plugins/files/server/file_client/integration_tests/es_file_client.test.ts b/x-pack/plugins/files/server/file_client/integration_tests/es_file_client.test.ts index 25e6e1b5d4c16..58bf5cec29c41 100644 --- a/x-pack/plugins/files/server/file_client/integration_tests/es_file_client.test.ts +++ b/x-pack/plugins/files/server/file_client/integration_tests/es_file_client.test.ts @@ -11,11 +11,7 @@ import { createEsFileClient } from '../create_es_file_client'; import { FileClient } from '../types'; import { FileMetadata } from '../../../common'; -/** - * This file client is using Elasticsearch interfaces directly to manage files. - */ -// FLAKY: https://github.com/elastic/kibana/issues/139565 -describe.skip('ES-index-backed file client', () => { +describe('ES-index-backed file client', () => { let esClient: TestEnvironmentUtils['esClient']; let fileClient: FileClient; let testHarness: TestEnvironmentUtils; @@ -197,6 +193,9 @@ describe.skip('ES-index-backed file client', () => { }) ); - await Promise.all([fileClient.delete({ id: id1 }), fileClient.delete({ id: id2 })]); + await Promise.all([ + fileClient.delete({ id: id1, hasContent: false }), + fileClient.delete({ id: id2, hasContent: false }), + ]); }); }); diff --git a/x-pack/plugins/files/server/routes/common.test.ts b/x-pack/plugins/files/server/routes/common.test.ts index 2c4d302d04625..1f9292e3ff07a 100644 --- a/x-pack/plugins/files/server/routes/common.test.ts +++ b/x-pack/plugins/files/server/routes/common.test.ts @@ -26,30 +26,30 @@ describe('getDownloadHeadersForFile', () => { const file = { data: { name: 'test', mimeType: undefined } } as unknown as File; test('no mime type and name from file object', () => { - expect(getDownloadHeadersForFile(file, undefined)).toEqual( + expect(getDownloadHeadersForFile({ file, fileName: undefined })).toEqual( expectHeaders({ contentType: 'application/octet-stream', contentDisposition: 'test' }) ); }); test('no mime type and name (without ext)', () => { - expect(getDownloadHeadersForFile(file, 'myfile')).toEqual( + expect(getDownloadHeadersForFile({ file, fileName: 'myfile' })).toEqual( expectHeaders({ contentType: 'application/octet-stream', contentDisposition: 'myfile' }) ); }); test('no mime type and name (with ext)', () => { - expect(getDownloadHeadersForFile(file, 'myfile.png')).toEqual( + expect(getDownloadHeadersForFile({ file, fileName: 'myfile.png' })).toEqual( expectHeaders({ contentType: 'image/png', contentDisposition: 'myfile.png' }) ); }); test('mime type and no name', () => { const fileWithMime = { data: { ...file.data, mimeType: 'application/pdf' } } as File; - expect(getDownloadHeadersForFile(fileWithMime, undefined)).toEqual( + expect(getDownloadHeadersForFile({ file: fileWithMime, fileName: undefined })).toEqual( expectHeaders({ contentType: 'application/pdf', contentDisposition: 'test' }) ); }); test('mime type and name', () => { const fileWithMime = { data: { ...file.data, mimeType: 'application/pdf' } } as File; - expect(getDownloadHeadersForFile(fileWithMime, 'a cool file.pdf')).toEqual( + expect(getDownloadHeadersForFile({ file: fileWithMime, fileName: 'a cool file.pdf' })).toEqual( expectHeaders({ contentType: 'application/pdf', contentDisposition: 'a cool file.pdf' }) ); }); diff --git a/x-pack/plugins/files/server/routes/common.ts b/x-pack/plugins/files/server/routes/common.ts index 0730a6435de02..8e17a39511b53 100644 --- a/x-pack/plugins/files/server/routes/common.ts +++ b/x-pack/plugins/files/server/routes/common.ts @@ -8,7 +8,12 @@ import mime from 'mime'; import type { ResponseHeaders } from '@kbn/core/server'; import type { File } from '../../common/types'; -export function getDownloadHeadersForFile(file: File, fileName?: string): ResponseHeaders { +interface Args { + file: File; + fileName?: string; +} + +export function getDownloadHeadersForFile({ file, fileName }: Args): ResponseHeaders { return { 'content-type': (fileName && mime.getType(fileName)) ?? file.data.mimeType ?? 'application/octet-stream', diff --git a/x-pack/plugins/files/server/routes/file_kind/create.ts b/x-pack/plugins/files/server/routes/file_kind/create.ts index a134bdd292e98..78a7260771a16 100644 --- a/x-pack/plugins/files/server/routes/file_kind/create.ts +++ b/x-pack/plugins/files/server/routes/file_kind/create.ts @@ -23,7 +23,7 @@ const rt = { }), }; -export type Endpoint = CreateRouteDefinition; +export type Endpoint = CreateRouteDefinition }>; export const handler: CreateHandler = async ({ fileKind, files }, req, res) => { const { fileService } = await files; diff --git a/x-pack/plugins/files/server/routes/file_kind/download.ts b/x-pack/plugins/files/server/routes/file_kind/download.ts index 85f8b5bd0a2d6..d4ae37ddb6623 100644 --- a/x-pack/plugins/files/server/routes/file_kind/download.ts +++ b/x-pack/plugins/files/server/routes/file_kind/download.ts @@ -40,7 +40,7 @@ export const handler: CreateHandler = async ({ files, fileKind }, req, const body: Response = await file.downloadContent(); return res.ok({ body, - headers: getDownloadHeadersForFile(file, fileName), + headers: getDownloadHeadersForFile({ file, fileName }), }); } catch (e) { if (e instanceof fileErrors.NoDownloadAvailableError) { diff --git a/x-pack/plugins/files/server/routes/file_kind/get_by_id.ts b/x-pack/plugins/files/server/routes/file_kind/get_by_id.ts index 00c5bd2312f89..4d86e05564fdf 100644 --- a/x-pack/plugins/files/server/routes/file_kind/get_by_id.ts +++ b/x-pack/plugins/files/server/routes/file_kind/get_by_id.ts @@ -19,7 +19,7 @@ const rt = { }), }; -export type Endpoint = CreateRouteDefinition; +export type Endpoint = CreateRouteDefinition }>; export const handler: CreateHandler = async ({ files, fileKind }, req, res) => { const { fileService } = await files; diff --git a/x-pack/plugins/files/server/routes/file_kind/list.ts b/x-pack/plugins/files/server/routes/file_kind/list.ts index 3f1e36913bdc9..b6a869117b37f 100644 --- a/x-pack/plugins/files/server/routes/file_kind/list.ts +++ b/x-pack/plugins/files/server/routes/file_kind/list.ts @@ -18,7 +18,7 @@ const rt = { }), }; -export type Endpoint = CreateRouteDefinition; +export type Endpoint = CreateRouteDefinition> }>; export const handler: CreateHandler = async ({ files, fileKind }, req, res) => { const { diff --git a/x-pack/plugins/files/server/routes/file_kind/update.ts b/x-pack/plugins/files/server/routes/file_kind/update.ts index 733f9c9ce78c2..9621fc56c311c 100644 --- a/x-pack/plugins/files/server/routes/file_kind/update.ts +++ b/x-pack/plugins/files/server/routes/file_kind/update.ts @@ -26,7 +26,7 @@ const rt = { }), }; -export type Endpoint = CreateRouteDefinition; +export type Endpoint = CreateRouteDefinition }>; export const handler: CreateHandler = async ({ files, fileKind }, req, res) => { const { fileService } = await files; diff --git a/x-pack/plugins/files/server/routes/public_facing/download.ts b/x-pack/plugins/files/server/routes/public_facing/download.ts index bd739f93077fe..bd77cabaf7e4d 100644 --- a/x-pack/plugins/files/server/routes/public_facing/download.ts +++ b/x-pack/plugins/files/server/routes/public_facing/download.ts @@ -43,7 +43,7 @@ const handler: CreateHandler = async ({ files }, req, res) => { const body: Readable = await file.downloadContent(); return res.ok({ body, - headers: getDownloadHeadersForFile(file, fileName), + headers: getDownloadHeadersForFile({ file, fileName }), }); } catch (e) { if ( diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index ea2ad78dc10cd..9e47451aa02bb 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -96,10 +96,13 @@ export interface Agent extends AgentBase { access_api_key?: string; // @deprecated default_api_key_history?: FleetServerAgent['default_api_key_history']; - outputs?: Array<{ - api_key_id: string; - to_retire_api_key_ids?: FleetServerAgent['default_api_key_history']; - }>; + outputs?: Record< + string, + { + api_key_id: string; + to_retire_api_key_ids?: FleetServerAgent['default_api_key_history']; + } + >; status?: AgentStatus; packages: string[]; sort?: Array; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx index c5ccd2916e465..cd0164124bfbc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx @@ -217,11 +217,11 @@ export const AddIntegrationPageStep: React.FC = (props return ( extensionView && ( - + ) ); - }, [packagePolicy, extensionView]); + }, [extensionView]); const content = useMemo(() => { if (packageInfo.name !== 'endpoint') { diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx index 58d3a847d3efc..7539197e8462f 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx @@ -6,7 +6,7 @@ */ import { Search as LocalSearch, PrefixIndexStrategy } from 'js-search'; -import { useEffect, useRef } from 'react'; +import { useRef } from 'react'; import type { IntegrationCardItem } from '../../../../common/types/models'; @@ -16,13 +16,11 @@ export const fieldsToSearch = ['name', 'title']; export function useLocalSearch(packageList: IntegrationCardItem[]) { const localSearchRef = useRef(new LocalSearch(searchIdField)); - useEffect(() => { - const localSearch = new LocalSearch(searchIdField); - localSearch.indexStrategy = new PrefixIndexStrategy(); - fieldsToSearch.forEach((field) => localSearch.addIndex(field)); - localSearch.addDocuments(packageList); - localSearchRef.current = localSearch; - }, [packageList]); + const localSearch = new LocalSearch(searchIdField); + localSearch.indexStrategy = new PrefixIndexStrategy(); + fieldsToSearch.forEach((field) => localSearch.addIndex(field)); + localSearch.addDocuments(packageList); + localSearchRef.current = localSearch; return localSearchRef; } diff --git a/x-pack/plugins/fleet/public/index.ts b/x-pack/plugins/fleet/public/index.ts index 823d46becb4ff..7b53d4e1d2ccd 100644 --- a/x-pack/plugins/fleet/public/index.ts +++ b/x-pack/plugins/fleet/public/index.ts @@ -37,7 +37,6 @@ export type { PackagePolicyCreateExtensionComponentProps, PackagePolicyCreateMultiStepExtension, PackagePolicyCreateMultiStepExtensionComponent, - PackagePolicyCreateMultiStepExtensionComponentProps, PackagePolicyEditExtension, PackagePolicyEditExtensionComponent, PackagePolicyEditExtensionComponentProps, diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index 24f9fef730613..fc8726e7b813a 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -136,13 +136,7 @@ export interface PackagePolicyCreateExtension { * UI Component Extension is used on the pages displaying the ability to Create a multi step * Integration Policy */ -export type PackagePolicyCreateMultiStepExtensionComponent = - ComponentType; - -export interface PackagePolicyCreateMultiStepExtensionComponentProps { - /** The integration policy being created */ - newPolicy: NewPackagePolicy; -} +export type PackagePolicyCreateMultiStepExtensionComponent = ComponentType<{}>; /** Extension point registration contract for Integration Policy Create views in multi-step onboarding */ export interface PackagePolicyCreateMultiStepExtension { diff --git a/x-pack/plugins/fleet/server/services/agents/action_status.ts b/x-pack/plugins/fleet/server/services/agents/action_status.ts index a057af185a066..7ab162b0aef79 100644 --- a/x-pack/plugins/fleet/server/services/agents/action_status.ts +++ b/x-pack/plugins/fleet/server/services/agents/action_status.ts @@ -64,12 +64,13 @@ export async function getActionStatuses( const matchingBucket = (acks?.aggregations?.ack_counts as any)?.buckets?.find( (bucket: any) => bucket.key === action.actionId ); + const nbAgentsActioned = action.nbAgentsActioned || action.nbAgentsActionCreated; const nbAgentsAck = Math.min( matchingBucket?.doc_count ?? 0, - (matchingBucket?.agent_count as any)?.value ?? 0 + (matchingBucket?.agent_count as any)?.value ?? 0, + nbAgentsActioned ); const completionTime = (matchingBucket?.max_timestamp as any)?.value_as_string; - const nbAgentsActioned = action.nbAgentsActioned || action.nbAgentsActionCreated; const complete = nbAgentsAck >= nbAgentsActioned; const cancelledAction = cancelledActions.find((a) => a.actionId === action.actionId); diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts index 9169df19fbcfb..b1e69fa861488 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts @@ -331,15 +331,22 @@ describe('invalidateAPIKeysForAgents', () => { id: 'defaultApiKeyHistory2', }, ], - outputs: [ - { + outputs: { + output1: { api_key_id: 'outputApiKey1', to_retire_api_key_ids: [{ id: 'outputApiKeyRetire1' }, { id: 'outputApiKeyRetire2' }], }, - { + output2: { api_key_id: 'outputApiKey2', }, - ], + output3: { + api_key_id: 'outputApiKey3', + to_retire_api_key_ids: [ + // Somes Fleet Server agents don't have an id here (probably a bug) + { retired_at: 'foo' }, + ], + }, + }, } as any, ]); @@ -353,6 +360,7 @@ describe('invalidateAPIKeysForAgents', () => { 'outputApiKeyRetire1', 'outputApiKeyRetire2', 'outputApiKey2', + 'outputApiKey3', ]); }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts index fed5d44fe98e8..1b17e688a8a26 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts @@ -216,12 +216,16 @@ export async function invalidateAPIKeysForAgents(agents: Agent[]) { agent.default_api_key_history.forEach((apiKey) => keys.push(apiKey.id)); } if (agent.outputs) { - agent.outputs.forEach((output) => { + Object.values(agent.outputs).forEach((output) => { if (output.api_key_id) { keys.push(output.api_key_id); } if (output.to_retire_api_key_ids) { - output.to_retire_api_key_ids.forEach((apiKey) => keys.push(apiKey.id)); + Object.values(output.to_retire_api_key_ids).forEach((apiKey) => { + if (apiKey?.id) { + keys.push(apiKey.id); + } + }); } }); } diff --git a/x-pack/plugins/lens/common/embeddable_factory/index.ts b/x-pack/plugins/lens/common/embeddable_factory/index.ts index 8ddddf654b017..b794ec642f40a 100644 --- a/x-pack/plugins/lens/common/embeddable_factory/index.ts +++ b/x-pack/plugins/lens/common/embeddable_factory/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { SerializableRecord, Serializable } from '@kbn/utility-types'; -import { SavedObjectReference } from '@kbn/core/types'; +import type { SerializableRecord, Serializable } from '@kbn/utility-types'; +import type { SavedObjectReference } from '@kbn/core/types'; import type { EmbeddableStateWithType, EmbeddableRegistryDefinition, diff --git a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts index b9b4fb4889994..a2b7d730766c7 100644 --- a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts @@ -12,18 +12,6 @@ import type { TimeRange } from '@kbn/es-query'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; -// mock the specific inner variable: -// there are intra dependencies in the data plugin we might break trying to mock the whole thing -jest.mock('@kbn/data-plugin/common/query/timefilter/get_time', () => { - const localMoment = jest.requireActual('moment'); - return { - calculateBounds: jest.fn(({ from, to }) => ({ - min: localMoment(from), - max: localMoment(to), - })), - }; -}); - import { getTimeScale } from './time_scale'; import type { TimeScaleArgs } from './types'; @@ -34,7 +22,11 @@ describe('time_scale', () => { context?: ExecutionContext ) => Promise; - const timeScale = getTimeScale(createDatatableUtilitiesMock, () => 'UTC'); + const timeScale = getTimeScale( + createDatatableUtilitiesMock, + () => 'UTC', + () => new Date('2010-01-04T06:30:30') + ); const emptyTable: Datatable = { type: 'datatable', @@ -402,7 +394,6 @@ describe('time_scale', () => { ...emptyTable, rows: [ { - date: moment('2010-01-01T00:00:00.000Z').valueOf(), metric: 300, }, ], @@ -425,6 +416,34 @@ describe('time_scale', () => { expect(result.rows.map(({ scaledMetric }) => scaledMetric)).toEqual([75]); }); + it('should work with relative time range', async () => { + const result = await timeScaleWrapped( + { + ...emptyTable, + rows: [ + { + metric: 300, + }, + ], + }, + { + inputColumnId: 'metric', + outputColumnId: 'scaledMetric', + targetUnit: 'd', + }, + { + getSearchContext: () => ({ + timeRange: { + from: 'now-2d', + to: 'now', + }, + }), + } as unknown as ExecutionContext + ); + + expect(result.rows.map(({ scaledMetric }) => scaledMetric)).toEqual([150]); + }); + it('should apply fn for non-histogram fields (with Reduced time range)', async () => { const result = await timeScaleWrapped( { diff --git a/x-pack/plugins/lens/common/expressions/time_scale/time_scale_fn.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale_fn.ts index 722056321e17a..6b2e1857a902c 100644 --- a/x-pack/plugins/lens/common/expressions/time_scale/time_scale_fn.ts +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale_fn.ts @@ -24,12 +24,39 @@ const unitInMs: Record = { d: 1000 * 60 * 60 * 24, }; +// the datemath plugin always parses dates by using the current default moment time zone. +// to use the configured time zone, we are temporary switching it just for the calculation. + +// The code between this call and the reset in the finally block is not allowed to get async, +// otherwise the timezone setting can leak out of this function. +const withChangedTimeZone = ( + timeZone: string | undefined, + action: () => TReturnedValue +): TReturnedValue => { + if (timeZone) { + const defaultTimezone = moment().zoneName(); + try { + moment.tz.setDefault(timeZone); + return action(); + } finally { + // reset default moment timezone + moment.tz.setDefault(defaultTimezone); + } + } else { + return action(); + } +}; + +const getTimeBounds = (timeRange: TimeRange, timeZone?: string, getForceNow?: () => Date) => + withChangedTimeZone(timeZone, () => calculateBounds(timeRange, { forceNow: getForceNow?.() })); + export const timeScaleFn = ( getDatatableUtilities: ( context: ExecutionContext ) => DatatableUtilitiesService | Promise, - getTimezone: (context: ExecutionContext) => string | Promise + getTimezone: (context: ExecutionContext) => string | Promise, + getForceNow?: () => Date ): TimeScaleExpressionFunction['fn'] => async ( input, @@ -69,7 +96,9 @@ export const timeScaleFn = timeZone: contextTimeZone, }); const intervalDuration = timeInfo?.interval && parseInterval(timeInfo.interval); - timeBounds = timeInfo?.timeRange && calculateBounds(timeInfo.timeRange); + + timeBounds = + timeInfo?.timeRange && getTimeBounds(timeInfo.timeRange, timeInfo?.timeZone, getForceNow); getStartEndOfBucketMeta = (row) => { const startOfBucket = moment.tz(row[dateColumnId], timeInfo?.timeZone ?? contextTimeZone); @@ -89,8 +118,18 @@ export const timeScaleFn = } } else { const timeRange = context.getSearchContext().timeRange as TimeRange; - const endOfBucket = moment.tz(timeRange.to, contextTimeZone); - let startOfBucket = moment.tz(timeRange.from, contextTimeZone); + timeBounds = getTimeBounds(timeRange, contextTimeZone, getForceNow); + + if (!timeBounds.max || !timeBounds.min) { + throw new Error( + i18n.translate('xpack.lens.functions.timeScale.timeBoundsMissingMessage', { + defaultMessage: 'Could not parse "Time Range"', + }) + ); + } + + const endOfBucket = timeBounds.max; + let startOfBucket = timeBounds.min; if (reducedTimeRange) { const reducedStartOfBucket = endOfBucket.clone().subtract(parseInterval(reducedTimeRange)); @@ -100,8 +139,6 @@ export const timeScaleFn = } } - timeBounds = calculateBounds(timeRange); - getStartEndOfBucketMeta = () => ({ startOfBucket, endOfBucket, diff --git a/x-pack/plugins/lens/common/constants.test.ts b/x-pack/plugins/lens/common/helpers.test.ts similarity index 100% rename from x-pack/plugins/lens/common/constants.test.ts rename to x-pack/plugins/lens/common/helpers.test.ts diff --git a/x-pack/plugins/lens/common/index.ts b/x-pack/plugins/lens/common/index.ts index 7929ebf6d1784..45dd41422b018 100644 --- a/x-pack/plugins/lens/common/index.ts +++ b/x-pack/plugins/lens/common/index.ts @@ -10,7 +10,6 @@ export * from './constants'; export * from './types'; -export * from './visualizations'; // Note: do not import the expression folder here or the page bundle will be bloated with all // the package diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index c21da9e31475c..d70ee4b7025a0 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -6,12 +6,12 @@ */ import type { Filter, FilterMeta } from '@kbn/es-query'; -import { Position } from '@elastic/charts'; -import { $Values } from '@kbn/utility-types'; +import type { Position } from '@elastic/charts'; +import type { $Values } from '@kbn/utility-types'; import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import type { ColorMode } from '@kbn/charts-plugin/common'; -import { LegendSize } from '@kbn/visualizations-plugin/common'; +import type { LegendSize } from '@kbn/visualizations-plugin/common'; import { CategoryDisplay, layerTypes, diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 58e65b3b79bbe..648fd61203943 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -405,39 +405,6 @@ describe('Lens App', () => { }); describe('TopNavMenu#dataViewPickerProps', () => { - it('calls the nav component with the correct dataview picker props if no permissions are given', async () => { - const { instance, lensStore } = await mountWith({ preloadedState: {} }); - const document = { - savedObjectId: defaultSavedObjectId, - state: { - query: 'fake query', - filters: [{ query: { match_phrase: { src: 'test' } } }], - }, - references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], - } as unknown as Document; - - act(() => { - lensStore.dispatch( - setState({ - query: 'fake query' as unknown as Query, - persistedDoc: document, - }) - ); - }); - instance.update(); - const props = instance - .find('[data-test-subj="lnsApp_topNav"]') - .prop('dataViewPickerComponentProps') as TopNavMenuData[]; - expect(props).toEqual( - expect.objectContaining({ - currentDataViewId: 'mockip', - onChangeDataView: expect.any(Function), - onDataViewCreated: undefined, - onAddField: undefined, - }) - ); - }); - it('calls the nav component with the correct dataview picker props if permissions are given', async () => { const { instance, lensStore, services } = await mountWith({ preloadedState: {} }); services.dataViewEditor.userPermissions.editDataView = () => true; 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 80c1384a1020c..4176e5f1e51a7 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 @@ -288,7 +288,8 @@ export const LensTopNavMenu = ({ ] ); - const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); + const canEditDataView = + Boolean(dataViewEditor?.userPermissions.editDataView()) || !currentIndexPattern?.isPersisted(); const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -756,39 +757,32 @@ export const LensTopNavMenu = ({ [editField, canEditDataView] ); - const createNewDataView = useMemo( - () => - canEditDataView - ? () => { - closeDataViewEditor.current = dataViewEditor.openEditor({ - onSave: async (dataView) => { - if (dataView.id) { - if (isOnTextBasedMode) { - dispatch( - switchAndCleanDatasource({ - newDatasourceId: 'indexpattern', - visualizationId: visualization?.activeId, - currentIndexPatternId: dataView?.id, - }) - ); - } - dispatchChangeIndexPattern(dataView); - setCurrentIndexPattern(dataView); - } - }, - allowAdHocDataView: true, - }); + const createNewDataView = useCallback(() => { + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataView) => { + if (dataView.id) { + if (isOnTextBasedMode) { + dispatch( + switchAndCleanDatasource({ + newDatasourceId: 'indexpattern', + visualizationId: visualization?.activeId, + currentIndexPatternId: dataView?.id, + }) + ); } - : undefined, - [ - canEditDataView, - dataViewEditor, - dispatch, - dispatchChangeIndexPattern, - isOnTextBasedMode, - visualization?.activeId, - ] - ); + dispatchChangeIndexPattern(dataView); + setCurrentIndexPattern(dataView); + } + }, + allowAdHocDataView: true, + }); + }, [ + dataViewEditor, + dispatch, + dispatchChangeIndexPattern, + isOnTextBasedMode, + visualization?.activeId, + ]); const onCreateDefaultAdHocDataView = useCallback( async (pattern: string) => { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts index d96c2aee26c03..cef9e7d698faa 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts @@ -23,16 +23,16 @@ import { IContainer, ErrorEmbeddable, } from '@kbn/embeddable-plugin/public'; -import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { Start as InspectorStart } from '@kbn/inspector-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { Start as InspectorStart } from '@kbn/inspector-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; -import { LensByReferenceInput, LensEmbeddableInput } from './embeddable'; -import { Document } from '../persistence/saved_object_store'; -import { LensAttributeService } from '../lens_attribute_service'; +import type { LensByReferenceInput, LensEmbeddableInput } from './embeddable'; +import type { Document } from '../persistence/saved_object_store'; +import type { LensAttributeService } from '../lens_attribute_service'; import { DOC_TYPE } from '../../common/constants'; -import { ErrorMessage } from '../editor_frame_service/types'; +import type { ErrorMessage } from '../editor_frame_service/types'; import { extract, inject } from '../../common/embeddable_factory'; -import { DatasourceMap, VisualizationMap } from '../types'; +import type { DatasourceMap, VisualizationMap } from '../types'; export interface LensEmbeddableStartServices { data: DataPublicPluginStart; diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index a5f193d63e4f3..bfad4e70cf1c7 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -6,6 +6,7 @@ */ import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; + import { getDatatable } from '../common/expressions/datatable/datatable'; import { datatableColumn } from '../common/expressions/datatable/datatable_column'; import { mapToColumns } from '../common/expressions/map_to_columns/map_to_columns'; @@ -14,11 +15,14 @@ import { counterRate } from '../common/expressions/counter_rate'; import { getTimeScale } from '../common/expressions/time_scale/time_scale'; import { collapse } from '../common/expressions'; +type TimeScaleArguments = Parameters; + export const setupExpressions = ( expressions: ExpressionsSetup, formatFactory: Parameters[0], - getDatatableUtilities: Parameters[0], - getTimeZone: Parameters[1] + getDatatableUtilities: TimeScaleArguments[0], + getTimeZone: TimeScaleArguments[1], + getForceNow: TimeScaleArguments[2] ) => { [ collapse, @@ -27,6 +31,6 @@ export const setupExpressions = ( mapToColumns, datatableColumn, getDatatable(formatFactory), - getTimeScale(getDatatableUtilities, getTimeZone), + getTimeScale(getDatatableUtilities, getTimeZone, getForceNow), ].forEach((expressionFn) => expressions.registerFunction(expressionFn)); }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index 1ce184e623e7a..4107cd0c14fa5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -329,7 +329,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ const fieldInfoUnavailable = existenceFetchFailed || existenceFetchTimeout || currentIndexPattern?.hasRestrictions; - const editPermission = indexPatternFieldEditor.userPermissions.editIndexPattern(); + const editPermission = + indexPatternFieldEditor.userPermissions.editIndexPattern() || !currentIndexPattern.isPersisted; const unfilteredFieldGroups: FieldGroups = useMemo(() => { const containsData = (field: IndexPatternField) => { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 6019f566e0ba6..10465a88f1623 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -334,7 +334,8 @@ export class LensPlugin { async () => { const { getTimeZone } = await import('./utils'); return getTimeZone(core.uiSettings); - } + }, + () => startServices().plugins.data.nowProvider.get() ); const getPresentationUtilContext = () => diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts index 92a3ef01fdb7d..ae7e84c61b3c5 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts @@ -118,6 +118,15 @@ describe('IndexPattern Data Source', () => { query: { sql: 'SELECT * FROM foo' }, }, }, + fieldList: [ + { + id: 'col1', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + ], } as unknown as TextBasedLanguagesPrivateState; }); @@ -159,7 +168,9 @@ describe('IndexPattern Data Source', () => { describe('#getPersistedState', () => { it('should persist from saved state', async () => { expect(textBasedLanguagesDatasource.getPersistableState(baseState)).toEqual({ - state: baseState, + state: { + layers: baseState.layers, + }, savedObjectReferences: [ { name: 'textBasedLanguages-datasource-layer-a', type: 'index-pattern', id: 'foo' }, ], @@ -649,6 +660,22 @@ describe('IndexPattern Data Source', () => { index: 'foo', }, }, + fieldList: [ + { + id: 'col1', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + { + id: 'col2', + name: 'Test 2', + meta: { + type: 'number', + }, + }, + ], } as unknown as TextBasedLanguagesPrivateState; publicAPI = textBasedLanguagesDatasource.getPublicAPI({ @@ -661,6 +688,28 @@ describe('IndexPattern Data Source', () => { { columnId: 'col2', fields: ['Test 2'] }, ]); }); + + it('should return only the columns that exist on the query', () => { + const state = { + ...baseState, + fieldList: [ + { + id: 'col2', + name: 'Test 2', + meta: { + type: 'number', + }, + }, + ], + } as unknown as TextBasedLanguagesPrivateState; + + publicAPI = textBasedLanguagesDatasource.getPublicAPI({ + state, + layerId: 'a', + indexPatterns, + }); + expect(publicAPI.getTableSpec()).toEqual([]); + }); }); describe('getOperationForColumnId', () => { diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx index e9ab24b8392c6..7157f643c6e8c 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx @@ -360,7 +360,7 @@ export function getTextBasedLanguagesDatasource({ customLabel = selectedField?.fieldName; } - const columnExists = props.state.fieldList.some((f) => f.name === selectedField?.fieldName); + const columnExists = props.state.fieldList.some((f) => f.name === customLabel); render( { + const columns = state.layers[layerId]?.columns.filter((c) => { + const columnExists = state?.fieldList?.some((f) => f.name === c?.fieldName); + if (columnExists) return c; + }); return ( - state.layers[layerId]?.columns?.map((column) => ({ + columns.map((column) => ({ columnId: column.columnId, fields: [column.fieldName], })) || [] diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts index bc511bb2b86ce..1e7096414d19e 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts @@ -5,7 +5,7 @@ * 2.0. */ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; - +import type { DatatableColumn } from '@kbn/expressions-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; @@ -14,7 +14,9 @@ import { getIndexPatternFromTextBasedQuery, loadIndexPatternRefs, getStateFromAggregateQuery, + getAllColumns, } from './utils'; +import type { TextBasedLanguagesLayerColumn } from './types'; import { type AggregateQuery } from '@kbn/es-query'; jest.mock('./fetch_data_from_aggregate_query', () => ({ @@ -73,6 +75,74 @@ describe('Text based languages utils', () => { }); }); + describe('getAllColumns', () => { + it('should remove columns that do not exist on the query and remove duplicates', async () => { + const existingOnLayer = [ + { + fieldName: 'time', + columnId: 'time', + meta: { + type: 'date', + }, + }, + { + fieldName: 'bytes', + columnId: 'bytes', + meta: { + type: 'number', + }, + }, + ] as TextBasedLanguagesLayerColumn[]; + const columnsFromQuery = [ + { + name: 'timestamp', + id: 'timestamp', + meta: { + type: 'date', + }, + }, + { + name: 'bytes', + id: 'bytes', + meta: { + type: 'number', + }, + }, + { + name: 'memory', + id: 'memory', + meta: { + type: 'number', + }, + }, + ] as DatatableColumn[]; + const allColumns = getAllColumns(existingOnLayer, columnsFromQuery); + expect(allColumns).toStrictEqual([ + { + fieldName: 'bytes', + columnId: 'bytes', + meta: { + type: 'number', + }, + }, + { + fieldName: 'timestamp', + columnId: 'timestamp', + meta: { + type: 'date', + }, + }, + { + fieldName: 'memory', + columnId: 'memory', + meta: { + type: 'number', + }, + }, + ]); + }); + }); + describe('getStateFromAggregateQuery', () => { it('should return the correct state', async () => { const state = { diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts index c4e41103f0fd1..5504cd39bd6a1 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts @@ -35,6 +35,34 @@ export async function loadIndexPatternRefs( }); } +export const getAllColumns = ( + existingColumns: TextBasedLanguagesLayerColumn[], + columnsFromQuery: DatatableColumn[] +) => { + // filter out columns that do not exist on the query + const columns = existingColumns.filter((c) => { + const columnExists = columnsFromQuery?.some((f) => f.name === c?.fieldName); + if (columnExists) return c; + }); + const allCols = [ + ...columns, + ...columnsFromQuery.map((c) => ({ columnId: c.id, fieldName: c.id, meta: c.meta })), + ]; + const uniqueIds: string[] = []; + + return allCols.filter((col) => { + const isDuplicate = uniqueIds.includes(col.columnId); + + if (!isDuplicate) { + uniqueIds.push(col.columnId); + + return true; + } + + return false; + }); +}; + export async function getStateFromAggregateQuery( state: TextBasedLanguagesPrivateState, query: AggregateQuery, @@ -59,12 +87,7 @@ export async function getStateFromAggregateQuery( const dataView = await dataViews.get(index); timeFieldName = dataView.timeFieldName; columnsFromQuery = table?.columns ?? []; - const existingColumns = state.layers[newLayerId].allColumns; - - allColumns = [ - ...existingColumns, - ...columnsFromQuery.map((c) => ({ columnId: c.id, fieldName: c.id, meta: c.meta })), - ]; + allColumns = getAllColumns(state.layers[newLayerId].allColumns, columnsFromQuery); } catch (e) { errors.push(e); } diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index 9f2d0920983ad..7a0c94d5d1b33 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -19,8 +19,8 @@ import { NumberDisplay, PieChartTypes, PieVisualizationState, - isPartitionShape, } from '../../../common'; +import { isPartitionShape } from '../../../common/visualizations'; import type { PieChartType } from '../../../common/types'; import { PartitionChartsMeta } from './partition_charts_meta'; diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index 7fd65d44bb0b6..282308753698e 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -34,7 +34,8 @@ import { VisState850, LensDocShape850, } from './types'; -import { DOCUMENT_FIELD_NAME, layerTypes, LegacyMetricState, isPartitionShape } from '../../common'; +import { DOCUMENT_FIELD_NAME, layerTypes, LegacyMetricState } from '../../common'; +import { isPartitionShape } from '../../common/visualizations'; import { LensDocShape } from './saved_object_migrations'; export const commonRenameOperationsForFormula = ( diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx index 69467d7ecea8b..4a358a5d43863 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx @@ -11,7 +11,6 @@ import styled from 'styled-components'; import { HttpStart } from '@kbn/core/public'; import { addIdToItem } from '@kbn/securitysolution-utils'; import { - CreateExceptionListItemSchema, ExceptionListItemSchema, ExceptionListType, NamespaceType, @@ -24,6 +23,7 @@ import { import { CreateExceptionListItemBuilderSchema, ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, OperatorOption, containsValueListEntry, filterExceptionItems, @@ -68,7 +68,7 @@ const initialState: State = { export interface OnChangeProps { errorExists: boolean; - exceptionItems: Array; + exceptionItems: ExceptionsBuilderReturnExceptionItem[]; exceptionsToDelete: ExceptionListItemSchema[]; warningExists: boolean; } @@ -84,15 +84,16 @@ export interface ExceptionBuilderProps { isNestedDisabled: boolean; isOrDisabled: boolean; isOrHidden?: boolean; - listId: string; - listNamespaceType: NamespaceType; + listId: string | undefined; + listNamespaceType: NamespaceType | undefined; listType: ExceptionListType; listTypeSpecificIndexPatternFilter?: ( pattern: DataViewBase, type: ExceptionListType ) => DataViewBase; onChange: (arg: OnChangeProps) => void; - ruleName: string; + exceptionItemName?: string; + ruleName?: string; isDisabled?: boolean; operatorsList?: OperatorOption[]; } @@ -113,6 +114,7 @@ export const ExceptionBuilderComponent = ({ listTypeSpecificIndexPatternFilter, onChange, ruleName, + exceptionItemName, isDisabled = false, osTypes, operatorsList, @@ -289,10 +291,10 @@ export const ExceptionBuilderComponent = ({ const newException = getNewExceptionItem({ listId, namespaceType: listNamespaceType, - ruleName, + ruleName: exceptionItemName ?? `${ruleName ?? 'Rule'} - Exception item`, }); setUpdateExceptions([...exceptions, { ...newException }]); - }, [setUpdateExceptions, exceptions, listId, listNamespaceType, ruleName]); + }, [listId, listNamespaceType, exceptionItemName, ruleName, setUpdateExceptions, exceptions]); // The builder can have existing exception items, or new exception items that have yet // to be created (and thus lack an id), this was creating some React bugs with relying diff --git a/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx b/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx index 6a8802599f6cc..dab912902ba46 100644 --- a/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx +++ b/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx @@ -43,35 +43,40 @@ export function FlameGraphsView({ children }: { children: React.ReactElement }) services: { fetchElasticFlamechart }, } = useProfilingDependencies(); - const state = useTimeRangeAsync(() => { - return Promise.all([ - fetchElasticFlamechart({ - timeFrom: new Date(timeRange.start).getTime() / 1000, - timeTo: new Date(timeRange.end).getTime() / 1000, - kuery, - }), - comparisonTimeRange.start && comparisonTimeRange.end - ? fetchElasticFlamechart({ - timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000, - timeTo: new Date(comparisonTimeRange.end).getTime() / 1000, - kuery: comparisonKuery, - }) - : Promise.resolve(undefined), - ]).then(([primaryFlamegraph, comparisonFlamegraph]) => { - return { - primaryFlamegraph, - comparisonFlamegraph, - }; - }); - }, [ - timeRange.start, - timeRange.end, - kuery, - comparisonTimeRange.start, - comparisonTimeRange.end, - comparisonKuery, - fetchElasticFlamechart, - ]); + const state = useTimeRangeAsync( + ({ http }) => { + return Promise.all([ + fetchElasticFlamechart({ + http, + timeFrom: new Date(timeRange.start).getTime() / 1000, + timeTo: new Date(timeRange.end).getTime() / 1000, + kuery, + }), + comparisonTimeRange.start && comparisonTimeRange.end + ? fetchElasticFlamechart({ + http, + timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000, + timeTo: new Date(comparisonTimeRange.end).getTime() / 1000, + kuery: comparisonKuery, + }) + : Promise.resolve(undefined), + ]).then(([primaryFlamegraph, comparisonFlamegraph]) => { + return { + primaryFlamegraph, + comparisonFlamegraph, + }; + }); + }, + [ + timeRange.start, + timeRange.end, + kuery, + comparisonTimeRange.start, + comparisonTimeRange.end, + comparisonKuery, + fetchElasticFlamechart, + ] + ); const { data } = state; @@ -173,7 +178,6 @@ export function FlameGraphsView({ children }: { children: React.ReactElement }) = ({ id, - height, comparisonMode, primaryFlamegraph, comparisonFlamegraph, diff --git a/x-pack/plugins/profiling/public/components/functions_view/index.tsx b/x-pack/plugins/profiling/public/components/functions_view/index.tsx index 94410f961c469..2c7b25754f3d9 100644 --- a/x-pack/plugins/profiling/public/components/functions_view/index.tsx +++ b/x-pack/plugins/profiling/public/components/functions_view/index.tsx @@ -42,28 +42,36 @@ export function FunctionsView({ children }: { children: React.ReactElement }) { services: { fetchTopNFunctions }, } = useProfilingDependencies(); - const state = useTimeRangeAsync(() => { - return fetchTopNFunctions({ - timeFrom: new Date(timeRange.start).getTime() / 1000, - timeTo: new Date(timeRange.end).getTime() / 1000, - startIndex: 0, - endIndex: 1000, - kuery, - }); - }, [timeRange.start, timeRange.end, kuery, fetchTopNFunctions]); + const state = useTimeRangeAsync( + ({ http }) => { + return fetchTopNFunctions({ + http, + timeFrom: new Date(timeRange.start).getTime() / 1000, + timeTo: new Date(timeRange.end).getTime() / 1000, + startIndex: 0, + endIndex: 1000, + kuery, + }); + }, + [timeRange.start, timeRange.end, kuery, fetchTopNFunctions] + ); - const comparisonState = useTimeRangeAsync(() => { - if (!comparisonTimeRange.start || !comparisonTimeRange.end) { - return undefined; - } - return fetchTopNFunctions({ - timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000, - timeTo: new Date(comparisonTimeRange.end).getTime() / 1000, - startIndex: 0, - endIndex: 1000, - kuery: comparisonKuery, - }); - }, [comparisonTimeRange.start, comparisonTimeRange.end, comparisonKuery, fetchTopNFunctions]); + const comparisonState = useTimeRangeAsync( + ({ http }) => { + if (!comparisonTimeRange.start || !comparisonTimeRange.end) { + return undefined; + } + return fetchTopNFunctions({ + http, + timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000, + timeTo: new Date(comparisonTimeRange.end).getTime() / 1000, + startIndex: 0, + endIndex: 1000, + kuery: comparisonKuery, + }); + }, + [comparisonTimeRange.start, comparisonTimeRange.end, comparisonKuery, fetchTopNFunctions] + ); const routePath = useProfilingRoutePath() as | '/functions' diff --git a/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx b/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx index 23bb4a7bd4b6f..19834410c6b7a 100644 --- a/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx +++ b/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx @@ -50,29 +50,33 @@ export function StackTracesView() { rangeTo, }); - const state = useTimeRangeAsync(() => { - if (!topNType) { - return Promise.resolve({ charts: [], metadata: {} }); - } - return fetchTopN({ - type: topNType, - timeFrom: new Date(timeRange.start).getTime() / 1000, - timeTo: new Date(timeRange.end).getTime() / 1000, - kuery, - }).then((response: TopNResponse) => { - const totalCount = response.TotalCount; - const samples = response.TopN; - const charts = groupSamplesByCategory({ - samples, - totalCount, - metadata: response.Metadata, - labels: response.Labels, + const state = useTimeRangeAsync( + ({ http }) => { + if (!topNType) { + return Promise.resolve({ charts: [], metadata: {} }); + } + return fetchTopN({ + http, + type: topNType, + timeFrom: new Date(timeRange.start).getTime() / 1000, + timeTo: new Date(timeRange.end).getTime() / 1000, + kuery, + }).then((response: TopNResponse) => { + const totalCount = response.TotalCount; + const samples = response.TopN; + const charts = groupSamplesByCategory({ + samples, + totalCount, + metadata: response.Metadata, + labels: response.Labels, + }); + return { + charts, + }; }); - return { - charts, - }; - }); - }, [topNType, timeRange.start, timeRange.end, fetchTopN, kuery]); + }, + [topNType, timeRange.start, timeRange.end, fetchTopN, kuery] + ); const [highlightedSubchart, setHighlightedSubchart] = useState( undefined diff --git a/x-pack/plugins/profiling/public/components/topn_functions.tsx b/x-pack/plugins/profiling/public/components/topn_functions.tsx index 4d8522913a245..8421b99b5dc7d 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions.tsx @@ -144,6 +144,9 @@ export const TopNFunctionsTable = ({ defaultMessage: 'Samples', }), align: 'right', + render: (_, { samples }) => { + return {samples}; + }, }, { field: TopNFunctionSortField.ExclusiveCPU, diff --git a/x-pack/plugins/profiling/public/hooks/use_async.ts b/x-pack/plugins/profiling/public/hooks/use_async.ts index ea6da578c3879..7156b4e8bfffd 100644 --- a/x-pack/plugins/profiling/public/hooks/use_async.ts +++ b/x-pack/plugins/profiling/public/hooks/use_async.ts @@ -4,8 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { HttpStart } from '@kbn/core-http-browser'; -import { useEffect, useState } from 'react'; +import { HttpFetchOptions, HttpHandler, HttpStart } from '@kbn/core-http-browser'; +import { AbortError } from '@kbn/kibana-utils-plugin/common'; +import { useEffect, useRef, useState } from 'react'; +import { Overwrite, ValuesType } from 'utility-types'; import { useProfilingDependencies } from '../components/contexts/profiling_dependencies/use_profiling_dependencies'; export enum AsyncStatus { @@ -20,8 +22,22 @@ export interface AsyncState { status: AsyncStatus; } +const HTTP_METHODS = ['fetch', 'get', 'post', 'put', 'delete', 'patch'] as const; + +type HttpMethod = ValuesType; + +type AutoAbortedHttpMethod = ( + path: string, + options: Omit +) => ReturnType; + +export type AutoAbortedHttpService = Overwrite< + HttpStart, + Record +>; + export type UseAsync = ( - fn: ({ http }: { http: HttpStart }) => Promise | undefined, + fn: ({ http }: { http: AutoAbortedHttpService }) => Promise | undefined, dependencies: any[] ) => AsyncState; @@ -37,8 +53,30 @@ export const useAsync: UseAsync = (fn, dependencies) => { const { data, error } = asyncState; + const controllerRef = useRef(new AbortController()); + useEffect(() => { - const returnValue = fn({ http }); + controllerRef.current.abort(); + + controllerRef.current = new AbortController(); + + const autoAbortedMethods = {} as Record; + + for (const key of HTTP_METHODS) { + autoAbortedMethods[key] = (path, options) => { + return http[key](path, { ...options, signal: controllerRef.current.signal }).catch( + (err) => { + if (err.name === 'AbortError') { + // return never-resolving promise + return new Promise(() => {}); + } + throw err; + } + ); + }; + } + + const returnValue = fn({ http: { ...http, ...autoAbortedMethods } }); if (returnValue === undefined) { setAsyncState({ @@ -63,13 +101,23 @@ export const useAsync: UseAsync = (fn, dependencies) => { }); returnValue.catch((nextError) => { + if (nextError instanceof AbortError) { + return; + } setAsyncState({ status: AsyncStatus.Settled, error: nextError, }); + throw nextError; }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [http, ...dependencies]); + useEffect(() => { + return () => { + controllerRef.current.abort(); + }; + }, []); + return asyncState; }; diff --git a/x-pack/plugins/profiling/public/plugin.tsx b/x-pack/plugins/profiling/public/plugin.tsx index 080941729b014..ead023a079e29 100644 --- a/x-pack/plugins/profiling/public/plugin.tsx +++ b/x-pack/plugins/profiling/public/plugin.tsx @@ -90,7 +90,7 @@ export class ProfilingPlugin implements Plugin { unknown ]; - const profilingFetchServices = getServices(coreStart); + const profilingFetchServices = getServices(); const { renderApp } = await import('./app'); function pushKueryToSubject(location: Location) { diff --git a/x-pack/plugins/profiling/public/services.ts b/x-pack/plugins/profiling/public/services.ts index a1f345dc96a2c..4dcad550d0bbb 100644 --- a/x-pack/plugins/profiling/public/services.ts +++ b/x-pack/plugins/profiling/public/services.ts @@ -4,21 +4,23 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { CoreStart, HttpFetchQuery } from '@kbn/core/public'; +import { HttpFetchQuery } from '@kbn/core/public'; import { getRoutePaths } from '../common'; import { BaseFlameGraph, createFlameGraph, ElasticFlameGraph } from '../common/flamegraph'; import { TopNFunctions } from '../common/functions'; import { TopNResponse } from '../common/topn'; +import { AutoAbortedHttpService } from './hooks/use_async'; export interface Services { fetchTopN: (params: { + http: AutoAbortedHttpService; type: string; timeFrom: number; timeTo: number; kuery: string; }) => Promise; fetchTopNFunctions: (params: { + http: AutoAbortedHttpService; timeFrom: number; timeTo: number; startIndex: number; @@ -26,42 +28,31 @@ export interface Services { kuery: string; }) => Promise; fetchElasticFlamechart: (params: { + http: AutoAbortedHttpService; timeFrom: number; timeTo: number; kuery: string; }) => Promise; } -export function getServices(core: CoreStart): Services { +export function getServices(): Services { const paths = getRoutePaths(); return { - fetchTopN: async ({ type, timeFrom, timeTo, kuery }) => { + fetchTopN: async ({ http, type, timeFrom, timeTo, kuery }) => { try { const query: HttpFetchQuery = { timeFrom, timeTo, kuery, }; - return await core.http.get(`${paths.TopN}/${type}`, { query }); + return await http.get(`${paths.TopN}/${type}`, { query }); } catch (e) { return e; } }, - fetchTopNFunctions: async ({ - timeFrom, - timeTo, - startIndex, - endIndex, - kuery, - }: { - timeFrom: number; - timeTo: number; - startIndex: number; - endIndex: number; - kuery: string; - }) => { + fetchTopNFunctions: async ({ http, timeFrom, timeTo, startIndex, endIndex, kuery }) => { try { const query: HttpFetchQuery = { timeFrom, @@ -70,28 +61,20 @@ export function getServices(core: CoreStart): Services { endIndex, kuery, }; - return await core.http.get(paths.TopNFunctions, { query }); + return await http.get(paths.TopNFunctions, { query }); } catch (e) { return e; } }, - fetchElasticFlamechart: async ({ - timeFrom, - timeTo, - kuery, - }: { - timeFrom: number; - timeTo: number; - kuery: string; - }) => { + fetchElasticFlamechart: async ({ http, timeFrom, timeTo, kuery }) => { try { const query: HttpFetchQuery = { timeFrom, timeTo, kuery, }; - const baseFlamegraph: BaseFlameGraph = await core.http.get(paths.Flamechart, { query }); + const baseFlamegraph = (await http.get(paths.Flamechart, { query })) as BaseFlameGraph; return createFlameGraph(baseFlamegraph); } catch (e) { return e; diff --git a/x-pack/plugins/profiling/server/routes/flamechart.ts b/x-pack/plugins/profiling/server/routes/flamechart.ts index 772d505817484..49f0415049ad7 100644 --- a/x-pack/plugins/profiling/server/routes/flamechart.ts +++ b/x-pack/plugins/profiling/server/routes/flamechart.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteRegisterParameters } from '.'; import { getRoutePaths } from '../../common'; import { createCalleeTree } from '../../common/callee'; +import { handleRouteHandlerError } from '../utils/handle_route_error_handler'; import { createBaseFlameGraph } from '../../common/flamegraph'; import { createProfilingEsClient } from '../utils/create_profiling_es_client'; import { withProfilingSpan } from '../utils/with_profiling_span'; @@ -71,14 +72,8 @@ export function registerFlameChartSearchRoute({ router, logger }: RouteRegisterP logger.info('returning payload response to client'); return response.ok({ body: flamegraph }); - } catch (e) { - logger.error(e); - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: e.message, - }, - }); + } catch (error) { + return handleRouteHandlerError({ error, logger, response }); } } ); diff --git a/x-pack/plugins/profiling/server/routes/functions.ts b/x-pack/plugins/profiling/server/routes/functions.ts index adbbc59003376..9d3eed222b908 100644 --- a/x-pack/plugins/profiling/server/routes/functions.ts +++ b/x-pack/plugins/profiling/server/routes/functions.ts @@ -9,6 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { RouteRegisterParameters } from '.'; import { getRoutePaths } from '../../common'; import { createTopNFunctions } from '../../common/functions'; +import { handleRouteHandlerError } from '../utils/handle_route_error_handler'; import { createProfilingEsClient } from '../utils/create_profiling_es_client'; import { withProfilingSpan } from '../utils/with_profiling_span'; import { getClient } from './compat'; @@ -72,14 +73,8 @@ export function registerTopNFunctionsSearchRoute({ router, logger }: RouteRegist return response.ok({ body: topNFunctions, }); - } catch (e) { - logger.error(e); - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: e.message, - }, - }); + } catch (error) { + return handleRouteHandlerError({ error, logger, response }); } } ); diff --git a/x-pack/plugins/profiling/server/routes/topn.ts b/x-pack/plugins/profiling/server/routes/topn.ts index 790f40049e167..a8a7efc01bb52 100644 --- a/x-pack/plugins/profiling/server/routes/topn.ts +++ b/x-pack/plugins/profiling/server/routes/topn.ts @@ -14,6 +14,7 @@ import { computeBucketWidthFromTimeRangeAndBucketCount } from '../../common/hist import { groupStackFrameMetadataByStackTrace, StackTraceID } from '../../common/profiling'; import { getFieldNameForTopNType, TopNType } from '../../common/stack_traces'; import { createTopNSamples, getTopNAggregationRequest, TopNResponse } from '../../common/topn'; +import { handleRouteHandlerError } from '../utils/handle_route_error_handler'; import { ProfilingRequestHandlerContext } from '../types'; import { createProfilingEsClient, ProfilingESClient } from '../utils/create_profiling_es_client'; import { withProfilingSpan } from '../utils/with_profiling_span'; @@ -189,15 +190,8 @@ export function queryTopNCommon( kuery, }), }); - } catch (e) { - logger.error(e); - - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: 'Profiling TopN request failed: ' + e.message + '; full error ' + e.toString(), - }, - }); + } catch (error) { + return handleRouteHandlerError({ error, logger, response }); } } ); diff --git a/x-pack/plugins/profiling/server/utils/handle_route_error_handler.ts b/x-pack/plugins/profiling/server/utils/handle_route_error_handler.ts new file mode 100644 index 0000000000000..782beeeae15cb --- /dev/null +++ b/x-pack/plugins/profiling/server/utils/handle_route_error_handler.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 { KibanaResponseFactory } from '@kbn/core-http-server'; +import { Logger } from '@kbn/logging'; +import { WrappedElasticsearchClientError } from '@kbn/observability-plugin/server'; +import { errors } from '@elastic/elasticsearch'; + +export function handleRouteHandlerError({ + error, + logger, + response, +}: { + error: any; + response: KibanaResponseFactory; + logger: Logger; +}) { + if ( + error instanceof WrappedElasticsearchClientError && + error.originalError instanceof errors.RequestAbortedError + ) { + return response.custom({ + statusCode: 499, + body: { + message: 'Client closed request', + }, + }); + } + logger.error(error); + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message, + }, + }); +} diff --git a/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx b/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx index 22236f77ffdae..b535a8a94a3ac 100644 --- a/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx @@ -16,6 +16,7 @@ import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, + EuiMarkdownFormat, EuiSpacer, EuiSteps, EuiText, @@ -178,7 +179,9 @@ export const ReportDiagnostic = ({ apiClient }: Props) => { {help.length ? ( -

{help.join('\n')}

+

+ {help.join('\n')} +

) : null} diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 6357dfd766a4c..7b2c4e034a963 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -281,6 +281,7 @@ export const DETECTION_ENGINE_RULES_BULK_UPDATE = `${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const; export const INTERNAL_RISK_SCORE_URL = '/internal/risk_score' as const; +export const RISK_SCORE_RESTART_TRANSFORMS = `${INTERNAL_RISK_SCORE_URL}/transforms/restart`; export const DEV_TOOL_PREBUILT_CONTENT = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/{console_id}` as const; export const devToolPrebuiltContentUrl = (spaceId: string, consoleId: string) => @@ -295,7 +296,6 @@ export const RISK_SCORE_CREATE_INDEX = `${INTERNAL_RISK_SCORE_URL}/indices/creat export const RISK_SCORE_DELETE_INDICES = `${INTERNAL_RISK_SCORE_URL}/indices/delete`; export const RISK_SCORE_CREATE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/create`; export const RISK_SCORE_DELETE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/delete`; - /** * Internal detection engine routes */ 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 index 425928d8fa208..60b7fd9141b29 100644 --- 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 @@ -28,7 +28,7 @@ describe('find_exception_list_references_schema', () => { }); }); - test('"ids" cannot be undefined', () => { + test('"ids" can be optional', () => { const payload: Omit = { list_ids: '123,456', namespace_types: 'single,agnostic', @@ -37,11 +37,14 @@ describe('find_exception_list_references_schema', () => { 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({}); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + list_ids: ['123', '456'], + namespace_types: ['single', 'agnostic'], + }); }); - test('"list_ids" cannot be undefined', () => { + test('"list_ids" can be undefined', () => { const payload: Omit = { ids: 'abc', namespace_types: 'single', @@ -50,10 +53,11 @@ describe('find_exception_list_references_schema', () => { 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({}); + expect(formatErrors(output.errors)).toEqual([]); + expect(output.schema).toEqual({ + ids: ['abc'], + namespace_types: ['single'], + }); }); test('defaults "namespacetypes" to ["single"] if none set', () => { 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 index 8cd21df8ab08b..367f0c157ebfc 100644 --- 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 @@ -9,13 +9,21 @@ 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, - }) -); +// If ids and list_ids are undefined, route will fetch all lists matching the +// specified namespace type +export const findExceptionReferencesOnRuleSchema = t.intersection([ + t.exact( + t.type({ + namespace_types: DefaultNamespaceArray, + }) + ), + t.exact( + t.partial({ + ids: NonEmptyStringArray, + list_ids: NonEmptyStringArray, + }) + ), +]); export type FindExceptionReferencesOnRuleSchema = t.OutputOf< 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 index 743645ba0f818..d8ab3d64fe783 100644 --- 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 @@ -7,62 +7,66 @@ import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils'; import { - ruleReferenceSchema, + exceptionListRuleReferencesSchema, rulesReferencedByExceptionListsSchema, } from './find_exception_list_references_schema'; import type { - RuleReferenceSchema, + ExceptionListRuleReferencesSchema, RulesReferencedByExceptionListsSchema, } from './find_exception_list_references_schema'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; describe('find_exception_list_references_schema', () => { - describe('ruleReferenceSchema', () => { + describe('exceptionListRuleReferencesSchema', () => { test('validates all fields', () => { - const payload: RuleReferenceSchema = { - name: 'My rule', - id: '4656dc92-5832-11ea-8e2d-0242ac130003', - rule_id: 'my-rule-id', - exception_lists: [ + const payload: ExceptionListRuleReferencesSchema = { + ...getExceptionListSchemaMock(), + referenced_rules: [ { - id: 'myListId', - list_id: 'my-list-id', - namespace_type: 'single', - type: 'detection', + 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 decoded = exceptionListRuleReferencesSchema.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', - }); + expect(output.schema).toEqual(payload); }); 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', + const payload: ExceptionListRuleReferencesSchema & { extra_value?: string } = { extra_value: 'foo', - exception_lists: [ + ...getExceptionListSchemaMock(), + referenced_rules: [ { - id: 'myListId', - list_id: 'my-list-id', - namespace_type: 'single', - type: 'detection', + 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 decoded = exceptionListRuleReferencesSchema.decode(payload); const checked = exactCheck(payload, decoded); const output = foldLeftRight(checked); expect(formatErrors(output.errors)).toEqual(['invalid keys "extra_value"']); @@ -75,21 +79,24 @@ describe('find_exception_list_references_schema', () => { 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', - }, - ], - }, - ], + 'my-list-id': { + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + 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', + }, + ], + }, + ], + }, }, ], }; @@ -98,27 +105,7 @@ describe('find_exception_list_references_schema', () => { 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', - }, - ], - }, - ], - }); + expect(output.schema).toEqual(payload); }); test('validates "references" with empty array', () => { @@ -140,21 +127,24 @@ describe('find_exception_list_references_schema', () => { 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', - }, - ], - }, - ], + 'my-list-id': { + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + 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', + }, + ], + }, + ], + }, }, ], }; 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 index a7f2527edc096..2bdcd0ba4cc2b 100644 --- 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 @@ -7,11 +7,11 @@ import * as t from 'io-ts'; -import { listArray, list_id } from '@kbn/securitysolution-io-ts-list-types'; +import { exceptionListSchema, listArray, list_id } from '@kbn/securitysolution-io-ts-list-types'; import { rule_id, id, name } from '../common/schemas'; -export const ruleReferenceSchema = t.exact( +export const ruleReferenceRuleInfoSchema = t.exact( t.type({ name, id, @@ -20,9 +20,25 @@ export const ruleReferenceSchema = t.exact( }) ); -export type RuleReferenceSchema = t.OutputOf; +export type ExceptionListRuleReferencesInfoSchema = t.OutputOf; -export const rulesReferencedByExceptionListSchema = t.record(list_id, t.array(ruleReferenceSchema)); +export const exceptionListRuleReferencesSchema = t.intersection([ + exceptionListSchema, + t.exact( + t.type({ + referenced_rules: t.array(ruleReferenceRuleInfoSchema), + }) + ), +]); + +export type ExceptionListRuleReferencesSchema = t.OutputOf< + typeof exceptionListRuleReferencesSchema +>; + +export const rulesReferencedByExceptionListSchema = t.record( + list_id, + exceptionListRuleReferencesSchema +); export type RuleReferencesSchema = t.OutputOf; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/all/index.ts index 7bed3ffe95c0d..8cd20ad8837f0 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/all/index.ts @@ -10,11 +10,13 @@ import type { IEsSearchResponse } from '@kbn/data-plugin/common'; import type { Inspect, Maybe, PageInfoPaginated } from '../../../common'; import type { RequestOptionsPaginated } from '../..'; import type { SortableUsersFields } from '../common'; +import type { RiskSeverity } from '../../risk_score'; export interface User { name: string; lastSeen: string; domain: string; + risk?: RiskSeverity; } export interface UsersStrategyResponse extends IEsSearchResponse { diff --git a/x-pack/plugins/security_solution/common/types/risk_scores.ts b/x-pack/plugins/security_solution/common/types/risk_scores.ts new file mode 100644 index 0000000000000..8ee513c885530 --- /dev/null +++ b/x-pack/plugins/security_solution/common/types/risk_scores.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface ESProcessorConfig { + on_failure?: Processor[]; + ignore_failure?: boolean; + if?: string; + tag?: string; + [key: string]: unknown; +} + +export interface Processor { + [typeName: string]: ESProcessorConfig; +} + +export interface Pipeline { + name: string; + description?: string; + version?: number; + processors: string | Processor[]; + on_failure?: Processor[]; + isManaged?: boolean; +} diff --git a/x-pack/plugins/security_solution/common/utils/risk_score_modules.ts b/x-pack/plugins/security_solution/common/utils/risk_score_modules.ts index 4318a841cc46b..d08f6b05432d6 100644 --- a/x-pack/plugins/security_solution/common/utils/risk_score_modules.ts +++ b/x-pack/plugins/security_solution/common/utils/risk_score_modules.ts @@ -7,6 +7,7 @@ import { DEFAULT_ALERTS_INDEX } from '../constants'; import { RiskScoreEntity, RiskScoreFields } from '../search_strategy'; +import type { Pipeline, Processor } from '../types/risk_scores'; /** * * Since 8.5, all the transforms, scripts, @@ -203,8 +204,8 @@ export const getRiskScoreIngestPipelineOptions = ( riskScoreEntity: RiskScoreEntity, spaceId = 'default', stringifyScript?: boolean -) => { - const processors = [ +): Pipeline => { + const processors: Processor[] = [ { set: { field: 'ingest_timestamp', @@ -301,7 +302,6 @@ export const getCreateRiskScoreIndicesOptions = ({ }; }; -/** /** * This should be aligned with * console_templates/enable_user_risk_score.console step 8 diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts new file mode 100644 index 0000000000000..94cf890ba5dd8 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getNewRule } from '../../objects/rule'; +import { + ENABLE_HOST_RISK_SCORE_BUTTON, + ENABLE_USER_RISK_SCORE_BUTTON, + RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST, + RISK_SCORE_INSTALLATION_SUCCESS_TOAST, +} from '../../screens/entity_analytics'; +import { + deleteRiskScore, + intercepInstallRiskScoreModule, + waitForInstallRiskScoreModule, +} from '../../tasks/api_calls/risk_scores'; +import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects'; +import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { cleanKibana } from '../../tasks/common'; +import { login, visit } from '../../tasks/login'; +import { clickEnableRiskScore } from '../../tasks/risk_scores'; +import { RiskScoreEntity } from '../../tasks/risk_scores/common'; +import { + getRiskScoreLatestTransformId, + getRiskScorePivotTransformId, + getTransformState, +} from '../../tasks/risk_scores/transforms'; +import { ENTITY_ANALYTICS_URL } from '../../urls/navigation'; + +const spaceId = 'default'; + +describe('Enable risk scores', () => { + before(() => { + cleanKibana(); + login(); + createCustomRuleEnabled(getNewRule(), 'rule1'); + }); + + beforeEach(() => { + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.host, spaceId, deleteAll: true }); + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.user, spaceId, deleteAll: true }); + visit(ENTITY_ANALYTICS_URL); + }); + + afterEach(() => { + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.host, spaceId, deleteAll: true }); + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.user, spaceId, deleteAll: true }); + }); + + it('shows enable host risk button', () => { + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('exist'); + }); + + it('should install host risk score successfully', () => { + intercepInstallRiskScoreModule(); + clickEnableRiskScore(RiskScoreEntity.host); + waitForInstallRiskScoreModule(); + + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('be.disabled'); + + cy.get(RISK_SCORE_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.host)).should('exist'); + cy.get(RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.host)).should('exist'); + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('not.exist'); + getTransformState(getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + getTransformState(getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + findSavedObjects(RiskScoreEntity.host, spaceId).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.saved_objects.length).to.eq(11); + }); + }); + + it('shows enable user risk button', () => { + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('exist'); + }); + + it('should install user risk score successfully', () => { + intercepInstallRiskScoreModule(); + clickEnableRiskScore(RiskScoreEntity.user); + waitForInstallRiskScoreModule(); + + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('be.disabled'); + + cy.get(RISK_SCORE_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.user)).should('exist'); + + cy.get(RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.user)).should('exist'); + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('not.exist'); + getTransformState(getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + getTransformState(getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + + findSavedObjects(RiskScoreEntity.user, spaceId).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.saved_objects.length).to.eq(11); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts new file mode 100644 index 0000000000000..5fcaca256656c --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getNewRule } from '../../objects/rule'; +import { + RISK_SCORE_INSTALLATION_SUCCESS_TOAST, + UPGRADE_HOST_RISK_SCORE_BUTTON, + UPGRADE_USER_RISK_SCORE_BUTTON, + UPGRADE_CANCELLATION_BUTTON, + UPGRADE_CONFIRMARION_MODAL, + RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST, +} from '../../screens/entity_analytics'; +import { deleteRiskScore, installLegacyRiskScoreModule } from '../../tasks/api_calls/risk_scores'; +import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects'; +import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { cleanKibana } from '../../tasks/common'; +import { login, visit } from '../../tasks/login'; +import { + clickUpgradeRiskScore, + clickUpgradeRiskScoreConfirmed, + interceptUpgradeRiskScoreModule, + waitForUpgradeRiskScoreModule, +} from '../../tasks/risk_scores'; +import { RiskScoreEntity } from '../../tasks/risk_scores/common'; +import { + getRiskScoreLatestTransformId, + getRiskScorePivotTransformId, + getTransformState, +} from '../../tasks/risk_scores/transforms'; +import { ENTITY_ANALYTICS_URL } from '../../urls/navigation'; + +const spaceId = 'default'; + +describe('Upgrade risk scores', () => { + before(() => { + cleanKibana(); + login(); + createCustomRuleEnabled(getNewRule(), 'rule1'); + }); + + beforeEach(() => { + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.host, spaceId, deleteAll: true }); + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.user, spaceId, deleteAll: true }); + installLegacyRiskScoreModule(RiskScoreEntity.host, spaceId); + installLegacyRiskScoreModule(RiskScoreEntity.user, spaceId); + visit(ENTITY_ANALYTICS_URL); + }); + + afterEach(() => { + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.host, spaceId, deleteAll: true }); + deleteRiskScore({ riskScoreEntity: RiskScoreEntity.user, spaceId, deleteAll: true }); + }); + + it('shows upgrade host risk button', () => { + cy.get(UPGRADE_HOST_RISK_SCORE_BUTTON).should('be.visible'); + }); + + it('should show a confirmation modal for upgrading host risk score', () => { + clickUpgradeRiskScore(RiskScoreEntity.host); + cy.get(UPGRADE_CONFIRMARION_MODAL(RiskScoreEntity.host)).should('exist'); + }); + + it('display a link to host risk score Elastic doc', () => { + clickUpgradeRiskScore(RiskScoreEntity.host); + + cy.get(UPGRADE_CANCELLATION_BUTTON) + .get(`${UPGRADE_CONFIRMARION_MODAL(RiskScoreEntity.host)} a`) + .then((link) => { + expect(link.prop('href')).to.eql( + `https://www.elastic.co/guide/en/security/current/${RiskScoreEntity.host}-risk-score.html` + ); + }); + }); + + it('should upgrade host risk score successfully', () => { + clickUpgradeRiskScore(RiskScoreEntity.host); + + interceptUpgradeRiskScoreModule(RiskScoreEntity.host); + + clickUpgradeRiskScoreConfirmed(); + waitForUpgradeRiskScoreModule(); + + cy.get(UPGRADE_HOST_RISK_SCORE_BUTTON).should('be.disabled'); + + cy.get(RISK_SCORE_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.host)).should('exist'); + cy.get(RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.host)).should('exist'); + + cy.get(UPGRADE_HOST_RISK_SCORE_BUTTON).should('not.exist'); + getTransformState(getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + getTransformState(getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + findSavedObjects(RiskScoreEntity.host, spaceId).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.saved_objects.length).to.eq(11); + }); + }); + + it('shows upgrade user risk button', () => { + cy.get(UPGRADE_USER_RISK_SCORE_BUTTON).should('be.visible'); + }); + + it('should show a confirmation modal for upgrading user risk score', () => { + clickUpgradeRiskScore(RiskScoreEntity.user); + cy.get(UPGRADE_CONFIRMARION_MODAL(RiskScoreEntity.user)).should('exist'); + }); + + it('display a link to user risk score Elastic doc', () => { + clickUpgradeRiskScore(RiskScoreEntity.user); + + cy.get(UPGRADE_CANCELLATION_BUTTON) + .get(`${UPGRADE_CONFIRMARION_MODAL(RiskScoreEntity.user)} a`) + .then((link) => { + expect(link.prop('href')).to.eql( + `https://www.elastic.co/guide/en/security/current/${RiskScoreEntity.user}-risk-score.html` + ); + }); + }); + + it('should upgrade user risk score successfully', () => { + clickUpgradeRiskScore(RiskScoreEntity.user); + interceptUpgradeRiskScoreModule(RiskScoreEntity.user); + clickUpgradeRiskScoreConfirmed(); + waitForUpgradeRiskScoreModule(); + cy.get(UPGRADE_USER_RISK_SCORE_BUTTON).should('be.disabled'); + + cy.get(RISK_SCORE_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.user)).should('exist'); + cy.get(RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST(RiskScoreEntity.user)).should('exist'); + + cy.get(UPGRADE_USER_RISK_SCORE_BUTTON).should('not.exist'); + getTransformState(getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + getTransformState(getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId)).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.transforms[0].id).to.eq( + getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId) + ); + expect(res.body.transforms[0].state).to.eq('started'); + }); + + findSavedObjects(RiskScoreEntity.user, spaceId).then((res) => { + expect(res.status).to.eq(200); + expect(res.body.saved_objects.length).to.eq(11); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/timelines/unsaved_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/timelines/unsaved_timeline.cy.ts new file mode 100644 index 0000000000000..e18937ee1fc09 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/timelines/unsaved_timeline.cy.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Timeline } from '../../objects/timeline'; +import { + MODAL_CONFIRMATION_BTN, + MODAL_CONFIRMATION_CANCEL_BTN, +} from '../../screens/alerts_detection_rules'; +import { + ALERTS_PAGE, + APP_LEAVE_CONFIRM_MODAL, + CASES_PAGE, + MANAGE_PAGE, + OBSERVABILITY_ALERTS_PAGE, +} from '../../screens/kibana_navigation'; +import { TIMELINE_SAVE_MODAL } from '../../screens/timeline'; +import { cleanKibana } from '../../tasks/common'; +import { + navigateFromKibanaCollapsibleTo, + openKibanaNavigation, +} from '../../tasks/kibana_navigation'; +import { login, visit } from '../../tasks/login'; +import { closeTimelineUsingToggle } from '../../tasks/security_main'; +import { + addNameAndDescriptionToTimeline, + createNewTimeline, + populateTimeline, + waitForTimelineChanges, +} from '../../tasks/timeline'; +import { HOSTS_URL } from '../../urls/navigation'; + +describe('Save Timeline Prompts', () => { + before(() => { + cleanKibana(); + login(); + /* + * When timeline changes are pending, chrome would popup with + * a confirm dialog stating that `you can lose unsaved changed. + * Below changes will disable that. + * + * */ + cy.window().then((win) => { + win.onbeforeunload = null; + }); + }); + + beforeEach(() => { + visit(HOSTS_URL); + createNewTimeline(); + }); + + it('unchanged & unsaved timeline should NOT prompt when user navigates away', () => { + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); + cy.url().should('not.contain', HOSTS_URL); + }); + + it('Changed & unsaved timeline should prompt when user navigates away from security solution', () => { + populateTimeline(); + waitForTimelineChanges(); + closeTimelineUsingToggle(); + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + cy.get(MODAL_CONFIRMATION_BTN).click(); + }); + + it('Changed & unsaved timeline should NOT prompt when user navigates away within security solution where timelines are enabled', () => { + populateTimeline(); + + waitForTimelineChanges(); + closeTimelineUsingToggle(); + // navigate to any other page in security solution + openKibanaNavigation(); + cy.get(CASES_PAGE).click(); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); + }); + + it('Changed & unsaved timeline should prompt when user navigates away within security solution where timelines are disbaled eg. admin screen', () => { + populateTimeline(); + waitForTimelineChanges(); + openKibanaNavigation(); + cy.get(MANAGE_PAGE).click(); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + cy.get(MODAL_CONFIRMATION_BTN).click(); + }); + + it('Changed & saved timeline should NOT prompt when user navigates away out of security solution', () => { + populateTimeline(); + waitForTimelineChanges(); + closeTimelineUsingToggle(); + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + cy.get(MODAL_CONFIRMATION_CANCEL_BTN).click(); + addNameAndDescriptionToTimeline( + { + title: 'Some Timeline', + description: 'Some Timeline', + } as Timeline, + true + ); + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); + cy.url().should('not.contain', HOSTS_URL); + }); + + it('Changed & saved timeline should NOT prompt when user navigates within security solution where timelines are disabled', () => { + populateTimeline(); + waitForTimelineChanges(); + closeTimelineUsingToggle(); + openKibanaNavigation(); + cy.get(MANAGE_PAGE).click(); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + cy.get(MODAL_CONFIRMATION_CANCEL_BTN).click(); + addNameAndDescriptionToTimeline( + { + title: 'Some Timeline', + description: 'Some Timeline', + } as Timeline, + true + ); + openKibanaNavigation(); + cy.get(MANAGE_PAGE).click(); + cy.url().should('not.contain', HOSTS_URL); + }); + + it('When user navigates to the page where timeline is present, Time save modal shold not exists.', () => { + populateTimeline(); + waitForTimelineChanges(); + closeTimelineUsingToggle(); + openKibanaNavigation(); + cy.get(MANAGE_PAGE).click(); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + cy.get(MODAL_CONFIRMATION_BTN).click(); + + // Navigate back to HOSTS_URL and ensure that + // timeline save modal is NOT present + + openKibanaNavigation(); + cy.get(ALERTS_PAGE).click(); + cy.get(TIMELINE_SAVE_MODAL).should('not.exist'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts b/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts index ed4792a83c92b..4d60556173fd9 100644 --- a/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts +++ b/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { RiskScoreEntity } from '../tasks/risk_scores/common'; + export const ENABLE_HOST_RISK_SCORE_BUTTON = '[data-test-subj="enable_host_risk_score"]'; export const UPGRADE_HOST_RISK_SCORE_BUTTON = '[data-test-subj="host-risk-score-upgrade"]'; @@ -17,6 +19,13 @@ export const HOST_RISK_SCORE_NO_DATA_DETECTED = export const USER_RISK_SCORE_NO_DATA_DETECTED = '[data-test-subj="user-risk-score-no-data-detected"]'; +export const RISK_SCORE_DASHBOARDS_INSTALLATION_SUCCESS_TOAST = ( + riskScoreEntity: RiskScoreEntity +) => `[data-test-subj="${riskScoreEntity}RiskScoreDashboardsSuccessToast"]`; + +export const RISK_SCORE_INSTALLATION_SUCCESS_TOAST = (riskScoreEntity: RiskScoreEntity) => + `[data-test-subj="${riskScoreEntity}EnableSuccessToast"]`; + export const HOSTS_DONUT_CHART = '[data-test-subj="entity_analytics_hosts"] [data-test-subj="donut-chart"]'; @@ -37,3 +46,10 @@ export const ANOMALIES_TABLE = '[data-test-subj="entity_analytics_anomalies"] #entityAnalyticsDashboardAnomaliesTable'; export const ANOMALIES_TABLE_ROWS = '[data-test-subj="entity_analytics_anomalies"] .euiTableRow'; + +export const UPGRADE_CONFIRMARION_MODAL = (riskScoreEntity: RiskScoreEntity) => + `[data-test-subj="${riskScoreEntity}-risk-score-upgrade-confirmation-modal"]`; + +export const UPGRADE_CONFIRMATION_BUTTON = '[data-test-subj="confirmModalConfirmButton"]'; + +export const UPGRADE_CANCELLATION_BUTTON = '[data-test-subj="confirmModalCancelButton"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts index 67c84488911f2..d9b3f23f13873 100644 --- a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts +++ b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts @@ -30,6 +30,11 @@ export const MANAGE_PAGE = export const KIBANA_NAVIGATION_TOGGLE = '[data-test-subj="toggleNavButton"]'; +export const OBSERVABILITY_ALERTS_PAGE = + '[data-test-subj="collapsibleNavGroup-observability"] [title="Alerts"]'; + export const SPACES_BUTTON = '[data-test-subj="spacesNavSelector"]'; +export const APP_LEAVE_CONFIRM_MODAL = '[data-test-subj="appLeaveConfirmModal"]'; + export const getGoToSpaceMenuItem = (space: string) => `[data-test-subj="space-avatar-${space}"]`; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 87d70a73dbd1f..c2f83fe2b4760 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -237,6 +237,8 @@ export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]'; export const TIMELINE_EDIT_MODAL_OPEN_BUTTON = '[data-test-subj="save-timeline-button-icon"]'; +export const TIMELINE_SAVE_MODAL = '[data-test-subj="save-timeline-modal"]'; + export const TIMELINE_EDIT_MODAL_SAVE_BUTTON = '[data-test-subj="save-button"]'; export const TIMELINE_EXIT_FULL_SCREEN_BUTTON = '[data-test-subj="exit-full-screen"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index b8e38d3e11e0e..8be6f2b26dfb5 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -179,9 +179,7 @@ export const loadPrebuiltDetectionRules = () => { * load prebuilt rules by clicking button on page header */ export const loadPrebuiltDetectionRulesFromHeaderBtn = () => { - cy.get(LOAD_PREBUILT_RULES_ON_PAGE_HEADER_BTN) - .pipe(($el) => $el.trigger('click')) - .should('not.exist'); + cy.get(LOAD_PREBUILT_RULES_ON_PAGE_HEADER_BTN).click().should('not.exist'); }; export const openIntegrationsPopover = () => { diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/index.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/index.ts new file mode 100644 index 0000000000000..8180c47698ea7 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/index.ts @@ -0,0 +1,290 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ENTITY_ANALYTICS_URL } from '../../../urls/navigation'; +import { RISK_SCORE_URL } from '../../../urls/risk_score'; +import { visit } from '../../login'; +import { RiskScoreEntity } from '../../risk_scores/common'; +import { + getCreateLegacyRiskScoreIndicesOptions, + getCreateLegacyRiskScoreLatestIndicesOptions, +} from '../../risk_scores/indices'; +import { + getIngestPipelineName, + getLegacyIngestPipelineName, + getLegacyRiskScoreIngestPipelineOptions, +} from '../../risk_scores/ingest_pipelines'; +import { + getLegacyRiskHostCreateInitScriptOptions, + getLegacyRiskHostCreateLevelScriptOptions, + getLegacyRiskHostCreateMapScriptOptions, + getLegacyRiskHostCreateReduceScriptOptions, + getLegacyRiskScoreInitScriptId, + getLegacyRiskScoreLevelScriptId, + getLegacyRiskScoreMapScriptId, + getLegacyRiskScoreReduceScriptId, + getLegacyRiskUserCreateLevelScriptOptions, + getLegacyRiskUserCreateMapScriptOptions, + getLegacyRiskUserCreateReduceScriptOptions, + getRiskScoreInitScriptId, + getRiskScoreLevelScriptId, + getRiskScoreMapScriptId, + getRiskScoreReduceScriptId, +} from '../../risk_scores/stored_scripts'; +import { + createTransform, + deleteTransforms, + getCreateLegacyLatestTransformOptions, + getCreateLegacyMLHostPivotTransformOptions, + getCreateLegacyMLUserPivotTransformOptions, + getRiskScoreLatestTransformId, + getRiskScorePivotTransformId, + startTransforms, +} from '../../risk_scores/transforms'; +import { createIndex, deleteRiskScoreIndicies } from './indices'; +import { createIngestPipeline, deleteRiskScoreIngestPipelines } from './ingest_pipelines'; +import { deleteSavedObjects } from './saved_objects'; +import { createStoredScript, deleteStoredScripts } from './stored_scripts'; + +/** + * @deleteAll: If set to true, it deletes both old and new version. + * If set to false, it deletes legacy version only. + */ +export const deleteRiskScore = ({ + riskScoreEntity, + spaceId, + deleteAll, +}: { + riskScoreEntity: RiskScoreEntity; + spaceId?: string; + deleteAll: boolean; +}) => { + const transformIds = [ + getRiskScorePivotTransformId(riskScoreEntity, spaceId), + getRiskScoreLatestTransformId(riskScoreEntity, spaceId), + ]; + const legacyIngestPipelineNames = [getLegacyIngestPipelineName(riskScoreEntity)]; + const ingestPipelinesNames = deleteAll + ? [...legacyIngestPipelineNames, getIngestPipelineName(riskScoreEntity, spaceId)] + : legacyIngestPipelineNames; + + const legacyScriptIds = [ + ...(riskScoreEntity === RiskScoreEntity.host + ? [getLegacyRiskScoreInitScriptId(riskScoreEntity)] + : []), + getLegacyRiskScoreLevelScriptId(riskScoreEntity), + getLegacyRiskScoreMapScriptId(riskScoreEntity), + getLegacyRiskScoreReduceScriptId(riskScoreEntity), + ]; + const scripts = deleteAll + ? [ + ...legacyScriptIds, + ...(riskScoreEntity === RiskScoreEntity.host + ? [getRiskScoreInitScriptId(riskScoreEntity, spaceId)] + : []), + getRiskScoreLevelScriptId(riskScoreEntity, spaceId), + getRiskScoreMapScriptId(riskScoreEntity, spaceId), + getRiskScoreReduceScriptId(riskScoreEntity, spaceId), + ] + : legacyScriptIds; + + deleteTransforms(transformIds); + deleteRiskScoreIngestPipelines(ingestPipelinesNames); + deleteStoredScripts(scripts); + deleteSavedObjects(`${riskScoreEntity}RiskScoreDashboards`, deleteAll); + deleteRiskScoreIndicies(riskScoreEntity, spaceId); +}; + +const installLegacyHostRiskScoreModule = (spaceId: string) => { + /** + * Step 1 Upload script: ml_hostriskscore_levels_script + */ + createStoredScript(getLegacyRiskHostCreateLevelScriptOptions()) + .then(() => { + /** + * Step 2 Upload script: ml_hostriskscore_init_script + */ + return createStoredScript(getLegacyRiskHostCreateInitScriptOptions()); + }) + .then(() => { + /** + * Step 3 Upload script: ml_hostriskscore_map_script + */ + return createStoredScript(getLegacyRiskHostCreateMapScriptOptions()); + }) + .then(() => { + /** + * Step 4 Upload script: ml_hostriskscore_reduce_script + */ + return createStoredScript(getLegacyRiskHostCreateReduceScriptOptions()); + }) + .then(() => { + /** + * Step 5 Upload the ingest pipeline: ml_hostriskscore_ingest_pipeline + */ + return createIngestPipeline(getLegacyRiskScoreIngestPipelineOptions(RiskScoreEntity.host)); + }) + .then(() => { + /** + * Step 6 create ml_host_risk_score_{spaceId} index + */ + return createIndex( + getCreateLegacyRiskScoreIndicesOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.host, + }) + ); + }) + .then(() => { + /** + * Step 7 create transform: ml_hostriskscore_pivot_transform_{spaceId} + */ + return createTransform( + getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId), + getCreateLegacyMLHostPivotTransformOptions({ spaceId }) + ); + }) + .then(() => { + /** + * Step 8 create ml_host_risk_score_latest_{spaceId} index + */ + return createIndex( + getCreateLegacyRiskScoreLatestIndicesOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.host, + }) + ); + }) + .then(() => { + /** + * Step 9 create transform: ml_hostriskscore_latest_transform_{spaceId} + */ + return createTransform( + getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId), + getCreateLegacyLatestTransformOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.host, + }) + ); + }) + .then(() => { + /** + * Step 10 Start the pivot transform + * Step 11 Start the latest transform + */ + const transformIds = [ + getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId), + getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId), + ]; + return startTransforms(transformIds); + }) + .then(() => { + // refresh page + visit(ENTITY_ANALYTICS_URL); + }); +}; + +const installLegacyUserRiskScoreModule = async (spaceId = 'default') => { + /** + * Step 1 Upload script: ml_userriskscore_levels_script + */ + createStoredScript(getLegacyRiskUserCreateLevelScriptOptions()) + .then(() => { + /** + * Step 2 Upload script: ml_userriskscore_map_script + */ + return createStoredScript(getLegacyRiskUserCreateMapScriptOptions()); + }) + .then(() => { + /** + * Step 3 Upload script: ml_userriskscore_reduce_script + */ + return createStoredScript(getLegacyRiskUserCreateReduceScriptOptions()); + }) + .then(() => { + /** + * Step 4 Upload ingest pipeline: ml_userriskscore_ingest_pipeline + */ + return createIngestPipeline(getLegacyRiskScoreIngestPipelineOptions(RiskScoreEntity.user)); + }) + .then(() => { + /** + * Step 5 create ml_user_risk_score_{spaceId} index + */ + return createIndex( + getCreateLegacyRiskScoreIndicesOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.user, + }) + ); + }) + .then(() => { + /** + * Step 6 create Transform: ml_userriskscore_pivot_transform_{spaceId} + */ + return createTransform( + getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId), + getCreateLegacyMLUserPivotTransformOptions({ spaceId }) + ); + }) + .then(() => { + /** + * Step 7 create ml_user_risk_score_latest_{spaceId} index + */ + return createIndex( + getCreateLegacyRiskScoreLatestIndicesOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.user, + }) + ); + }) + .then(() => { + /** + * Step 8 create Transform: ml_userriskscore_latest_transform_{spaceId} + */ + return createTransform( + getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId), + getCreateLegacyLatestTransformOptions({ + spaceId, + riskScoreEntity: RiskScoreEntity.user, + }) + ); + }) + .then(() => { + /** + * Step 9 Start the pivot transform + * Step 10 Start the latest transform + */ + const transformIds = [ + getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId), + getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId), + ]; + return startTransforms(transformIds); + }) + .then(() => { + visit(ENTITY_ANALYTICS_URL); + }); +}; + +export const installLegacyRiskScoreModule = ( + riskScoreEntity: RiskScoreEntity, + spaceId = 'default' +) => { + if (riskScoreEntity === RiskScoreEntity.user) { + installLegacyUserRiskScoreModule(spaceId); + } else { + installLegacyHostRiskScoreModule(spaceId); + } +}; + +export const intercepInstallRiskScoreModule = () => { + cy.intercept(`POST`, RISK_SCORE_URL).as('install'); +}; + +export const waitForInstallRiskScoreModule = () => { + cy.wait(['@install'], { requestTimeout: 50000 }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/indices.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/indices.ts new file mode 100644 index 0000000000000..680159d43156d --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/indices.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { INDICES_URL } from '../../../urls/risk_score'; +import type { RiskScoreEntity } from '../../risk_scores/common'; +import { getLatestTransformIndex, getPivotTransformIndex } from '../../risk_scores/indices'; + +export const createIndex = (options: { + index: string; + mappings: string | Record; +}) => { + return cy.request({ + method: 'put', + url: `${INDICES_URL}/create`, + body: options, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +export const deleteRiskScoreIndicies = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => { + return cy + .request({ + method: 'post', + url: `${INDICES_URL}/delete`, + body: { + indices: [getPivotTransformIndex(riskScoreEntity, spaceId)], + }, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + failOnStatusCode: false, + }) + .then(() => { + return cy.request({ + method: 'post', + url: `${INDICES_URL}/delete`, + body: { + indices: [getLatestTransformIndex(riskScoreEntity, spaceId)], + }, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + failOnStatusCode: false, + }); + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/ingest_pipelines.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/ingest_pipelines.ts new file mode 100644 index 0000000000000..818b0cf8d18fd --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/ingest_pipelines.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 { INGEST_PIPELINES_URL } from '../../../urls/risk_score'; + +export const createIngestPipeline = (options: { name: string; processors: Array<{}> }) => { + return cy.request({ + method: 'post', + url: `${INGEST_PIPELINES_URL}`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + body: options, + }); +}; + +export const deleteRiskScoreIngestPipelines = (names: string[]) => { + return cy.request({ + method: 'delete', + url: `${INGEST_PIPELINES_URL}/${names.join(',')}`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + failOnStatusCode: false, + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/saved_objects.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/saved_objects.ts new file mode 100644 index 0000000000000..786396654b3bf --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/saved_objects.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RISK_SCORE_SAVED_OBJECTS_URL, SAVED_OBJECTS_URL } from '../../../urls/risk_score'; +import type { RiskScoreEntity } from '../../risk_scores/common'; +import { getRiskScoreTagName } from '../../risk_scores/saved_objects'; + +export const deleteSavedObjects = ( + templateName: `${RiskScoreEntity}RiskScoreDashboards`, + deleteAll: boolean +) => { + return cy.request({ + method: 'post', + url: `${RISK_SCORE_SAVED_OBJECTS_URL}/_bulk_delete/${templateName}`, + failOnStatusCode: false, + body: { + deleteAll, + }, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +export const createSavedObjects = (templateName: `${RiskScoreEntity}RiskScoreDashboards`) => { + return cy.request({ + method: 'post', + url: `${RISK_SCORE_SAVED_OBJECTS_URL}/_bulk_create/${templateName}`, + failOnStatusCode: false, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +export const findSavedObjects = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => { + const search = getRiskScoreTagName(riskScoreEntity, spaceId); + + const getReference = (tagId: string) => encodeURIComponent(`[{"type":"tag","id":"${tagId}"}]`); + + return cy + .request({ + method: 'get', + url: `${SAVED_OBJECTS_URL}/_find?fields=id&type=tag&sort_field=updated_at&search=${search}&search_fields=name`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }) + .then((res) => + cy.request({ + method: 'get', + url: `${SAVED_OBJECTS_URL}/_find?fields=id&type=index-pattern&type=tag&type=visualization&type=dashboard&type=lens&sort_field=updated_at&has_reference=${getReference( + res.body.saved_objects[0].id + )}`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }) + ); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/stored_scripts.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/stored_scripts.ts new file mode 100644 index 0000000000000..de5a2b3616075 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/risk_scores/stored_scripts.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 { STORED_SCRIPTS_URL } from '../../../urls/risk_score'; + +export const createStoredScript = (options: { id: string; script: {} }) => { + return cy.request({ + method: 'put', + url: `${STORED_SCRIPTS_URL}/create`, + body: options, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +const deleteStoredScript = (id: string) => { + return cy.request({ + method: 'delete', + url: `${STORED_SCRIPTS_URL}/delete`, + body: { id }, + failOnStatusCode: false, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +export const deleteStoredScripts = async (scriptIds: string[]) => { + await Promise.all(scriptIds.map((scriptId) => deleteStoredScript(scriptId))); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/common.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/common.ts new file mode 100644 index 0000000000000..03a064a4c7635 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/common.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 const enum RiskScoreEntity { + host = 'host', + user = 'user', +} diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/index.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/index.ts new file mode 100644 index 0000000000000..ab80122de1dd0 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/index.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 { + ENABLE_HOST_RISK_SCORE_BUTTON, + ENABLE_USER_RISK_SCORE_BUTTON, + UPGRADE_CONFIRMATION_BUTTON, + UPGRADE_HOST_RISK_SCORE_BUTTON, + UPGRADE_USER_RISK_SCORE_BUTTON, +} from '../../screens/entity_analytics'; +import { + INGEST_PIPELINES_URL, + RISK_SCORE_SAVED_OBJECTS_URL, + STORED_SCRIPTS_URL, + TRANSFORMS_URL, +} from '../../urls/risk_score'; +import { intercepInstallRiskScoreModule } from '../api_calls/risk_scores'; + +import { RiskScoreEntity } from './common'; +import { getLegacyIngestPipelineName } from './ingest_pipelines'; + +export const interceptUpgradeRiskScoreModule = (riskScoreEntity: RiskScoreEntity) => { + cy.intercept( + `POST`, + `${RISK_SCORE_SAVED_OBJECTS_URL}/_bulk_delete/${riskScoreEntity}RiskScoreDashboards` + ).as('deleteDashboards'); + cy.intercept(`POST`, `${TRANSFORMS_URL}/stop_transforms`).as('stopTransforms'); + cy.intercept(`POST`, `${TRANSFORMS_URL}/delete_transforms`).as('deleteTransforms'); + cy.intercept( + `DELETE`, + `${INGEST_PIPELINES_URL}/${getLegacyIngestPipelineName(riskScoreEntity)}` + ).as('deleteIngestPipelines'); + cy.intercept(`DELETE`, `${STORED_SCRIPTS_URL}/delete`).as('deleteScripts'); + intercepInstallRiskScoreModule(); +}; + +export const waitForUpgradeRiskScoreModule = () => { + cy.wait( + [ + '@deleteDashboards', + '@stopTransforms', + '@deleteTransforms', + '@deleteIngestPipelines', + '@deleteScripts', + '@install', + ], + { requestTimeout: 50000 } + ); +}; + +export const clickEnableRiskScore = (riskScoreEntity: RiskScoreEntity) => { + const button = + riskScoreEntity === RiskScoreEntity.user + ? ENABLE_USER_RISK_SCORE_BUTTON + : ENABLE_HOST_RISK_SCORE_BUTTON; + + cy.get(button).click(); +}; + +export const clickUpgradeRiskScore = (riskScoreEntity: RiskScoreEntity) => { + const button = + riskScoreEntity === RiskScoreEntity.user + ? UPGRADE_USER_RISK_SCORE_BUTTON + : UPGRADE_HOST_RISK_SCORE_BUTTON; + + cy.get(button).click(); +}; + +export const clickUpgradeRiskScoreConfirmed = () => { + cy.get(UPGRADE_CONFIRMATION_BUTTON).click(); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/indices.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/indices.ts new file mode 100644 index 0000000000000..6a7c7f317c8ca --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/indices.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RiskScoreEntity } from './common'; + +export const getPivotTransformIndex = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}_risk_score_${spaceId}`; + +export const getLatestTransformIndex = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}_risk_score_latest_${spaceId}`; + +export const getCreateLegacyRiskScoreIndicesOptions = ({ + spaceId = 'default', + riskScoreEntity, +}: { + spaceId?: string; + riskScoreEntity: RiskScoreEntity; +}) => { + const mappings = { + properties: { + [`${riskScoreEntity}.name`]: { + type: 'keyword', + }, + '@timestamp': { + type: 'date', + }, + ingest_timestamp: { + type: 'date', + }, + risk: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + risk_stats: { + properties: { + risk_score: { + type: 'float', + }, + }, + }, + }, + }; + return { + index: getPivotTransformIndex(riskScoreEntity, spaceId), + mappings, + }; +}; + +export const getCreateLegacyRiskScoreLatestIndicesOptions = ({ + spaceId = 'default', + riskScoreEntity, +}: { + spaceId?: string; + riskScoreEntity: RiskScoreEntity; +}) => { + const mappings = { + properties: { + [`${riskScoreEntity}.name`]: { + type: 'keyword', + }, + '@timestamp': { + type: 'date', + }, + ingest_timestamp: { + type: 'date', + }, + risk: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + risk_stats: { + properties: { + risk_score: { + type: 'float', + }, + }, + }, + }, + }; + return { + index: getLatestTransformIndex(riskScoreEntity, spaceId), + mappings, + }; +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/ingest_pipelines.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/ingest_pipelines.ts new file mode 100644 index 0000000000000..d3f29323b5268 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/ingest_pipelines.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RiskScoreEntity } from './common'; +import { getLegacyRiskScoreLevelScriptId } from './stored_scripts'; + +export const getIngestPipelineName = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}riskscore_ingest_pipeline_${spaceId}`; + +export const getLegacyIngestPipelineName = (riskScoreEntity: RiskScoreEntity) => + `ml_${riskScoreEntity}riskscore_ingest_pipeline`; + +export const getLegacyRiskScoreIngestPipelineOptions = (riskScoreEntity: RiskScoreEntity) => { + const processors = [ + { + set: { + field: 'ingest_timestamp', + value: '{{_ingest.timestamp}}', + }, + }, + { + fingerprint: { + fields: ['@timestamp', '_id'], + method: 'SHA-256', + target_field: '_id', + }, + }, + { + script: { + id: getLegacyRiskScoreLevelScriptId(riskScoreEntity), + params: { + risk_score: 'risk_stats.risk_score', + }, + }, + }, + ]; + return { + name: getLegacyIngestPipelineName(riskScoreEntity), + processors, + }; +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/saved_objects.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/saved_objects.ts new file mode 100644 index 0000000000000..47177b257ae93 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/saved_objects.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RiskScoreEntity } from './common'; + +const HOST_RISK_SCORE = 'Host Risk Score'; +const USER_RISK_SCORE = 'User Risk Score'; + +const getRiskScore = (riskScoreEntity: RiskScoreEntity) => + riskScoreEntity === RiskScoreEntity.user ? USER_RISK_SCORE : HOST_RISK_SCORE; + +export const getRiskScoreTagName = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `${getRiskScore(riskScoreEntity)} ${spaceId}`; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/stored_scripts.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/stored_scripts.ts new file mode 100644 index 0000000000000..61ee6ff430c9b --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/stored_scripts.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RiskScoreEntity } from './common'; + +export const getRiskScoreLevelScriptId = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}riskscore_levels_script_${spaceId}`; +export const getRiskScoreInitScriptId = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}riskscore_init_script_${spaceId}`; +export const getRiskScoreMapScriptId = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}riskscore_map_script_${spaceId}`; +export const getRiskScoreReduceScriptId = (riskScoreEntity: RiskScoreEntity, spaceId = 'default') => + `ml_${riskScoreEntity}riskscore_reduce_script_${spaceId}`; + +export const getLegacyRiskScoreLevelScriptId = (riskScoreEntity: RiskScoreEntity) => + `ml_${riskScoreEntity}riskscore_levels_script`; +export const getLegacyRiskScoreInitScriptId = (riskScoreEntity: RiskScoreEntity) => + `ml_${riskScoreEntity}riskscore_init_script`; +export const getLegacyRiskScoreMapScriptId = (riskScoreEntity: RiskScoreEntity) => + `ml_${riskScoreEntity}riskscore_map_script`; +export const getLegacyRiskScoreReduceScriptId = (riskScoreEntity: RiskScoreEntity) => + `ml_${riskScoreEntity}riskscore_reduce_script`; + +export const getLegacyRiskHostCreateLevelScriptOptions = (stringifyScript?: boolean) => { + const source = + "double risk_score = (def)ctx.getByPath(params.risk_score);\nif (risk_score < 20) {\n ctx['risk'] = 'Unknown'\n}\nelse if (risk_score >= 20 && risk_score < 40) {\n ctx['risk'] = 'Low'\n}\nelse if (risk_score >= 40 && risk_score < 70) {\n ctx['risk'] = 'Moderate'\n}\nelse if (risk_score >= 70 && risk_score < 90) {\n ctx['risk'] = 'High'\n}\nelse if (risk_score >= 90) {\n ctx['risk'] = 'Critical'\n}"; + return { + id: getLegacyRiskScoreLevelScriptId(RiskScoreEntity.host), + script: { + lang: 'painless', + source: stringifyScript ? JSON.stringify(source) : source, + }, + }; +}; + +export const getLegacyRiskHostCreateInitScriptOptions = (stringifyScript?: boolean) => { + const source = + 'state.rule_risk_stats = new HashMap();\nstate.host_variant_set = false;\nstate.host_variant = new String();\nstate.tactic_ids = new HashSet();'; + return { + id: getLegacyRiskScoreInitScriptId(RiskScoreEntity.host), + script: { + lang: 'painless', + source: stringifyScript ? JSON.stringify(source) : source, + }, + }; +}; + +export const getLegacyRiskHostCreateMapScriptOptions = (stringifyScript?: boolean) => { + const source = + '// Get the host variant\nif (state.host_variant_set == false) {\n if (doc.containsKey("host.os.full") && doc["host.os.full"].size() != 0) {\n state.host_variant = doc["host.os.full"].value;\n state.host_variant_set = true;\n }\n}\n// Aggregate all the tactics seen on the host\nif (doc.containsKey("signal.rule.threat.tactic.id") && doc["signal.rule.threat.tactic.id"].size() != 0) {\n state.tactic_ids.add(doc["signal.rule.threat.tactic.id"].value);\n}\n// Get running sum of time-decayed risk score per rule name per shard\nString rule_name = doc["signal.rule.name"].value;\ndef stats = state.rule_risk_stats.getOrDefault(rule_name, [0.0,"",false]);\nint time_diff = (int)((System.currentTimeMillis() - doc["@timestamp"].value.toInstant().toEpochMilli()) / (1000.0 * 60.0 * 60.0));\ndouble risk_derate = Math.min(1, Math.exp((params.lookback_time - time_diff) / params.time_decay_constant));\nstats[0] = Math.max(stats[0], doc["signal.rule.risk_score"].value * risk_derate);\nif (stats[2] == false) {\n stats[1] = doc["kibana.alert.rule.uuid"].value;\n stats[2] = true;\n}\nstate.rule_risk_stats.put(rule_name, stats);'; + return { + id: getLegacyRiskScoreMapScriptId(RiskScoreEntity.host), + script: { + lang: 'painless', + source: stringifyScript ? JSON.stringify(source) : source, + }, + }; +}; + +export const getLegacyRiskHostCreateReduceScriptOptions = (stringifyScript?: boolean) => { + const source = + '// Consolidating time decayed risks and tactics from across all shards\nMap total_risk_stats = new HashMap();\nString host_variant = new String();\ndef tactic_ids = new HashSet();\nfor (state in states) {\n for (key in state.rule_risk_stats.keySet()) {\n def rule_stats = state.rule_risk_stats.get(key);\n def stats = total_risk_stats.getOrDefault(key, [0.0,"",false]);\n stats[0] = Math.max(stats[0], rule_stats[0]);\n if (stats[2] == false) {\n stats[1] = rule_stats[1];\n stats[2] = true;\n } \n total_risk_stats.put(key, stats);\n }\n if (host_variant.length() == 0) {\n host_variant = state.host_variant;\n }\n tactic_ids.addAll(state.tactic_ids);\n}\n// Consolidating individual rule risks and arranging them in decreasing order\nList risks = new ArrayList();\nfor (key in total_risk_stats.keySet()) {\n risks.add(total_risk_stats[key][0])\n}\nCollections.sort(risks, Collections.reverseOrder());\n// Calculating total host risk score\ndouble total_risk = 0.0;\ndouble risk_cap = params.max_risk * params.zeta_constant;\nfor (int i=0;i= 40 && total_norm_risk < 50) {\n total_norm_risk = 85 + (total_norm_risk - 40);\n}\nelse {\n total_norm_risk = 95 + (total_norm_risk - 50) / 10;\n}\n// Calculating multipliers to the host risk score\ndouble risk_multiplier = 1.0;\nList multipliers = new ArrayList();\n// Add a multiplier if host is a server\nif (host_variant.toLowerCase().contains("server")) {\n risk_multiplier *= params.server_multiplier;\n multipliers.add("Host is a server");\n}\n// Add multipliers based on number and diversity of tactics seen on the host\nfor (String tactic : tactic_ids) {\n multipliers.add("Tactic "+tactic);\n risk_multiplier *= 1 + params.tactic_base_multiplier * params.tactic_weights.getOrDefault(tactic, 0);\n}\n// Calculating final risk\ndouble final_risk = total_norm_risk;\nif (risk_multiplier > 1.0) {\n double prior_odds = (total_norm_risk) / (100 - total_norm_risk);\n double updated_odds = prior_odds * risk_multiplier; \n final_risk = 100 * updated_odds / (1 + updated_odds);\n}\n// Adding additional metadata\nList rule_stats = new ArrayList();\nfor (key in total_risk_stats.keySet()) {\n Map temp = new HashMap();\n temp["rule_name"] = key;\n temp["rule_risk"] = total_risk_stats[key][0];\n temp["rule_id"] = total_risk_stats[key][1];\n rule_stats.add(temp);\n}\n\nreturn ["calculated_score_norm": final_risk, "rule_risks": rule_stats, "multipliers": multipliers];'; + return { + id: getLegacyRiskScoreReduceScriptId(RiskScoreEntity.host), + script: { + lang: 'painless', + source: stringifyScript ? JSON.stringify(source) : source, + }, + }; +}; + +export const getLegacyRiskUserCreateLevelScriptOptions = () => { + const source = + "double risk_score = (def)ctx.getByPath(params.risk_score);\nif (risk_score < 20) {\n ctx['risk'] = 'Unknown'\n}\nelse if (risk_score >= 20 && risk_score < 40) {\n ctx['risk'] = 'Low'\n}\nelse if (risk_score >= 40 && risk_score < 70) {\n ctx['risk'] = 'Moderate'\n}\nelse if (risk_score >= 70 && risk_score < 90) {\n ctx['risk'] = 'High'\n}\nelse if (risk_score >= 90) {\n ctx['risk'] = 'Critical'\n}"; + return { + id: getLegacyRiskScoreLevelScriptId(RiskScoreEntity.user), + script: { + lang: 'painless', + source, + }, + }; +}; + +export const getLegacyRiskUserCreateMapScriptOptions = () => { + const source = + '// Get running sum of risk score per rule name per shard\\\\\nString rule_name = doc["signal.rule.name"].value;\ndef stats = state.rule_risk_stats.getOrDefault(rule_name, 0.0);\nstats = doc["signal.rule.risk_score"].value;\nstate.rule_risk_stats.put(rule_name, stats);'; + return { + id: getLegacyRiskScoreMapScriptId(RiskScoreEntity.user), + script: { + lang: 'painless', + source, + }, + }; +}; + +export const getLegacyRiskUserCreateReduceScriptOptions = () => { + const source = + '// Consolidating time decayed risks from across all shards\nMap total_risk_stats = new HashMap();\nfor (state in states) {\n for (key in state.rule_risk_stats.keySet()) {\n def rule_stats = state.rule_risk_stats.get(key);\n def stats = total_risk_stats.getOrDefault(key, 0.0);\n stats = rule_stats;\n total_risk_stats.put(key, stats);\n }\n}\n// Consolidating individual rule risks and arranging them in decreasing order\nList risks = new ArrayList();\nfor (key in total_risk_stats.keySet()) {\n risks.add(total_risk_stats[key])\n}\nCollections.sort(risks, Collections.reverseOrder());\n// Calculating total risk and normalizing it to a range\ndouble total_risk = 0.0;\ndouble risk_cap = params.max_risk * params.zeta_constant;\nfor (int i=0;i= 40 && total_norm_risk < 50) {\n total_norm_risk = 85 + (total_norm_risk - 40);\n}\nelse {\n total_norm_risk = 95 + (total_norm_risk - 50) / 10;\n}\n\nList rule_stats = new ArrayList();\nfor (key in total_risk_stats.keySet()) {\n Map temp = new HashMap();\n temp["rule_name"] = key;\n temp["rule_risk"] = total_risk_stats[key];\n rule_stats.add(temp);\n}\n\nreturn ["risk_score": total_norm_risk, "rule_risks": rule_stats];'; + return { + id: getLegacyRiskScoreReduceScriptId(RiskScoreEntity.user), + script: { + lang: 'painless', + source, + }, + }; +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/risk_scores/transforms.ts b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/transforms.ts new file mode 100644 index 0000000000000..aa41facee24e9 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/risk_scores/transforms.ts @@ -0,0 +1,307 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TRANSFORMS_URL } from '../../urls/risk_score'; +import { RiskScoreEntity } from './common'; +import { getLatestTransformIndex, getPivotTransformIndex } from './indices'; +import { getLegacyIngestPipelineName } from './ingest_pipelines'; +import { + getLegacyRiskScoreInitScriptId, + getLegacyRiskScoreMapScriptId, + getLegacyRiskScoreReduceScriptId, +} from './stored_scripts'; + +const DEFAULT_ALERTS_INDEX = '.alerts-security.alerts' as const; +export const getAlertsIndex = (spaceId = 'default') => `${DEFAULT_ALERTS_INDEX}-${spaceId}`; + +export const getRiskScorePivotTransformId = ( + riskScoreEntity: RiskScoreEntity, + spaceId = 'default' +) => `ml_${riskScoreEntity}riskscore_pivot_transform_${spaceId}`; + +export const getRiskScoreLatestTransformId = ( + riskScoreEntity: RiskScoreEntity, + spaceId = 'default' +) => `ml_${riskScoreEntity}riskscore_latest_transform_${spaceId}`; + +export const getTransformState = (transformId: string) => { + return cy.request<{ transforms: Array<{ id: string; state: string }>; count: number }>({ + method: 'get', + url: `${TRANSFORMS_URL}/transforms/${transformId}/_stats`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + }); +}; + +export const startTransforms = (transformIds: string[]) => { + return cy.request({ + method: 'post', + url: `${TRANSFORMS_URL}/start_transforms`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + body: transformIds.map((id) => ({ + id, + })), + }); +}; + +const stopTransform = (state: { + transforms: Array<{ id: string; state: string }>; + count: number; +}) => { + return cy.request({ + method: 'post', + url: `${TRANSFORMS_URL}/stop_transforms`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + body: + state != null && state.transforms.length > 0 + ? [ + { + id: state.transforms[0].id, + state: state.transforms[0].state, + }, + ] + : ([] as Array<{ id: string; state: string }>), + }); +}; + +export const createTransform = (transformId: string, options: string | Record) => { + return cy.request({ + method: 'put', + url: `${TRANSFORMS_URL}/transforms/${transformId}`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + body: options, + }); +}; + +export const deleteTransform = (transformId: string) => { + return cy.request({ + method: 'post', + url: `${TRANSFORMS_URL}/delete_transforms`, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + failOnStatusCode: false, + body: { + transformsInfo: [ + { + id: transformId, + state: 'stopped', + }, + ], + deleteDestIndex: true, + deleteDestDataView: true, + forceDelete: false, + }, + }); +}; + +export const deleteTransforms = (transformIds: string[]) => { + const deleteSingleTransform = (transformId: string) => + getTransformState(transformId) + .then(({ body: result }) => { + return stopTransform(result); + }) + .then(() => { + deleteTransform(transformId); + }); + + transformIds.map((transformId) => deleteSingleTransform(transformId)); +}; + +export const getCreateLegacyMLHostPivotTransformOptions = ({ + spaceId = 'default', +}: { + spaceId?: string; +}) => { + const options = { + dest: { + index: getPivotTransformIndex(RiskScoreEntity.host, spaceId), + pipeline: getLegacyIngestPipelineName(RiskScoreEntity.host), + }, + frequency: '1h', + pivot: { + aggregations: { + '@timestamp': { + max: { + field: '@timestamp', + }, + }, + risk_stats: { + scripted_metric: { + combine_script: 'return state', + init_script: { + id: getLegacyRiskScoreInitScriptId(RiskScoreEntity.host), + }, + map_script: { + id: getLegacyRiskScoreMapScriptId(RiskScoreEntity.host), + }, + params: { + lookback_time: 72, + max_risk: 100, + p: 1.5, + server_multiplier: 1.5, + tactic_base_multiplier: 0.25, + tactic_weights: { + TA0001: 1, + TA0002: 2, + TA0003: 3, + TA0004: 4, + TA0005: 4, + TA0006: 4, + TA0007: 4, + TA0008: 5, + TA0009: 6, + TA0010: 7, + TA0011: 6, + TA0040: 8, + TA0042: 1, + TA0043: 1, + }, + time_decay_constant: 6, + zeta_constant: 2.612, + }, + reduce_script: { + id: getLegacyRiskScoreReduceScriptId(RiskScoreEntity.host), + }, + }, + }, + }, + group_by: { + [`${RiskScoreEntity.host}.name`]: { + terms: { + field: `${RiskScoreEntity.host}.name`, + }, + }, + }, + }, + source: { + index: [getAlertsIndex(spaceId)], + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: 'now-5d', + }, + }, + }, + ], + }, + }, + }, + sync: { + time: { + delay: '120s', + field: '@timestamp', + }, + }, + }; + + return options; +}; + +export const getCreateLegacyMLUserPivotTransformOptions = ({ + spaceId = 'default', +}: { + spaceId?: string; +}) => { + const options = { + dest: { + index: getPivotTransformIndex(RiskScoreEntity.user, spaceId), + pipeline: getLegacyIngestPipelineName(RiskScoreEntity.user), + }, + frequency: '1h', + pivot: { + aggregations: { + '@timestamp': { + max: { + field: '@timestamp', + }, + }, + risk_stats: { + scripted_metric: { + combine_script: 'return state', + init_script: 'state.rule_risk_stats = new HashMap();', + map_script: { + id: getLegacyRiskScoreMapScriptId(RiskScoreEntity.user), + }, + params: { + max_risk: 100, + p: 1.5, + zeta_constant: 2.612, + }, + reduce_script: { + id: getLegacyRiskScoreReduceScriptId(RiskScoreEntity.user), + }, + }, + }, + }, + group_by: { + 'user.name': { + terms: { + field: 'user.name', + }, + }, + }, + }, + source: { + index: [getAlertsIndex(spaceId)], + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: 'now-90d', + }, + }, + }, + { + match: { + 'signal.status': 'open', + }, + }, + ], + }, + }, + }, + sync: { + time: { + delay: '120s', + field: '@timestamp', + }, + }, + }; + return options; +}; + +export const getCreateLegacyLatestTransformOptions = ({ + spaceId = 'default', + riskScoreEntity, +}: { + spaceId?: string; + riskScoreEntity: RiskScoreEntity; +}) => { + const options = { + dest: { + index: getLatestTransformIndex(riskScoreEntity, spaceId), + }, + frequency: '1h', + latest: { + sort: '@timestamp', + unique_key: [`${riskScoreEntity}.name`], + }, + source: { + index: [getPivotTransformIndex(riskScoreEntity, spaceId)], + }, + sync: { + time: { + delay: '2s', + field: 'ingest_timestamp', + }, + }, + }; + return options; +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index a83e1157e98d7..f5b1a2c794cbc 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -87,24 +87,36 @@ import { closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; export const hostExistsQuery = 'host.name: *'; -export const addDescriptionToTimeline = (description: string) => { - cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); +export const addDescriptionToTimeline = ( + description: string, + modalAlreadyOpen: boolean = false +) => { + if (!modalAlreadyOpen) { + cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); + } cy.get(TIMELINE_DESCRIPTION_INPUT).type(description); cy.get(TIMELINE_DESCRIPTION_INPUT).invoke('val').should('equal', description); cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click(); cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); }; -export const addNameToTimeline = (name: string) => { - cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); +export const addNameToTimeline = (name: string, modalAlreadyOpen: boolean = false) => { + if (!modalAlreadyOpen) { + cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); + } cy.get(TIMELINE_TITLE_INPUT).type(`${name}{enter}`); cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', name); cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click(); cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); }; -export const addNameAndDescriptionToTimeline = (timeline: Timeline) => { - cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); +export const addNameAndDescriptionToTimeline = ( + timeline: Timeline, + modalAlreadyOpen: boolean = false +) => { + if (!modalAlreadyOpen) { + cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click(); + } cy.get(TIMELINE_TITLE_INPUT).type(`${timeline.title}{enter}`); cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', timeline.title); cy.get(TIMELINE_DESCRIPTION_INPUT).type(timeline.description); diff --git a/x-pack/plugins/security_solution/cypress/urls/risk_score.ts b/x-pack/plugins/security_solution/cypress/urls/risk_score.ts new file mode 100644 index 0000000000000..6bd821a1c4f21 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/urls/risk_score.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const RISK_SCORE_URL = `/internal/risk_score` as const; +export const INDICES_URL = `/internal/risk_score/indices` as const; +export const INGEST_PIPELINES_URL = `/api/ingest_pipelines` as const; +export const TRANSFORMS_URL = `/api/transform` as const; +export const STORED_SCRIPTS_URL = `/internal/risk_score/stored_scripts` as const; +export const RISK_SCORE_SAVED_OBJECTS_URL = + `/internal/risk_score/prebuilt_content/saved_objects` as const; +export const SAVED_OBJECTS_URL = `/api/saved_objects` as const; diff --git a/x-pack/plugins/security_solution/public/cases/links.ts b/x-pack/plugins/security_solution/public/cases/links.ts index 679c355c819ed..43fdabcec1cca 100644 --- a/x-pack/plugins/security_solution/public/cases/links.ts +++ b/x-pack/plugins/security_solution/public/cases/links.ts @@ -26,12 +26,10 @@ export const getCasesLinkItems = (): LinkItem => { capabilities: [`${CASES_FEATURE_ID}.${UPDATE_CASES_CAPABILITY}`], licenseType: 'gold', sideNavDisabled: true, - hideTimeline: true, }, [SecurityPageName.caseCreate]: { capabilities: [`${CASES_FEATURE_ID}.${CREATE_CASES_CAPABILITY}`], sideNavDisabled: true, - hideTimeline: true, }, }, }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts new file mode 100644 index 0000000000000..84c37a58fcf4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useMemo } from 'react'; +import { useDispatch } from 'react-redux'; +import type { AppLeaveHandler } from '@kbn/core-application-browser'; +import { useHistory } from 'react-router-dom'; +import { useShowTimelineForGivenPath } from '../../utils/timeline/use_show_timeline_for_path'; +import type { TimelineId } from '../../../../common/types'; +import { TimelineStatus, TimelineTabs } from '../../../../common/types'; +import { useKibana } from '../../lib/kibana'; +import { useDeepEqualSelector } from '../use_selector'; +import { APP_ID, APP_PATH } from '../../../../common/constants'; +import { getTimelineShowStatusByIdSelector } from '../../../timelines/components/flyout/selectors'; +import { timelineActions } from '../../../timelines/store/timeline'; +import { + UNSAVED_TIMELINE_SAVE_PROMPT, + UNSAVED_TIMELINE_SAVE_PROMPT_TITLE, +} from '../../translations'; + +// Issue with history.block +// https://github.com/elastic/kibana/issues/132597 + +export const useTimelineSavePrompt = ( + timelineId: TimelineId, + onAppLeave: (handler: AppLeaveHandler) => void +) => { + const dispatch = useDispatch(); + const { overlays, application } = useKibana().services; + const getIsTimelineVisible = useShowTimelineForGivenPath(); + const history = useHistory(); + + const getTimelineShowStatus = useMemo(() => getTimelineShowStatusByIdSelector(), []); + const { status: timelineStatus, updated } = useDeepEqualSelector((state) => + getTimelineShowStatus(state, timelineId) + ); + + const showSaveTimelineModal = useCallback(() => { + dispatch(timelineActions.showTimeline({ id: timelineId, show: true })); + dispatch( + timelineActions.setActiveTabTimeline({ + id: timelineId, + activeTab: TimelineTabs.query, + }) + ); + dispatch( + timelineActions.toggleModalSaveTimeline({ + id: timelineId, + showModalSaveTimeline: true, + }) + ); + }, [dispatch, timelineId]); + + useEffect(() => { + const unblock = history.block((location) => { + const relativePath = location.pathname.replace(APP_PATH, ''); + async function confirmSaveTimeline() { + const confirmRes = await overlays?.openConfirm(UNSAVED_TIMELINE_SAVE_PROMPT, { + title: UNSAVED_TIMELINE_SAVE_PROMPT_TITLE, + 'data-test-subj': 'appLeaveConfirmModal', + }); + + if (confirmRes) { + unblock(); + + application.navigateToUrl(location.pathname + location.hash + location.search, { + state: location.state, + }); + } else { + showSaveTimelineModal(); + } + } + + if ( + !getIsTimelineVisible(relativePath) && + timelineStatus === TimelineStatus.draft && + updated != null + ) { + confirmSaveTimeline(); + } else { + return; + } + return false; + }); + + return () => { + unblock(); + }; + }, [ + history, + application, + overlays, + showSaveTimelineModal, + getIsTimelineVisible, + timelineStatus, + updated, + ]); + + useEffect(() => { + onAppLeave((actions, nextAppId) => { + // Confirm when the user has made any changes to a timeline + if ( + !(nextAppId ?? '').includes(APP_ID) && + timelineStatus === TimelineStatus.draft && + updated != null + ) { + return actions.confirm( + UNSAVED_TIMELINE_SAVE_PROMPT, + UNSAVED_TIMELINE_SAVE_PROMPT_TITLE, + showSaveTimelineModal + ); + } else { + return actions.default(); + } + }); + }); +}; diff --git a/x-pack/plugins/security_solution/public/common/translations.ts b/x-pack/plugins/security_solution/public/common/translations.ts index 0595a9fcb1867..18661e6580013 100644 --- a/x-pack/plugins/security_solution/public/common/translations.ts +++ b/x-pack/plugins/security_solution/public/common/translations.ts @@ -36,3 +36,17 @@ export const UPGRADE_ENDPOINT_FOR_RESPONDER = i18n.translate( 'The current version of the Agent does not support this feature. Upgrade your Agent through Fleet to use this feature and new response actions such as killing and suspending processes.', } ); + +export const UNSAVED_TIMELINE_SAVE_PROMPT = i18n.translate( + 'xpack.securitySolution.timeline.unsavedWorkMessage', + { + defaultMessage: 'Leave Timeline with unsaved work?', + } +); + +export const UNSAVED_TIMELINE_SAVE_PROMPT_TITLE = i18n.translate( + 'xpack.securitySolution.timeline.unsavedWorkTitle', + { + defaultMessage: 'Unsaved changes', + } +); diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx index 63eecdc084a4c..c6554b80c2626 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx @@ -6,55 +6,16 @@ */ import { useMemo } from 'react'; -import { matchPath, useLocation } from 'react-router-dom'; - -import { getLinksWithHiddenTimeline } from '../../links'; -import { useIsGroupedNavigationEnabled } from '../../components/navigation/helpers'; -import { SourcererScopeName } from '../../store/sourcerer/model'; -import { useSourcererDataView } from '../../containers/sourcerer'; -import { useKibana } from '../../lib/kibana'; - -const DEPRECATED_HIDDEN_TIMELINE_ROUTES: readonly string[] = [ - `/cases/configure`, - '/administration', - '/rules/create', - '/get_started', - '/explore', - '/dashboards', - '/manage', - '/cloud_security_posture*', -]; - -const isTimelinePathVisible = ( - currentPath: string, - isGroupedNavigationEnabled: boolean -): boolean => { - const groupLinksWithHiddenTimelinePaths = getLinksWithHiddenTimeline().map((l) => l.path); - - const hiddenTimelineRoutes = isGroupedNavigationEnabled - ? groupLinksWithHiddenTimelinePaths - : DEPRECATED_HIDDEN_TIMELINE_ROUTES; - - return !hiddenTimelineRoutes.find((route) => matchPath(currentPath, route)); -}; +import { useLocation } from 'react-router-dom'; +import { useShowTimelineForGivenPath } from './use_show_timeline_for_path'; export const useShowTimeline = () => { - const isGroupedNavigationEnabled = useIsGroupedNavigationEnabled(); const { pathname } = useLocation(); - const { indicesExist, dataViewId } = useSourcererDataView(SourcererScopeName.timeline); - const userHasSecuritySolutionVisible = useKibana().services.application.capabilities.siem.show; + const getIsTimelineVisible = useShowTimelineForGivenPath(); - const isTimelineAllowed = useMemo( - () => userHasSecuritySolutionVisible && (indicesExist || dataViewId === null), - [indicesExist, dataViewId, userHasSecuritySolutionVisible] + const showTimeline = useMemo( + () => getIsTimelineVisible(pathname), + [pathname, getIsTimelineVisible] ); - - const showTimeline = useMemo(() => { - if (!isTimelineAllowed) { - return false; - } - return isTimelinePathVisible(pathname, isGroupedNavigationEnabled); - }, [isTimelineAllowed, pathname, isGroupedNavigationEnabled]); - return [showTimeline]; }; diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline_for_path.ts b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline_for_path.ts new file mode 100644 index 0000000000000..a06f6b5ab0e4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline_for_path.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useMemo } from 'react'; +import { matchPath } from 'react-router-dom'; + +import { getLinksWithHiddenTimeline } from '../../links'; +import { useIsGroupedNavigationEnabled } from '../../components/navigation/helpers'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererDataView } from '../../containers/sourcerer'; +import { useKibana } from '../../lib/kibana'; + +const DEPRECATED_HIDDEN_TIMELINE_ROUTES: readonly string[] = [ + `/cases/configure`, + '/administration', + '/rules/create', + '/get_started', + '/explore', + '/dashboards', + '/manage', + '/cloud_security_posture*', +]; + +const isTimelinePathVisible = ( + currentPath: string, + isGroupedNavigationEnabled: boolean +): boolean => { + const groupLinksWithHiddenTimelinePaths = getLinksWithHiddenTimeline().map((l) => l.path); + + const hiddenTimelineRoutes = isGroupedNavigationEnabled + ? groupLinksWithHiddenTimelinePaths + : DEPRECATED_HIDDEN_TIMELINE_ROUTES; + + return !hiddenTimelineRoutes.find((route) => matchPath(currentPath, route)); +}; + +export const useShowTimelineForGivenPath = () => { + const isGroupedNavigationEnabled = useIsGroupedNavigationEnabled(); + + const { indicesExist, dataViewId } = useSourcererDataView(SourcererScopeName.timeline); + const userHasSecuritySolutionVisible = useKibana().services.application.capabilities.siem.show; + + const isTimelineAllowed = useMemo( + () => userHasSecuritySolutionVisible && (indicesExist || dataViewId === null), + [indicesExist, dataViewId, userHasSecuritySolutionVisible] + ); + + const getIsTimelineVisible = useCallback( + (pathname: string) => { + if (!isTimelineAllowed) { + return false; + } + return isTimelinePathVisible(pathname, isGroupedNavigationEnabled); + }, + [isTimelineAllowed, isGroupedNavigationEnabled] + ); + + return getIsTimelineVisible; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index 99fede7ca1c72..c7b7050f23ffe 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -30,12 +30,15 @@ import { EuiFlexGroup, } from '@elastic/eui'; import type { + CreateExceptionListItemSchema, + ExceptionListItemSchema, ExceptionListType, OsTypeArray, - ExceptionListItemSchema, - CreateExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import type { ExceptionsBuilderExceptionItem } from '@kbn/securitysolution-list-utils'; +import type { + ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, +} from '@kbn/securitysolution-list-utils'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { DataViewBase } from '@kbn/es-query'; import { useRuleIndices } from '../../../../detections/containers/detection_engine/rules/use_rule_indices'; @@ -146,7 +149,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ const [shouldBulkCloseAlert, setShouldBulkCloseAlert] = useState(false); const [shouldDisableBulkClose, setShouldDisableBulkClose] = useState(false); const [exceptionItemsToAdd, setExceptionItemsToAdd] = useState< - Array + ExceptionsBuilderReturnExceptionItem[] >([]); const [fetchOrCreateListError, setFetchOrCreateListError] = useState(null); const { addError, addSuccess, addWarning } = useAppToasts(); @@ -190,7 +193,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ exceptionItems, errorExists, }: { - exceptionItems: Array; + exceptionItems: ExceptionsBuilderReturnExceptionItem[]; errorExists: boolean; }) => { setExceptionItemsToAdd(exceptionItems); @@ -337,10 +340,8 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ return hasAlertData ? retrieveAlertOsTypes(alertData) : selectedOs ? [...selectedOs] : []; }, [hasAlertData, alertData, selectedOs]); - const enrichExceptionItems = useCallback((): Array< - ExceptionListItemSchema | CreateExceptionListItemSchema - > => { - let enriched: Array = []; + const enrichExceptionItems = useCallback((): ExceptionsBuilderReturnExceptionItem[] => { + let enriched: ExceptionsBuilderReturnExceptionItem[] = []; enriched = comment !== '' ? enrichNewExceptionItemsWithComments(exceptionItemsToAdd, [{ comment }]) @@ -359,7 +360,9 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ shouldBulkCloseAlert && signalIndexName != null ? [signalIndexName] : undefined; addOrUpdateExceptionItems( maybeRule?.rule_id ?? '', - enrichExceptionItems(), + // This is being rewritten in https://github.com/elastic/kibana/pull/140643 + // As of now, flyout cannot yet create item of type CreateRuleExceptionListItemSchema + enrichExceptionItems() as Array, alertIdToClose, bulkCloseIndex ); 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 index fdffa134dd96f..44275ff3f3116 100644 --- 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 @@ -69,7 +69,9 @@ const ExceptionItemsViewerComponent: React.FC = ({ disableActions={disableActions} exceptionItem={exception} listType={listType} - ruleReferences={ruleReferences != null ? ruleReferences[exception.list_id] : []} + ruleReferences={ + ruleReferences != null ? ruleReferences[exception.list_id] : null + } onDeleteException={onDeleteException} onEditException={onEditExceptionItem} dataTestSubj="exceptionItemsViewerItem" 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 index ffe07bb2c5dfb..97de081738ffd 100644 --- 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 @@ -162,8 +162,17 @@ const ExceptionsViewerComponent = ({ [dispatch] ); - const [isLoadingReferences, isFetchReferencesError, allReferences] = - useFindExceptionListReferences(exceptionListsToQuery); + const [isLoadingReferences, isFetchReferencesError, allReferences, fetchReferences] = + useFindExceptionListReferences(); + + useEffect(() => { + if (fetchReferences != null && exceptionListsToQuery.length) { + const listsToQuery = exceptionListsToQuery.map( + ({ id, list_id: listId, namespace_type: namespaceType }) => ({ id, listId, namespaceType }) + ); + fetchReferences(listsToQuery); + } + }, [exceptionListsToQuery, fetchReferences]); useEffect(() => { if (isFetchReferencesError) { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx index 0ba2301b00261..85a59f06281f1 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx @@ -38,6 +38,7 @@ import type { import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { DataViewBase } from '@kbn/es-query'; +import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; import { useRuleIndices } from '../../../../detections/containers/detection_engine/rules/use_rule_indices'; import { hasEqlSequenceQuery, isEqlRule } from '../../../../../common/detection_engine/utils'; import { useFetchIndex } from '../../../../common/containers/source'; @@ -127,7 +128,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ const [shouldBulkCloseAlert, setShouldBulkCloseAlert] = useState(false); const [shouldDisableBulkClose, setShouldDisableBulkClose] = useState(false); const [exceptionItemsToAdd, setExceptionItemsToAdd] = useState< - Array + ExceptionsBuilderReturnExceptionItem[] >([]); const { addError, addSuccess } = useAppToasts(); const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); @@ -254,7 +255,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ exceptionItems, errorExists, }: { - exceptionItems: Array; + exceptionItems: ExceptionsBuilderReturnExceptionItem[]; errorExists: boolean; }) => { setExceptionItemsToAdd(exceptionItems); @@ -279,7 +280,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ const enrichExceptionItems = useCallback(() => { const [exceptionItemToEdit] = exceptionItemsToAdd; - let enriched: Array = [ + let enriched: ExceptionsBuilderReturnExceptionItem[] = [ { ...enrichExistingExceptionItemWithComments(exceptionItemToEdit, [ ...exceptionItem.comments, @@ -299,7 +300,9 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined; addOrUpdateExceptionItems( maybeRule?.rule_id ?? '', - enrichExceptionItems(), + // This is being rewritten in https://github.com/elastic/kibana/pull/140643 + // As of now, flyout cannot yet create item of type CreateRuleExceptionListItemSchema + enrichExceptionItems() as Array, undefined, bulkCloseIndex ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx index 5219f5d72d847..f80372dc11283 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.test.tsx @@ -13,6 +13,7 @@ import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { TestProviders } from '../../../../common/mock'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; jest.mock('../../../../common/lib/kibana'); @@ -28,27 +29,30 @@ describe('ExceptionItemCard', () => { onEditException={jest.fn()} exceptionItem={exceptionItem} listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} dataTestSubj="item" /> @@ -74,27 +78,30 @@ describe('ExceptionItemCard', () => { exceptionItem={exceptionItem} dataTestSubj="item" listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} /> ); @@ -119,27 +126,30 @@ describe('ExceptionItemCard', () => { exceptionItem={exceptionItem} dataTestSubj="item" listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} /> ); @@ -160,27 +170,30 @@ describe('ExceptionItemCard', () => { exceptionItem={exceptionItem} dataTestSubj="item" listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} /> ); @@ -210,27 +223,30 @@ describe('ExceptionItemCard', () => { exceptionItem={exceptionItem} dataTestSubj="item" listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} /> ); @@ -263,27 +279,30 @@ describe('ExceptionItemCard', () => { exceptionItem={exceptionItem} dataTestSubj="item" listType={ExceptionListTypeEnum.DETECTION} - ruleReferences={[ - { - exception_lists: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, - ]} + ruleReferences={{ + ...getExceptionListSchemaMock(), + referenced_rules: [ + { + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + }, + ], + }} /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx index cae3136dc6ad4..4bfa09e96486e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/index.tsx @@ -17,14 +17,14 @@ import * as i18n from './translations'; import { ExceptionItemCardHeader } from './header'; import { ExceptionItemCardConditions } from './conditions'; import { ExceptionItemCardMetaInfo } from './meta'; -import type { RuleReferenceSchema } from '../../../../../common/detection_engine/schemas/response'; +import type { ExceptionListRuleReferencesSchema } from '../../../../../common/detection_engine/schemas/response'; import { ExceptionItemCardComments } from './comments'; export interface ExceptionItemProps { exceptionItem: ExceptionListItemSchema; listType: ExceptionListTypeEnum; disableActions: boolean; - ruleReferences: RuleReferenceSchema[]; + ruleReferences: ExceptionListRuleReferencesSchema | null; onDeleteException: (arg: ExceptionListItemIdentifiers) => void; onEditException: (item: ExceptionListItemSchema) => void; dataTestSubj: string; 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 index c755321f5f4ea..8892117f1616e 100644 --- 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 @@ -7,7 +7,9 @@ import React from 'react'; import { mount } from 'enzyme'; + import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { ExceptionItemCardMetaInfo } from './meta'; import { TestProviders } from '../../../../common/mock'; @@ -18,27 +20,30 @@ describe('ExceptionItemCardMetaInfo', () => { @@ -57,27 +62,30 @@ describe('ExceptionItemCardMetaInfo', () => { @@ -96,27 +104,30 @@ describe('ExceptionItemCardMetaInfo', () => { @@ -132,46 +143,49 @@ describe('ExceptionItemCardMetaInfo', () => { 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 index a24526dd04e3a..453e1542bfce8 100644 --- 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 @@ -24,7 +24,7 @@ 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 type { ExceptionListRuleReferencesSchema } 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'; @@ -36,7 +36,7 @@ const StyledFlexItem = styled(EuiFlexItem)` export interface ExceptionItemCardMetaInfoProps { item: ExceptionListItemSchema; - references: RuleReferenceSchema[]; + references: ExceptionListRuleReferencesSchema | null; dataTestSubj: string; } @@ -51,7 +51,7 @@ export const ExceptionItemCardMetaInfo = memo( if (references == null) { return []; } - return references.map((reference) => ( + return references.referenced_rules.map((reference) => ( ( dataTestSubj={`${dataTestSubj}-updatedBy`} /> - - - {i18n.AFFECTED_RULES(references?.length ?? 0)} -
- } - panelPaddingSize="none" - isOpen={isPopoverOpen} - closePopover={onClosePopover} - data-test-subj={`${dataTestSubj}-items`} - > - - - + {references != null && ( + + + {i18n.AFFECTED_RULES(references?.referenced_rules.length ?? 0)} + + } + panelPaddingSize="none" + isOpen={isPopoverOpen} + closePopover={onClosePopover} + data-test-subj={`${dataTestSubj}-items`} + > + + + + )} ); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.test.tsx new file mode 100644 index 0000000000000..d9038b0c3fd04 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.test.tsx @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { getRulesSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; + +import { ExceptionsAddToRulesOrLists } from '.'; + +describe('ExceptionsAddToRulesOrLists', () => { + it('it passes empty array for shared lists attached if no rules passed in', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('ExceptionsAddToListsOptions').prop('sharedLists')).toEqual([]); + expect(wrapper.find('ExceptionsAddToListsOptions').prop('rulesCount')).toEqual(0); + }); + + it('it passes all shared lists attached to a single rule', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('ExceptionsAddToListsOptions').prop('sharedLists')).toEqual([ + { id: '123', list_id: 'my_list', namespace_type: 'single', type: 'detection' }, + ]); + }); + + it('it passes shared lists that are in common if multiple rules exist', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('ExceptionsAddToListsOptions').prop('sharedLists')).toEqual([ + { id: '123', list_id: 'my_list', namespace_type: 'single', type: 'detection' }, + ]); + }); + + it('it passes an empty array for shared lists if multiple rules exist and they have no shared lists in common', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('ExceptionsAddToListsOptions').prop('sharedLists')).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.tsx new file mode 100644 index 0000000000000..aa705891ecfe7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/index.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiTitle, EuiSpacer, EuiPanel } from '@elastic/eui'; +import styled, { css } from 'styled-components'; +import type { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import * as i18n from './translations'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { ExceptionsAddToRulesOptions } from '../add_to_rules_options'; +import { ExceptionsAddToListsOptions } from '../add_to_lists_options'; + +interface ExceptionsAddToRulesOrListsComponentProps { + /* Rules that exception item will be added to, or whose shared lists will be used to populate add to lists option. If none passed in, user is prompted to select what rules to add exception to. */ + rules: Rule[] | null; + selectedRadioOption: string; + /* Is user adding an exception item from the rules bulk actions */ + isBulkAction: boolean; + onListSelectionChange: (lists: ExceptionListSchema[]) => void; + onRuleSelectionChange: (rulesSelectedToAdd: Rule[]) => void; + onRadioChange: (radioId: string) => void; +} + +const SectionHeader = styled(EuiTitle)` + ${() => css` + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + `} +`; + +const ExceptionsAddToRulesOrListsComponent: React.FC = ({ + rules, + isBulkAction, + selectedRadioOption, + onListSelectionChange, + onRuleSelectionChange, + onRadioChange, +}): JSX.Element => { + const isSingleRule = useMemo(() => rules != null && rules.length === 1, [rules]); + + /* + * Determine what shared lists to display as selectable options for adding the exception item to: + * - if dealing with a single rule - show any shared exception lists it has attached + * - if dealing with multiple rules - show only shared exception lists that are common + * across all of the rules + */ + const sharedLists = useMemo(() => { + if (rules == null) return []; + + if (rules.length === 1) + return ( + rules[0].exceptions_list?.filter((list) => list.type === ExceptionListTypeEnum.DETECTION) ?? + [] + ); + + const lists = + rules?.map((rule) => (rule.exceptions_list != null ? rule.exceptions_list : [])) ?? []; + + lists.sort((a, b) => { + return a.length - b.length; + }); + + const shortestArrOfLists = lists.shift(); + + if (shortestArrOfLists == null || !shortestArrOfLists.length) return []; + + return shortestArrOfLists + .filter((exceptionListInfo) => + lists.every((l) => l.some(({ id }) => exceptionListInfo.id === id)) + ) + .filter((list) => list.type === ExceptionListTypeEnum.DETECTION); + }, [rules]); + const rulesCount = useMemo(() => (rules != null ? rules.length : 0), [rules]); + + return ( + + +

{i18n.ADD_TO_LISTS_SECTION_TITLE}

+
+ + + +
+ ); +}; + +export const ExceptionsAddToRulesOrLists = React.memo(ExceptionsAddToRulesOrListsComponent); + +ExceptionsAddToRulesOrLists.displayName = 'ExceptionsAddToRulesOrLists'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/translations.ts new file mode 100644 index 0000000000000..0f8b8daeb745f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_exception_to_rule_or_list/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 ADD_TO_LISTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addExceptionToRuleOrList.addToListsLabel', + { + defaultMessage: 'Add to rule or lists', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.test.tsx new file mode 100644 index 0000000000000..4b55522a638e2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { shallow } from 'enzyme'; + +import { ExceptionsAddToListsOptions } from '.'; + +jest.mock('../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'); + +describe('ExceptionsAddToListsOptions', () => { + it('it displays radio option as disabled if there are no "sharedLists"', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="exceptionsAddToListTable"]').exists()).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"]').at(0).props().disabled + ).toBeTruthy(); + }); + + it('it displays lists table if radio is selected', () => { + const wrapper = shallow( + + ); + + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"]').at(0).props().disabled + ).toBeFalsy(); + expect(wrapper.find('[data-test-subj="exceptionsAddToListTable"]').exists()).toBeTruthy(); + }); + + it('it does not display lists table if radio is not selected', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="exceptionsAddToListTable"]').exists()).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.tsx new file mode 100644 index 0000000000000..849c035d8aea4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/index.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText, EuiRadio, EuiFlexItem, EuiFlexGroup, EuiIconTip } from '@elastic/eui'; + +import type { ExceptionListSchema, ListArray } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from './translations'; +import { ExceptionsAddToListsTable } from '../add_to_lists_table'; + +interface ExceptionsAddToListsOptionsComponentProps { + rulesCount: number; + selectedRadioOption: string; + sharedLists: ListArray; + onListsSelectionChange: (listsSelectedToAdd: ExceptionListSchema[]) => void; + onRadioChange: (option: string) => void; +} + +const ExceptionsAddToListsOptionsComponent: React.FC = ({ + rulesCount, + selectedRadioOption, + sharedLists, + onListsSelectionChange, + onRadioChange, +}): JSX.Element => { + return ( + <> + + + {i18n.ADD_TO_LISTS_OPTION} + + + + + + + } + checked={selectedRadioOption === 'add_to_lists'} + disabled={sharedLists.length === 0 && rulesCount > 0} + onChange={() => onRadioChange('add_to_lists')} + data-test-subj="addToListsRadioOption" + /> + + {selectedRadioOption === 'add_to_lists' && (sharedLists.length > 0 || rulesCount === 0) && ( + + )} + + ); +}; + +export const ExceptionsAddToListsOptions = React.memo(ExceptionsAddToListsOptionsComponent); + +ExceptionsAddToListsOptions.displayName = 'ExceptionsAddToListsOptions'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/translations.ts new file mode 100644 index 0000000000000..cf7b51dbac76e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_options/translations.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 { i18n } from '@kbn/i18n'; + +export const ADD_TO_LISTS_OPTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsOptions.addToListsOptionLabel', + { + defaultMessage: 'Add to shared exception lists', + } +); + +export const ADD_TO_LISTS_OPTION_TOOLTIP = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsOptions.addToListsTooltip', + { + defaultMessage: + 'Shared exception list is a group of exceptions. Select this option if you’d like to add this exception to shared exception lists.', + } +); + +export const ADD_TO_LISTS_OPTION_DISABLED_TOOLTIP = (rulesCount: number) => + i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsOptions.addToListsTooltipTitle', + { + values: { rulesCount }, + defaultMessage: + 'Shared exception list is a group of exceptions. {rulesCount, plural, =1 {This rule currently has no shared} other {These rules currently have no commonly shared}} exception lists attached. To create one, visit the Exception lists management page.', + } + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.test.tsx new file mode 100644 index 0000000000000..7418fb2924d23 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.test.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { ExceptionsAddToListsTable } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import { useFindExceptionListReferences } from '../../../logic/use_find_references'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { mount } from 'enzyme'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; + +jest.mock('../../../logic/use_find_references'); + +describe('ExceptionsAddToListsTable', () => { + const mockFn = jest.fn(); + + beforeEach(() => { + (useFindExceptionListReferences as jest.Mock).mockReturnValue([ + false, + false, + { + my_list_id: { + ...getExceptionListSchemaMock(), + id: '123', + list_id: 'my_list_id', + namespace_type: 'single', + type: ExceptionListTypeEnum.DETECTION, + name: 'My exception list', + referenced_rules: [ + { + id: '345', + name: 'My rule', + rule_id: 'my_rule_id', + exception_lists: [ + { + id: '123', + list_id: 'my_list_id', + namespace_type: 'single', + type: ExceptionListTypeEnum.DETECTION, + }, + ], + }, + ], + }, + }, + mockFn, + ]); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('it displays loading state while fetching data', () => { + (useFindExceptionListReferences as jest.Mock).mockReturnValue([true, false, null, mockFn]); + const wrapper = mount( + + + + ); + + expect(mockFn).toHaveBeenCalledWith([ + { + id: '123', + listId: 'my_list_id', + namespaceType: 'single', + }, + ]); + expect(wrapper.find('[data-test-subj="exceptionItemListsTableLoading"]').exists()).toBeTruthy(); + }); + + it('it displays error state if fetching list and references data fails', () => { + (useFindExceptionListReferences as jest.Mock).mockReturnValue([false, true, null, jest.fn()]); + const wrapper = mount( + + + + ); + + expect(wrapper.find('EuiInMemoryTable').prop('error')).toEqual( + 'Unable to load shared exception lists' + ); + }); + + it('it invokes "useFindExceptionListReferences" with array of namespace types to fetch all lists if "showAllSharedLists" is "true"', () => { + mount( + + + + ); + + expect(mockFn).toHaveBeenCalledWith([ + { namespaceType: 'single' }, + { namespaceType: 'agnostic' }, + ]); + }); + + it('it displays lists with rule references', async () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="ruleReferencesDisplayPopoverButton"]').at(1).text() + ).toEqual('1'); + // Formatting is off since doesn't take css into account + expect(wrapper.find('[data-test-subj="exceptionListNameCell"]').at(1).text()).toEqual( + 'NameMy exception list' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.tsx new file mode 100644 index 0000000000000..f8dc8e41a6df4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/index.tsx @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState, useMemo } from 'react'; +import type { CriteriaWithPagination } from '@elastic/eui'; +import { EuiText, EuiSpacer, EuiInMemoryTable, EuiPanel, EuiLoadingContent } from '@elastic/eui'; +import type { ExceptionListSchema, ListArray } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import type { FindRulesReferencedByExceptionsListProp } from '../../../../../detections/containers/detection_engine/rules'; +import * as i18n from './translations'; +import { getSharedListsTableColumns } from '../utils'; +import { useFindExceptionListReferences } from '../../../logic/use_find_references'; +import type { ExceptionListRuleReferencesSchema } from '../../../../../../common/detection_engine/schemas/response'; + +interface ExceptionsAddToListsComponentProps { + /** + * Normally if there's no sharedExceptionLists, this opition is disabled, however, + * when adding an exception item from the exception lists management page, there is no + * list or rule to go off of, so user can select to add the exception to any rule or to any + * shared list. + */ + showAllSharedLists: boolean; + /* Shared exception lists to display as options to add item to */ + sharedExceptionLists: ListArray; + onListSelectionChange?: (listsSelectedToAdd: ExceptionListSchema[]) => void; +} + +const ExceptionsAddToListsComponent: React.FC = ({ + showAllSharedLists, + sharedExceptionLists, + onListSelectionChange, +}): JSX.Element => { + const listsToFetch = useMemo(() => { + return showAllSharedLists ? [] : sharedExceptionLists; + }, [showAllSharedLists, sharedExceptionLists]); + const [listsToDisplay, setListsToDisplay] = useState([]); + const [pagination, setPagination] = useState({ pageIndex: 0 }); + const [message, setMessage] = useState( + + ); + const [error, setError] = useState(undefined); + + const [isLoadingReferences, referenceFetchError, ruleReferences, fetchReferences] = + useFindExceptionListReferences(); + + useEffect(() => { + if (fetchReferences != null) { + const listsToQuery: FindRulesReferencedByExceptionsListProp[] = !listsToFetch.length + ? [{ namespaceType: 'single' }, { namespaceType: 'agnostic' }] + : listsToFetch.map(({ id, list_id: listId, namespace_type: namespaceType }) => ({ + id, + listId, + namespaceType, + })); + fetchReferences(listsToQuery); + } + }, [listsToFetch, fetchReferences]); + + useEffect(() => { + if (referenceFetchError) return setError(i18n.REFERENCES_FETCH_ERROR); + if (isLoadingReferences) { + return setMessage( + + ); + } + if (!ruleReferences) return; + const lists: ExceptionListRuleReferencesSchema[] = []; + for (const [_, value] of Object.entries(ruleReferences)) + if (value.type === ExceptionListTypeEnum.DETECTION) lists.push(value); + + setMessage(undefined); + setListsToDisplay(lists); + }, [isLoadingReferences, referenceFetchError, ruleReferences, showAllSharedLists]); + + const selectionValue = { + onSelectionChange: (selection: ExceptionListRuleReferencesSchema[]) => { + if (onListSelectionChange != null) { + onListSelectionChange( + selection.map( + ({ + referenced_rules: _, + namespace_type: namespaceType, + os_types: osTypes, + tags, + ...rest + }) => ({ + ...rest, + namespace_type: namespaceType ?? 'single', + os_types: osTypes ?? [], + tags: tags ?? [], + }) + ) + ); + } + }, + initialSelected: [], + }; + + return ( + + <> + {i18n.ADD_TO_LISTS_DESCRIPTION} + + + + tableCaption="Table of exception lists" + itemId="id" + items={listsToDisplay} + loading={message != null} + message={message} + columns={getSharedListsTableColumns()} + error={error} + pagination={{ + ...pagination, + pageSizeOptions: [5], + showPerPageOptions: false, + }} + onTableChange={({ page: { index } }: CriteriaWithPagination) => + setPagination({ pageIndex: index }) + } + selection={selectionValue} + isSelectable + sorting + data-test-subj="addExceptionToSharedListsTable" + /> + + + ); +}; + +export const ExceptionsAddToListsTable = React.memo(ExceptionsAddToListsComponent); + +ExceptionsAddToListsTable.displayName = 'ExceptionsAddToListsTable'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/translations.ts new file mode 100644 index 0000000000000..52f4a1c4c2a8d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_lists_table/translations.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const ADD_TO_LISTS_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsTableSelection.addToListsDescription', + { + defaultMessage: + 'Select shared exception list to add to. We will make a copy of this exception if multiple lists are selected.', + } +); + +export const VIEW_LIST_DETAIL_ACTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsTableSelection.viewListDetailActionLabel', + { + defaultMessage: 'View list detail', + } +); + +export const REFERENCES_FETCH_ERROR = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToListsTableSelection.referencesFetchError', + { + defaultMessage: 'Unable to load shared exception lists', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.test.tsx new file mode 100644 index 0000000000000..8084231a53676 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.test.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { ExceptionsAddToRulesOptions } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import { useFindRules } from '../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'; +import { getRulesSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; + +jest.mock('../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'); + +describe('ExceptionsAddToRulesOptions', () => { + beforeEach(() => { + (useFindRules as jest.Mock).mockReturnValue({ + data: { + rules: [getRulesSchemaMock(), { ...getRulesSchemaMock(), id: '345', name: 'My rule' }], + total: 0, + }, + isFetched: true, + }); + }); + + it('it displays option to add exception to single rule if a single rule is passed in', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="addToRuleRadioOption"]').exists()).toBeTruthy(); + }); + + it('it displays option to add exception to multiple rules if "isBulkAction" is "true" and rules are passed in', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="addToRulesRadioOption"]').exists()).toBeTruthy(); + }); + + it('it displays rules selection table if "isBulkAction" is "true" and rules are passed in', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="selectRulesToAddToRadioOption"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.tsx new file mode 100644 index 0000000000000..424f8221b5a48 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_options/index.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiRadio, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { ExceptionsAddToRulesTable } from '../add_to_rules_table'; + +export type AddToRuleListsRadioOptions = 'select_rules_to_add_to' | 'add_to_rules' | 'add_to_rule'; + +interface ExceptionsAddToRulesOptionsComponentProps { + possibleRules: Rule[] | null; + selectedRadioOption: string; + isSingleRule: boolean; + isBulkAction: boolean; + onRuleSelectionChange: (rulesSelectedToAdd: Rule[]) => void; + onRadioChange: (option: AddToRuleListsRadioOptions) => void; +} + +const ExceptionsAddToRulesOptionsComponent: React.FC = ({ + possibleRules, + isSingleRule, + isBulkAction, + selectedRadioOption, + onRuleSelectionChange, + onRadioChange, +}): JSX.Element => { + const ruleRadioOptionProps = useMemo(() => { + if (isBulkAction && possibleRules != null) { + return { + id: 'add_to_rules', + label: ( + + + {possibleRules.map(({ name }) => name).join(',')} + + ), + }} + /> + + ), + checked: selectedRadioOption === 'add_to_rules', + 'data-test-subj': 'addToRulesOptionsRadio', + onChange: () => { + onRadioChange('add_to_rules'); + onRuleSelectionChange(possibleRules); + }, + }; + } + + if (isSingleRule && possibleRules != null) { + return { + id: 'add_to_rule', + label: ( + + {possibleRules[0].name}, + }} + /> + + ), + checked: selectedRadioOption === 'add_to_rule', + 'data-test-subj': 'addToRuleOptionsRadio', + onChange: () => { + onRadioChange('add_to_rule'); + onRuleSelectionChange(possibleRules); + }, + }; + } + + return { + id: 'select_rules_to_add_to', + label: ( + + + + ), + checked: selectedRadioOption === 'select_rules_to_add_to', + 'data-test-subj': 'selectRulesToAddToOptionRadio', + onChange: () => onRadioChange('select_rules_to_add_to'), + }; + }, [ + isBulkAction, + possibleRules, + isSingleRule, + selectedRadioOption, + onRadioChange, + onRuleSelectionChange, + ]); + + return ( + <> + + {selectedRadioOption === 'select_rules_to_add_to' && ( + + )} + + ); +}; + +export const ExceptionsAddToRulesOptions = React.memo(ExceptionsAddToRulesOptionsComponent); + +ExceptionsAddToRulesOptions.displayName = 'ExceptionsAddToRulesOptions'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.test.tsx new file mode 100644 index 0000000000000..d0d5a265a2c4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +import { ExceptionsAddToRulesTable } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import { useFindRules } from '../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'; +import { getRulesSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; + +jest.mock('../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'); + +describe('ExceptionsAddToRulesTable', () => { + it('it displays loading state while fetching rules', () => { + (useFindRules as jest.Mock).mockReturnValue({ + data: { rules: [], total: 0 }, + isFetched: false, + }); + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-loading"]').exists() + ).toBeTruthy(); + }); + + it('it displays fetched rules', () => { + (useFindRules as jest.Mock).mockReturnValue({ + data: { + rules: [getRulesSchemaMock(), { ...getRulesSchemaMock(), id: '345', name: 'My rule' }], + total: 0, + }, + isFetched: true, + }); + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemViewerEmptyPrompts-loading"]').exists() + ).toBeFalsy(); + expect( + wrapper.find('.euiTableRow-isSelected td[data-test-subj="ruleNameCell"]').text() + ).toEqual('NameMy rule'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.tsx new file mode 100644 index 0000000000000..6b56b0eceb2f3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/index.tsx @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useMemo, useState } from 'react'; +import type { CriteriaWithPagination } from '@elastic/eui'; +import { EuiSpacer, EuiPanel, EuiText, EuiInMemoryTable, EuiLoadingContent } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import * as myI18n from './translations'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { useFindRules } from '../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'; +import { getRulesTableColumn } from '../utils'; + +interface ExceptionsAddToRulesComponentProps { + initiallySelectedRules?: Rule[]; + onRuleSelectionChange?: (rulesSelectedToAdd: Rule[]) => void; +} + +const ExceptionsAddToRulesTableComponent: React.FC = ({ + initiallySelectedRules, + onRuleSelectionChange, +}) => { + const { data: { rules } = { rules: [], total: 0 }, isFetched } = useFindRules({ + isInMemorySorting: true, + filterOptions: { + filter: '', + showCustomRules: false, + showElasticRules: false, + tags: [], + }, + sortingOptions: undefined, + pagination: undefined, + refetchInterval: false, + }); + + const [pagination, setPagination] = useState({ pageIndex: 0 }); + const [message, setMessage] = useState( + + ); + + useEffect(() => { + if (!isFetched) { + setMessage( + + ); + } + + if (isFetched) { + setMessage(undefined); + } + }, [setMessage, isFetched]); + + const ruleSelectionValue = { + onSelectionChange: (selection: Rule[]) => { + if (onRuleSelectionChange != null) { + onRuleSelectionChange(selection); + } + }, + initialSelected: initiallySelectedRules ?? [], + }; + + const searchOptions = useMemo( + () => ({ + box: { + incremental: true, + }, + filters: [ + { + type: 'field_value_selection' as const, + field: 'tags', + name: i18n.translate( + 'xpack.securitySolution.exceptions.addToRulesTable.tagsFilterLabel', + { + defaultMessage: 'Tags', + } + ), + multiSelect: 'or' as const, + options: rules.flatMap(({ tags }) => { + return tags.map((tag) => ({ + value: tag, + name: tag, + })); + }), + }, + ], + }), + [rules] + ); + + return ( + + <> + {myI18n.ADD_TO_SELECTED_RULES_DESCRIPTION} + + + tableCaption="Rules table" + itemId="id" + items={rules} + loading={!isFetched} + columns={getRulesTableColumn()} + pagination={{ + ...pagination, + itemsPerPage: 5, + showPerPageOptions: false, + }} + message={message} + onTableChange={({ page: { index } }: CriteriaWithPagination) => + setPagination({ pageIndex: index }) + } + selection={ruleSelectionValue} + search={searchOptions} + sorting + isSelectable + data-test-subj="addExceptionToRulesTable" + /> + + + ); +}; + +export const ExceptionsAddToRulesTable = React.memo(ExceptionsAddToRulesTableComponent); + +ExceptionsAddToRulesTable.displayName = 'ExceptionsAddToRulesTable'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/translations.ts new file mode 100644 index 0000000000000..da34fa5f83451 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/translations.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const ADD_TO_SELECTED_RULES_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToRulesTableSelection.addToSelectedRulesDescription', + { + defaultMessage: + 'Select rules add to. We will make a copy of this exception if it links to multiple rules. ', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx new file mode 100644 index 0000000000000..e7d56028a62a1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx @@ -0,0 +1,202 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionItemsFlyoutAlertsActions } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import type { AlertData } from '../../../utils/types'; + +jest.mock('../../../../../common/lib/kibana'); + +const alertDataMock: AlertData = { + '@timestamp': '1234567890', + _id: 'test-id', + file: { path: 'test/path' }, +}; + +describe('ExceptionItemsFlyoutAlertsActions', () => { + it('it displays single alert close checkbox if alert status is not "closed" and "alertData" exists', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeTruthy(); + }); + + it('it does not display single alert close checkbox if alert status is "closed"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeFalsy(); + }); + + it('it does not display single alert close checkbox if "alertData" does not exist', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeFalsy(); + }); + + it('it displays bulk close checkbox', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').exists() + ).toBeTruthy(); + }); + + it('it displays checkboxes disabled if "isAlertDataLoading" is "true"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').at(0).props().disabled + ).toBeTruthy(); + }); + + it('it displays bulk close checkbox disabled if "disableBulkCloseAlert" is "true"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeFalsy(); + }); + + it('it displays endpoint quarantine text if exception list type is "endpoint"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx new file mode 100644 index 0000000000000..9355e593d3d65 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useEffect, useMemo } from 'react'; +import styled, { css } from 'styled-components'; + +import { EuiTitle, EuiFormRow, EuiCheckbox, EuiSpacer, EuiText } from '@elastic/eui'; +import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; + +import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index'; +import type { Status } from '../../../../../../common/detection_engine/schemas/common/schemas'; +import { useFetchIndex } from '../../../../../common/containers/source'; +import { entryHasListType, entryHasNonEcsType } from './utils'; +import * as i18n from './translations'; +import type { AlertData } from '../../../utils/types'; + +const FlyoutCheckboxesSection = styled.section` + overflow-y: inherit; + height: auto; + .euiFlyoutBody__overflowContent { + padding-top: 0; + } +`; + +const SectionHeader = styled(EuiTitle)` + ${() => css` + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + `} +`; + +interface ExceptionsFlyoutAlertsActionsComponentProps { + exceptionListItems: ExceptionsBuilderReturnExceptionItem[]; + exceptionListType: ExceptionListType; + shouldBulkCloseAlert: boolean; + disableBulkClose: boolean; + alertData?: AlertData; + alertStatus?: Status; + isAlertDataLoading?: boolean; + shouldCloseSingleAlert?: boolean; + onUpdateBulkCloseIndex: (arg: string[] | undefined) => void; + onBulkCloseCheckboxChange: (arg: boolean) => void; + onSingleAlertCloseCheckboxChange?: (arg: boolean) => void; + onDisableBulkClose: (arg: boolean) => void; +} + +const ExceptionItemsFlyoutAlertsActionsComponent: React.FC< + ExceptionsFlyoutAlertsActionsComponentProps +> = ({ + isAlertDataLoading, + exceptionListItems, + exceptionListType, + shouldCloseSingleAlert, + shouldBulkCloseAlert, + disableBulkClose, + alertData, + alertStatus, + onDisableBulkClose, + onUpdateBulkCloseIndex, + onBulkCloseCheckboxChange, + onSingleAlertCloseCheckboxChange, +}): JSX.Element => { + const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); + const memoSignalIndexName = useMemo( + () => (signalIndexName !== null ? [signalIndexName] : []), + [signalIndexName] + ); + const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = + useFetchIndex(memoSignalIndexName); + + const handleBulkCloseCheckbox = useCallback( + (event: React.ChangeEvent): void => { + onBulkCloseCheckboxChange(event.currentTarget.checked); + }, + [onBulkCloseCheckboxChange] + ); + + const handleCloseSingleAlertCheckbox = useCallback( + (event: React.ChangeEvent): void => { + if (onSingleAlertCloseCheckboxChange != null) { + onSingleAlertCloseCheckboxChange(event.currentTarget.checked); + } + }, + [onSingleAlertCloseCheckboxChange] + ); + + useEffect(() => { + onUpdateBulkCloseIndex( + shouldBulkCloseAlert && memoSignalIndexName != null ? memoSignalIndexName : undefined + ); + }, [memoSignalIndexName, onUpdateBulkCloseIndex, shouldBulkCloseAlert]); + + useEffect((): void => { + if (disableBulkClose === true) { + onBulkCloseCheckboxChange(false); + } + }, [disableBulkClose, onBulkCloseCheckboxChange]); + + useEffect((): void => { + if (isSignalIndexPatternLoading === false && isSignalIndexLoading === false) { + onDisableBulkClose( + entryHasListType(exceptionListItems) || + entryHasNonEcsType(exceptionListItems, signalIndexPatterns) || + exceptionListItems.every((item) => item.entries.length === 0) + ); + } + }, [ + onDisableBulkClose, + exceptionListItems, + isSignalIndexPatternLoading, + isSignalIndexLoading, + signalIndexPatterns, + ]); + + return ( + + +

{i18n.CLOSE_ALERTS_SECTION_TITLE}

+
+ + {alertData != null && alertStatus !== 'closed' && ( + + + + )} + + + + {exceptionListType === 'endpoint' && ( + <> + + + {i18n.ENDPOINT_QUARANTINE_TEXT} + + + )} +
+ ); +}; + +export const ExceptionItemsFlyoutAlertsActions = React.memo( + ExceptionItemsFlyoutAlertsActionsComponent +); + +ExceptionItemsFlyoutAlertsActions.displayName = 'ExceptionItemsFlyoutAlertsActions'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/translations.ts new file mode 100644 index 0000000000000..74a43b32c267a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/translations.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CLOSE_ALERTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.alertsActions.sectionTitle', + { + defaultMessage: 'Alerts actions', + } +); + +export const SINGLE_ALERT_CLOSE_LABEL = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.alertsActions.singleAlertCloseLabel', + { + defaultMessage: 'Close this alert', + } +); + +export const BULK_CLOSE_LABEL_DISABLED = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.alertsActions.bulkCloseLabel.disabled', + { + defaultMessage: + 'Close all alerts that match this exception and were generated by this rule (Lists and non-ECS fields are not supported)', + } +); + +export const BULK_CLOSE_LABEL = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.alertsActions.bulkCloseLabel', + { + defaultMessage: + 'Close all alerts that match this exception and were generated by selected rule/s', + } +); + +export const ENDPOINT_QUARANTINE_TEXT = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.alertsActions.endpointQuarantineText', + { + defaultMessage: + 'On all Endpoint hosts, quarantined files that match the exception are automatically restored to their original locations. This exception applies to all rules using Endpoint exceptions.', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts new file mode 100644 index 0000000000000..777fec2fd135c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import type { EntriesArray, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { entryHasNonEcsType, entryHasListType } from './utils'; +import type { DataViewBase } from '@kbn/es-query'; + +describe('alerts_actions#utils', () => { + describe('#entryHasListType', () => { + test('it should return false with an empty array', () => { + const payload: ExceptionListItemSchema[] = []; + const result = entryHasListType(payload); + expect(result).toEqual(false); + }); + + test("it should return false with exception items that don't contain a list type", () => { + const payload = [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()]; + const result = entryHasListType(payload); + expect(result).toEqual(false); + }); + + test('it should return true with exception items that do contain a list type', () => { + const payload = [ + { + ...getExceptionListItemSchemaMock(), + entries: [{ type: ListOperatorTypeEnum.LIST }] as EntriesArray, + }, + getExceptionListItemSchemaMock(), + ]; + const result = entryHasListType(payload); + expect(result).toEqual(true); + }); + }); + + describe('#entryHasNonEcsType', () => { + const mockEcsIndexPattern = { + title: 'testIndex', + fields: [ + { + name: 'some.parentField', + }, + { + name: 'some.not.nested.field', + }, + { + name: 'nested.field', + }, + ], + } as DataViewBase; + + test('it should return false with an empty array', () => { + const payload: ExceptionListItemSchema[] = []; + const result = entryHasNonEcsType(payload, mockEcsIndexPattern); + expect(result).toEqual(false); + }); + + test("it should return false with exception items that don't contain a non ecs type", () => { + const payload = [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()]; + const result = entryHasNonEcsType(payload, mockEcsIndexPattern); + expect(result).toEqual(false); + }); + + test('it should return true with exception items that do contain a non ecs type', () => { + const payload = [ + { + ...getExceptionListItemSchemaMock(), + entries: [{ field: 'some.nonEcsField' }] as EntriesArray, + }, + getExceptionListItemSchemaMock(), + ]; + const result = entryHasNonEcsType(payload, mockEcsIndexPattern); + expect(result).toEqual(true); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts new file mode 100644 index 0000000000000..03dc4e168cf0b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataViewBase } from '@kbn/es-query'; +import type { Entry } from '@kbn/securitysolution-io-ts-list-types'; +import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { + EmptyEntry, + EmptyListEntry, + ExceptionsBuilderReturnExceptionItem, +} from '@kbn/securitysolution-list-utils'; +import { getOperatorType } from '@kbn/securitysolution-list-utils'; + +/** + * Determines if item entries has 'is in list'/'is not in list' entry + */ +export const entryHasListType = (exceptionItems: ExceptionsBuilderReturnExceptionItem[]) => { + for (const { entries } of exceptionItems) { + for (const exceptionEntry of entries ?? []) { + if (getOperatorType(exceptionEntry) === ListOperatorTypeEnum.LIST) { + return true; + } + } + } + return false; +}; + +/** + * Determines whether or not any entries within the given exceptionItems contain values not in the specified ECS mapping + */ +export const entryHasNonEcsType = ( + exceptionItems: ExceptionsBuilderReturnExceptionItem[], + indexPatterns: DataViewBase +): boolean => { + const doesFieldNameExist = (exceptionEntry: Entry | EmptyListEntry | EmptyEntry): boolean => { + return indexPatterns.fields.some(({ name }) => name === exceptionEntry.field); + }; + + if (exceptionItems.length === 0) { + return false; + } + for (const { entries } of exceptionItems) { + for (const exceptionEntry of entries ?? []) { + if (exceptionEntry.type === 'nested') { + for (const nestedExceptionEntry of exceptionEntry.entries) { + if (doesFieldNameExist(nestedExceptionEntry) === false) { + return true; + } + } + } else if (doesFieldNameExist(exceptionEntry) === false) { + return true; + } + } + } + return false; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.test.tsx new file mode 100644 index 0000000000000..a5d6de83b0655 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.test.tsx @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. 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 { ExceptionsConditions } from '.'; +import { TestProviders, mockIndexPattern } from '../../../../../common/mock'; +import { getRulesEqlSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from './translations'; + +jest.mock('@kbn/lists-plugin/public'); + +describe('ExceptionsConditions', () => { + describe('EQL rule type', () => { + it('it displays EQL warning callout if rule is EQL sequence', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="eqlSequenceCallout"]').at(0).text()).toEqual( + i18n.ADD_EXCEPTION_SEQUENCE_WARNING + ); + }); + + it('it displays EQL editing warning callout if rule is EQL sequence and "isEdit" is "true"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="eqlSequenceCallout"]').at(0).text()).toEqual( + i18n.EDIT_EXCEPTION_SEQUENCE_WARNING + ); + }); + + it('it does not display EQL warning callout if rule is EQL sequence', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="eqlSequenceCallout"]').exists()).toBeFalsy(); + }); + }); + + describe('OS options', () => { + it('it displays os options if "showOsTypeOptions" is "true"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="osSelectionDropdown"]').exists()).toBeTruthy(); + }); + + it('it displays the exception item os text if "isEdit" is "true"', () => { + const wrapper = mountWithIntl( + + + + ); + + // Text appears funky since not applying styling + expect(wrapper.find('[data-test-subj="exceptionItemSelectedOs"]').at(0).text()).toEqual( + 'Operating SystemWindows' + ); + }); + + it('it does not display os options if "showOsTypeOptions" is "false"', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="osSelectionDropdown"]').exists()).toBeFalsy(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.tsx new file mode 100644 index 0000000000000..46f16ec468873 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/index.tsx @@ -0,0 +1,276 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. 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 type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiCallOut, EuiComboBox, EuiFormRow, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; +import type { + CreateRuleExceptionListItemSchema, + CreateExceptionListItemSchema, + ExceptionListItemSchema, + ExceptionListType, + OsType, + OsTypeArray, + NamespaceType, +} from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { + ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, +} from '@kbn/securitysolution-list-utils'; +import type { DataViewBase } from '@kbn/es-query'; +import styled, { css } from 'styled-components'; +import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { hasEqlSequenceQuery, isEqlRule } from '../../../../../../common/detection_engine/utils'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { useKibana } from '../../../../../common/lib/kibana'; +import * as i18n from './translations'; +import * as sharedI18n from '../../../utils/translations'; + +const OS_OPTIONS: Array> = [ + { + label: sharedI18n.OPERATING_SYSTEM_WINDOWS, + value: ['windows'], + }, + { + label: sharedI18n.OPERATING_SYSTEM_MAC, + value: ['macos'], + }, + { + label: sharedI18n.OPERATING_SYSTEM_LINUX, + value: ['linux'], + }, + { + label: sharedI18n.OPERATING_SYSTEM_WINDOWS_AND_MAC, + value: ['windows', 'macos'], + }, +]; + +const SectionHeader = styled(EuiTitle)` + ${() => css` + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + `} +`; + +interface ExceptionsFlyoutConditionsComponentProps { + /* Exception list item field value for "name" */ + exceptionItemName: string; + /* Not all rule types support large value lists */ + allowLargeValueLists: boolean; + /* Exception items - could be one being edited, or multiple being added */ + exceptionListItems: ExceptionsBuilderExceptionItem[]; + /* Fields used to populate the field option dropdown */ + indexPatterns: DataViewBase; + /* Exception items can be added to zero (just being added to a shared list), one or more rules */ + rules: Rule[] | null; + /* OS options required for endpoint exceptions */ + showOsTypeOptions: boolean; + /* Selected OS option required for endpoint exceptions */ + selectedOs: OsTypeArray | undefined; + /* Determines whether component is being used in an add or edit functionality */ + isEdit: boolean; + /* + * Supported exception list types are 'endpoint', 'detection' and 'rule_default' */ + exceptionListType: ExceptionListType; + /* OS selection handler */ + onOsChange?: (os: OsTypeArray | undefined) => void; + /* Exception item builder takes a callback used when there are updates to the item */ + + onExceptionItemAdd: (items: ExceptionsBuilderReturnExceptionItem[]) => void; + /* Exception item builder takes a callback used when there are updates to the item that includes information on if any form errors exist */ + onSetErrorExists: (errorExists: boolean) => void; + onFilterIndexPatterns: ( + patterns: DataViewBase, + type: ExceptionListType, + osTypes?: Array<'linux' | 'macos' | 'windows'> | undefined + ) => DataViewBase; +} + +const ExceptionsConditionsComponent: React.FC = ({ + exceptionItemName, + allowLargeValueLists, + exceptionListItems, + indexPatterns, + rules, + exceptionListType, + showOsTypeOptions, + selectedOs, + isEdit, + onOsChange, + onExceptionItemAdd, + onSetErrorExists, + onFilterIndexPatterns, +}): JSX.Element => { + const { http, unifiedSearch } = useKibana().services; + const isEndpointException = useMemo( + (): boolean => exceptionListType === ExceptionListTypeEnum.ENDPOINT, + [exceptionListType] + ); + const includesRuleWithEQLSequenceStatement = useMemo((): boolean => { + return ( + rules != null && rules.some((rule) => isEqlRule(rule.type) && hasEqlSequenceQuery(rule.query)) + ); + }, [rules]); + + // If editing an item (can only edit a single item at a time), get it's + // list_id. Otherwise, if it is an item for an endpoint list, the list_id is + // hard coded. For all others, we'll fill in the appropriate list_id when + // enriching items before creating when we know if they'll be added to the rule_default + // list or a shared list. + const listIdToUse = useMemo((): string | undefined => { + if (isEndpointException) { + return 'endpoint_list'; + } + + const defaultValue = isEndpointException ? ENDPOINT_LIST_ID : undefined; + + return isEdit ? exceptionListItems[0].list_id : defaultValue; + }, [isEndpointException, isEdit, exceptionListItems]); + + // If editing an item (can only edit a single item at a time), get it's + // namespace_type. Otherwise, if it is an item for an endpoint list, the namespace_type is + // 'agnostic'. For all others, we'll fill in the appropriate list_id when + // enriching items before creating when we know if they'll be added to the rule_default + // list or a shared list. + const listNamespaceType = useMemo((): NamespaceType | undefined => { + const defaultValue = isEndpointException ? 'agnostic' : undefined; + + return isEdit ? exceptionListItems[0].namespace_type : defaultValue; + }, [exceptionListItems, isEdit, isEndpointException]); + + const handleBuilderOnChange = useCallback( + ({ + exceptionItems, + errorExists, + }: { + exceptionItems: Array< + ExceptionListItemSchema | CreateExceptionListItemSchema | CreateRuleExceptionListItemSchema + >; + errorExists: boolean; + }) => { + onExceptionItemAdd(exceptionItems); + onSetErrorExists(errorExists); + }, + [onSetErrorExists, onExceptionItemAdd] + ); + + const handleOSSelectionChange = useCallback( + (selectedOptions: Array>): void => { + const os = selectedOptions[0].value; + if (onOsChange != null) { + onOsChange(os ? os : undefined); + } + }, + [onOsChange] + ); + + const osSingleSelectionOptions = useMemo(() => { + return { asPlainText: true }; + }, []); + + const selectedOStoOptions = useMemo((): Array> => { + return OS_OPTIONS.filter((option) => { + return selectedOs === option.value; + }); + }, [selectedOs]); + + const isExceptionBuilderFormDisabled = useMemo(() => { + return showOsTypeOptions && selectedOs === undefined; + }, [showOsTypeOptions, selectedOs]); + + const osDisplay = (osTypes: OsTypeArray): string => { + const translateOS = (currentOs: OsType): string => { + return currentOs === 'linux' + ? sharedI18n.OPERATING_SYSTEM_LINUX + : currentOs === 'macos' + ? sharedI18n.OPERATING_SYSTEM_MAC + : sharedI18n.OPERATING_SYSTEM_WINDOWS; + }; + return osTypes + .reduce((osString, currentOs) => { + return `${translateOS(currentOs)}, ${osString}`; + }, '') + .slice(0, -2); + }; + + const eqlCalloutWarning = useMemo((): string => { + return isEdit ? i18n.EDIT_EXCEPTION_SEQUENCE_WARNING : i18n.ADD_EXCEPTION_SEQUENCE_WARNING; + }, [isEdit]); + + const osTypes = useMemo((): OsTypeArray => { + return (isEdit ? exceptionListItems[0].os_types : selectedOs) ?? []; + }, [exceptionListItems, isEdit, selectedOs]); + + return ( + <> + +

{i18n.RULE_EXCEPTION_CONDITIONS}

+
+ {includesRuleWithEQLSequenceStatement && ( + <> + + + + )} + + {i18n.EXCEPTION_BUILDER_INFO} + + {showOsTypeOptions && !isEdit && ( + <> + + + + + + )} + {showOsTypeOptions && isEdit && ( + <> + +
+
{sharedI18n.OPERATING_SYSTEM_LABEL}
+
{osDisplay(osTypes)}
+
+
+ + + )} + {getExceptionBuilderComponentLazy({ + allowLargeValueLists, + httpService: http, + autocompleteService: unifiedSearch.autocomplete, + exceptionListItems, + listType: exceptionListType, + osTypes, + listId: listIdToUse, + listNamespaceType, + listTypeSpecificIndexPatternFilter: onFilterIndexPatterns, + exceptionItemName, + indexPatterns, + isOrDisabled: isExceptionBuilderFormDisabled, + isAndDisabled: isExceptionBuilderFormDisabled, + isNestedDisabled: isExceptionBuilderFormDisabled, + dataTestSubj: 'alertExceptionBuilder', + idAria: 'alertExceptionBuilder', + onChange: handleBuilderOnChange, + isDisabled: isExceptionBuilderFormDisabled, + })} + + ); +}; + +export const ExceptionsConditions = React.memo(ExceptionsConditionsComponent); + +ExceptionsConditions.displayName = 'ExceptionsConditions'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/translations.ts new file mode 100644 index 0000000000000..4405f65e9cf5d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_conditions/translations.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +// Conditions component +export const RULE_EXCEPTION_CONDITIONS = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.itemConditions.conditionsTitle', + { + defaultMessage: 'Conditions', + } +); + +export const EXCEPTION_BUILDER_INFO = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.itemConditions.infoLabel', + { + defaultMessage: "Alerts are generated when the rule's conditions are met, except when:", + } +); + +export const ADD_EXCEPTION_SEQUENCE_WARNING = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.itemConditions.sequenceWarningAdd', + { + defaultMessage: + "This rule's query contains an EQL sequence statement. The exception created will apply to all events in the sequence.", + } +); + +export const EDIT_EXCEPTION_SEQUENCE_WARNING = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.itemConditions.sequenceWarningEdit', + { + defaultMessage: + "This rule's query contains an EQL sequence statement. The exception modified will apply to all events in the sequence.", + } +); + +export const OPERATING_SYSTEM_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.itemConditions.operatingSystemPlaceHolder', + { + defaultMessage: 'Select an operating system', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.test.tsx new file mode 100644 index 0000000000000..b280aa73b658d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.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; 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 { EuiFieldText } from '@elastic/eui'; + +import { ExceptionsFlyoutMeta } from '.'; +import { TestProviders } from '../../../../../common/mock'; + +describe('ExceptionsFlyoutMeta', () => { + it('it renders component', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionFlyoutName"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').at(1).props().value).toEqual( + 'Test name' + ); + }); + + it('it calls onChange on name change', () => { + const mockOnChange = jest.fn(); + const wrapper = mountWithIntl( + + + + ); + + ( + wrapper.find(EuiFieldText).at(0).props() as unknown as { + onChange: (e: React.ChangeEvent) => void; + } + ).onChange({ target: { value: 'Name change' } } as React.ChangeEvent); + + expect(mockOnChange).toHaveBeenCalledWith(['name', 'Name change']); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.tsx new file mode 100644 index 0000000000000..e666f21ec147c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/index.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { EuiFormRow, EuiFieldText } from '@elastic/eui'; + +import * as i18n from './translations'; + +interface ExceptionsFlyoutMetaComponentProps { + exceptionItemName: string; + onChange: (value: [string, string]) => void; +} + +const ExceptionsFlyoutMetaComponent: React.FC = ({ + exceptionItemName, + onChange, +}): JSX.Element => { + const onNameChange = useCallback( + (e: React.ChangeEvent) => { + onChange(['name', e.target.value]); + }, + [onChange] + ); + + return ( + + + + ); +}; + +export const ExceptionsFlyoutMeta = React.memo(ExceptionsFlyoutMetaComponent); + +ExceptionsFlyoutMeta.displayName = 'ExceptionsFlyoutMeta'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/translations.ts new file mode 100644 index 0000000000000..db3519ed98616 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/item_meta_form/translations.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const RULE_EXCEPTION_NAME_LABEL = i18n.translate( + 'xpack.securitySolution.rule_exceptions.itemMeta.nameLabel', + { + defaultMessage: 'Rule exception name', + } +); + +export const RULE_EXCEPTION_NAME_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.rule_exceptions.itemMeta.namePlaceholder', + { + defaultMessage: 'Name your rule exception', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.test.tsx new file mode 100644 index 0000000000000..a5582458e5984 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.test.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { ExceptionsLinkedToLists } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { mount } from 'enzyme'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; + +jest.mock('../../../logic/use_find_references'); + +describe('ExceptionsLinkedToLists', () => { + it('it displays loading state while "isLoadingReferences" is "true"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemListsTableLoading"]').exists()).toBeTruthy(); + }); + + it('it displays error state if "errorFetchingReferences" is "true"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('EuiInMemoryTable').prop('error')).toEqual( + 'Unable to fetch exception list.' + ); + }); + + it('it displays lists with rule references', async () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="ruleReferencesDisplayPopoverButton"]').at(1).text() + ).toEqual('1'); + // Formatting is off since doesn't take css into account + expect(wrapper.find('[data-test-subj="exceptionListNameCell"]').at(1).text()).toEqual( + 'NameMy exception list' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.tsx new file mode 100644 index 0000000000000..55a748ae7dffd --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/index.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { EuiTitle, EuiSpacer, EuiPanel, EuiInMemoryTable, EuiLoadingContent } from '@elastic/eui'; +import styled, { css } from 'styled-components'; + +import * as i18n from './translations'; +import type { ExceptionListRuleReferencesSchema } from '../../../../../../common/detection_engine/schemas/response'; +import { getSharedListsTableColumns } from '../utils'; + +interface ExceptionsLinkedToListComponentProps { + isLoadingReferences: boolean; + errorFetchingReferences: boolean; + listAndReferences: ExceptionListRuleReferencesSchema[]; +} + +const SectionHeader = styled(EuiTitle)` + ${() => css` + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + `} +`; + +const ExceptionsLinkedToListsComponent: React.FC = ({ + isLoadingReferences, + errorFetchingReferences, + listAndReferences, +}): JSX.Element => { + const [message, setMessage] = useState( + + ); + const [error, setError] = useState(undefined); + + useEffect(() => { + if (errorFetchingReferences) { + setError(i18n.LINKED_TO_LIST_ERROR); + } else if (!isLoadingReferences) { + setMessage(undefined); + } + }, [errorFetchingReferences, isLoadingReferences]); + + return ( + + +

{i18n.LINKED_TO_LIST_TITLE}

+
+ + + tableCaption="Table of exception lists" + itemId="id" + message={message} + loading={isLoadingReferences} + items={listAndReferences} + error={error} + columns={getSharedListsTableColumns()} + isSelectable={false} + sorting + data-test-subj="exceptionItemSharedList" + /> +
+ ); +}; + +export const ExceptionsLinkedToLists = React.memo(ExceptionsLinkedToListsComponent); + +ExceptionsLinkedToLists.displayName = 'ExceptionsLinkedToLists'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/translations.ts new file mode 100644 index 0000000000000..c5425a706c904 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_list/translations.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const LINKED_TO_LIST_TITLE = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.linkedToListSection.title', + { + defaultMessage: 'Linked to shared list', + } +); + +export const LINKED_TO_LIST_ERROR = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.linkedToListSection.error', + { + defaultMessage: 'Unable to fetch exception list.', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.test.tsx new file mode 100644 index 0000000000000..56cf481282d34 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.test.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +import { ExceptionsLinkedToRule } from '.'; +import { TestProviders } from '../../../../../common/mock'; +import { getRulesSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; + +jest.mock('../../../../../detections/pages/detection_engine/rules/all/rules_table/use_find_rules'); + +describe('ExceptionsLinkedToRule', () => { + it('it displays rule name and link', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="ruleNameCell"]').at(0).text()).toEqual('NameMy rule'); + expect(wrapper.find('[data-test-subj="ruleAction-viewDetails"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.tsx new file mode 100644 index 0000000000000..289660317025c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/index.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiTitle, EuiSpacer, EuiInMemoryTable } from '@elastic/eui'; +import styled, { css } from 'styled-components'; + +import * as i18n from './translations'; +import type { Rule } from '../../../../../detections/containers/detection_engine/rules/types'; +import { getRulesTableColumn } from '../utils'; + +interface ExceptionsLinkedToRuleComponentProps { + rule: Rule; +} + +const SectionHeader = styled(EuiTitle)` + ${() => css` + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + `} +`; + +const ExceptionsLinkedToRuleComponent: React.FC = ({ + rule, +}): JSX.Element => { + return ( + <> + +

{i18n.LINKED_TO_RULE_TITLE}

+
+ + + tableCaption="Rules table" + itemId="id" + items={[rule]} + columns={getRulesTableColumn()} + sorting + data-test-subj="addExceptionToRulesTable" + /> + + ); +}; + +export const ExceptionsLinkedToRule = React.memo(ExceptionsLinkedToRuleComponent); + +ExceptionsLinkedToRule.displayName = 'ExceptionsLinkedToRule'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/translations.ts new file mode 100644 index 0000000000000..d7497786b6d9b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/linked_to_rule/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 LINKED_TO_RULE_TITLE = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.linkedToRule.title', + { + defaultMessage: 'Linked to rule', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts new file mode 100644 index 0000000000000..378132abff178 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const VIEW_LIST_DETAIL_ACTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.viewListDetailActionLabel', + { + defaultMessage: 'View list detail', + } +); + +export const VIEW_RULE_DETAIL_ACTION = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.viewRuleDetailActionLabel', + { + defaultMessage: 'View rule detail', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.test.tsx new file mode 100644 index 0000000000000..e2e3f90542639 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.test.tsx @@ -0,0 +1,284 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getCreateExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import type { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; + +import { + enrichItemWithComment, + enrichItemWithName, + enrichEndpointItems, + enrichItemsForDefaultRuleList, + enrichItemsForSharedLists, + entrichNewExceptionItems, +} from './utils'; + +const getExceptionItems = (): ExceptionsBuilderReturnExceptionItem[] => [ + { ...getCreateExceptionListItemSchemaMock(), os_types: [] }, +]; + +describe('add_exception_flyout#utils', () => { + describe('entrichNewExceptionItems', () => { + it('enriches exception items for rule default list', () => { + const items = getExceptionItems(); + + expect( + entrichNewExceptionItems({ + itemName: 'My item', + commentToAdd: 'New comment', + addToRules: true, + addToSharedLists: false, + sharedLists: [], + selectedOs: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + items, + }) + ).toEqual([ + { + ...items[0], + comments: [{ comment: 'New comment' }], + list_id: undefined, + name: 'My item', + namespace_type: 'single', + }, + ]); + }); + + it('enriches exception items for shared lists', () => { + const items = getExceptionItems(); + const sharedLists: ExceptionListSchema[] = [ + { + ...getExceptionListSchemaMock(), + list_id: 'foo', + namespace_type: 'single', + }, + { + ...getExceptionListSchemaMock(), + list_id: 'bar', + }, + ]; + + expect( + entrichNewExceptionItems({ + itemName: 'My item', + commentToAdd: 'New comment', + addToRules: false, + addToSharedLists: true, + sharedLists, + selectedOs: [], + listType: ExceptionListTypeEnum.DETECTION, + items, + }) + ).toEqual([ + { + ...items[0], + comments: [{ comment: 'New comment' }], + list_id: 'foo', + name: 'My item', + namespace_type: 'single', + }, + { + ...items[0], + comments: [{ comment: 'New comment' }], + list_id: 'bar', + name: 'My item', + namespace_type: 'agnostic', + }, + ]); + }); + + it('enriches exception items for endpoint list', () => { + const items: ExceptionsBuilderReturnExceptionItem[] = [ + { + ...getCreateExceptionListItemSchemaMock(), + list_id: 'endpoint_list', + namespace_type: 'agnostic', + os_types: [], + }, + ]; + + expect( + entrichNewExceptionItems({ + itemName: 'My item', + commentToAdd: 'New comment', + addToRules: false, + addToSharedLists: false, + sharedLists: [], + selectedOs: ['windows'], + listType: ExceptionListTypeEnum.ENDPOINT, + items, + }) + ).toEqual([ + { + ...items[0], + comments: [{ comment: 'New comment' }], + list_id: 'endpoint_list', + name: 'My item', + namespace_type: 'agnostic', + os_types: ['windows'], + }, + ]); + }); + }); + + describe('enrichItemWithComment', () => { + it('returns items unchanged if no comment', () => { + const items = getExceptionItems(); + + expect(enrichItemWithComment(' ')(items)).toEqual(items); + }); + + it('returns items with new comment', () => { + const items = getExceptionItems(); + + expect(enrichItemWithComment('My new comment')(items)).toEqual([ + { + ...items[0], + comments: [ + { + comment: 'My new comment', + }, + ], + }, + ]); + }); + }); + + describe('enrichItemWithName', () => { + it('returns items unchanged if no name', () => { + const items = getExceptionItems(); + + expect(enrichItemWithName(' ')(items)).toEqual(items); + }); + + it('returns items with name', () => { + const items = getExceptionItems(); + + expect(enrichItemWithName('My item')(items)).toEqual([ + { + ...items[0], + name: 'My item', + }, + ]); + }); + }); + + describe('enrichEndpointItems', () => { + it('returns items unchanged if "listType" is not "endpoint"', () => { + const items = getExceptionItems(); + + expect(enrichEndpointItems(ExceptionListTypeEnum.DETECTION, [])(items)).toEqual(items); + }); + + it('returns items with os types', () => { + const items: ExceptionsBuilderReturnExceptionItem[] = [ + { + ...getCreateExceptionListItemSchemaMock(), + namespace_type: 'agnostic', + list_id: 'endpoint_list', + }, + ]; + + expect(enrichEndpointItems(ExceptionListTypeEnum.ENDPOINT, ['windows'])(items)).toEqual([ + { + ...items[0], + os_types: ['windows'], + }, + ]); + }); + }); + + describe('enrichItemsForDefaultRuleList', () => { + it('returns items unchanged if "addToRules" is "false"', () => { + const items = getExceptionItems(); + + expect(enrichItemsForDefaultRuleList(ExceptionListTypeEnum.DETECTION, false)(items)).toEqual( + items + ); + }); + + /* + * Wouldn't make sense for the second argument to be "true", when + * listType is endpoint, but figured it'd be good + * to test anyways. + */ + it('returns items unchanged if "listType" is "endpoint"', () => { + const items = getExceptionItems(); + + expect(enrichItemsForDefaultRuleList(ExceptionListTypeEnum.ENDPOINT, true)(items)).toEqual( + items + ); + }); + + it('returns items with to add for each shared list', () => { + const items = getExceptionItems(); + + expect(enrichItemsForDefaultRuleList(ExceptionListTypeEnum.DETECTION, true)(items)).toEqual([ + { + ...items[0], + list_id: undefined, + namespace_type: 'single', + }, + ]); + }); + }); + + describe('enrichItemsForSharedLists', () => { + it('returns items unchanged if "addToSharedLists" is "false"', () => { + const items = getExceptionItems(); + + expect(enrichItemsForSharedLists(ExceptionListTypeEnum.DETECTION, false, [])(items)).toEqual( + items + ); + }); + + /* + * Wouldn't make sense for the second argument to be "true", when + * listType is endpoint, but figured it'd be good + * to test anyways. + */ + it('returns items unchanged if "listType" is "endpoint"', () => { + const items = getExceptionItems(); + + expect(enrichItemsForSharedLists(ExceptionListTypeEnum.ENDPOINT, true, [])(items)).toEqual( + items + ); + }); + + it('returns items with to add for each shared list', () => { + const items = getExceptionItems(); + const sharedLists: ExceptionListSchema[] = [ + { + ...getExceptionListSchemaMock(), + list_id: 'foo', + namespace_type: 'single', + }, + { + ...getExceptionListSchemaMock(), + list_id: 'bar', + }, + ]; + expect( + enrichItemsForSharedLists(ExceptionListTypeEnum.DETECTION, true, sharedLists)(items) + ).toEqual([ + { + ...items[0], + list_id: 'foo', + namespace_type: 'single', + }, + { + ...items[0], + list_id: 'bar', + namespace_type: 'agnostic', + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx new file mode 100644 index 0000000000000..77e881fdad6f0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx @@ -0,0 +1,269 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. 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 { pipe } from 'lodash/fp'; + +import type { ExceptionListSchema, OsType } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; + +import { + enrichExceptionItemsWithOS, + enrichNewExceptionItemsWithComments, + enrichNewExceptionItemsWithName, + enrichRuleExceptions, + enrichSharedExceptions, + lowercaseHashValues, +} from '../../utils/helpers'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; +import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; +import { RuleDetailTabs } from '../../../../detections/pages/detection_engine/rules/details'; +import { SecurityPageName } from '../../../../../common/constants'; +import { PopoverItems } from '../../../../common/components/popover_items'; +import type { + ExceptionListRuleReferencesInfoSchema, + ExceptionListRuleReferencesSchema, +} from '../../../../../common/detection_engine/schemas/response'; +import type { Rule } from '../../../../detections/containers/detection_engine/rules/types'; +import * as i18n from './translations'; + +/** + * Adds user defined name to all new exceptionItems + * @param commentToAdd new comment to add to item + */ +export const enrichItemWithComment = + (commentToAdd: string) => + (items: ExceptionsBuilderReturnExceptionItem[]): ExceptionsBuilderReturnExceptionItem[] => { + return commentToAdd.trim() !== '' + ? enrichNewExceptionItemsWithComments(items, [{ comment: commentToAdd }]) + : items; + }; + +/** + * Adds user defined name to all new exceptionItems + * @param itemName exception item name + */ +export const enrichItemWithName = + (itemName: string) => (items: ExceptionsBuilderReturnExceptionItem[]) => { + return itemName.trim() !== '' ? enrichNewExceptionItemsWithName(items, itemName) : items; + }; + +/** + * Modifies item entries to be in correct format and adds os selection to items + * @param listType exception list type + * @param selectedOs os selection + */ +export const enrichEndpointItems = + (listType: ExceptionListTypeEnum, selectedOs: OsType[]) => + (items: ExceptionsBuilderReturnExceptionItem[]) => { + if (listType === ExceptionListTypeEnum.ENDPOINT) { + return lowercaseHashValues(enrichExceptionItemsWithOS(items, selectedOs)); + } else { + return items; + } + }; + +/** + * Modifies exception items to prepare for creating as rule_default + * list items + * @param listType exception list type + * @param addToRules boolean determining if user selected to add items to default rule list + */ +export const enrichItemsForDefaultRuleList = + (listType: ExceptionListTypeEnum, addToRules: boolean) => + (items: ExceptionsBuilderReturnExceptionItem[]) => { + if (addToRules && listType !== ExceptionListTypeEnum.ENDPOINT) { + return enrichRuleExceptions(items); + } else { + return items; + } + }; + +/** + * Prepares items to be added to shared exception lists + * @param listType exception list type + * @param addToSharedLists boolean determining if user selected to add items to shared list + * @param lists shared exception lists that were selected to add items to + */ +export const enrichItemsForSharedLists = + (listType: ExceptionListTypeEnum, addToSharedLists: boolean, lists: ExceptionListSchema[]) => + (items: ExceptionsBuilderReturnExceptionItem[]) => { + if (addToSharedLists && listType !== ExceptionListTypeEnum.ENDPOINT) { + return enrichSharedExceptions(items, lists); + } else { + return items; + } + }; + +/** + * Series of utils to modify and prepare exception items for update or creation + * @param itemName user defined exception item name + * @param commentToAdd comment to be added to item + * @param addToRules boolean determining if user selected to add items to default rule list + * @param addToSharedLists boolean determining if user selected to add items to shared list + * @param sharedLists shared exception lists that were selected to add items to + * @param selectedOs os selection + * @param listType exception list type + * @param items exception items to be modified + */ +export const entrichNewExceptionItems = ({ + itemName, + commentToAdd, + addToRules, + addToSharedLists, + sharedLists, + selectedOs, + listType, + items, +}: { + itemName: string; + commentToAdd: string; + selectedOs: OsType[]; + addToRules: boolean; + addToSharedLists: boolean; + sharedLists: ExceptionListSchema[]; + listType: ExceptionListTypeEnum; + items: ExceptionsBuilderReturnExceptionItem[]; +}): ExceptionsBuilderReturnExceptionItem[] => { + const enriched: ExceptionsBuilderReturnExceptionItem[] = pipe( + enrichItemWithComment(commentToAdd), + enrichItemWithName(itemName), + enrichEndpointItems(listType, selectedOs), + enrichItemsForDefaultRuleList(listType, addToRules), + enrichItemsForSharedLists(listType, addToSharedLists, sharedLists) + )(items); + + return enriched; +}; + +/** + * Series of utils to modify and prepare exception items for update or creation + * @param itemName user defined exception item name + * @param commentToAdd comment to be added to item + * @param addToRules boolean determining if user selected to add items to default rule list + * @param addToSharedLists boolean determining if user selected to add items to shared list + * @param sharedLists shared exception lists that were selected to add items to + * @param selectedOs os selection + * @param listType exception list type + * @param items exception items to be modified + */ +export const entrichExceptionItemsForUpdate = ({ + itemName, + commentToAdd, + selectedOs, + listType, + items, +}: { + itemName: string; + commentToAdd: string; + selectedOs: OsType[]; + listType: ExceptionListTypeEnum; + items: ExceptionsBuilderReturnExceptionItem[]; +}): ExceptionsBuilderReturnExceptionItem[] => { + const enriched: ExceptionsBuilderReturnExceptionItem[] = pipe( + enrichItemWithComment(commentToAdd), + enrichItemWithName(itemName), + enrichEndpointItems(listType, selectedOs) + )(items); + + return enriched; +}; + +/** + * Shared lists columns for EuiInMemoryTable + */ +export const getSharedListsTableColumns = () => [ + { + field: 'name', + name: 'Name', + sortable: true, + 'data-test-subj': 'exceptionListNameCell', + }, + { + field: 'referenced_rules', + name: '# of rules linked to', + sortable: false, + 'data-test-subj': 'exceptionListRulesLinkedToIdCell', + render: (references: ExceptionListRuleReferencesInfoSchema[]) => { + if (references.length === 0) return '0'; + + const renderItem = (reference: ExceptionListRuleReferencesInfoSchema, i: number) => ( + + {reference.name} + + ); + + return ( + + ); + }, + }, + // TODO: This will need to be updated once PR goes in with list details page + { + name: 'Actions', + actions: [ + { + 'data-test-subj': 'exceptionListRulesActionCell', + render: (list: ExceptionListRuleReferencesSchema) => { + return ( + + {i18n.VIEW_LIST_DETAIL_ACTION} + + ); + }, + }, + ], + }, +]; + +/** + * Rules columns for EuiInMemoryTable + */ +export const getRulesTableColumn = () => [ + { + field: 'name', + name: 'Name', + sortable: true, + 'data-test-subj': 'ruleNameCell', + }, + { + name: 'Actions', + actions: [ + { + 'data-test-subj': 'ruleAction-view', + render: (rule: Rule) => { + return ( + + {i18n.VIEW_RULE_DETAIL_ACTION} + + ); + }, + }, + ], + }, +]; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx new file mode 100644 index 0000000000000..214d0041fb9a2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { EuiTextArea } from '@elastic/eui'; + +import { ExceptionItemComments } from '.'; +import { TestProviders } from '../../../../common/mock'; +import { useCurrentUser } from '../../../../common/lib/kibana'; +import { shallow } from 'enzyme'; + +jest.mock('../../../../common/lib/kibana'); + +describe('ExceptionItemComments', () => { + beforeEach(() => { + (useCurrentUser as jest.Mock).mockReturnValue({ + username: 'user', + email: 'email', + fullName: 'full name', + roles: ['user-role'], + enabled: true, + authentication_realm: { name: 'native1', type: 'native' }, + lookup_realm: { name: 'native1', type: 'native' }, + authentication_provider: { type: 'basic', name: 'basic1' }, + authentication_type: 'realm', + elastic_cloud_user: false, + metadata: { _reserved: false }, + }); + }); + + it('it uses user full_name if one exists', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemCommentAvatar"]').prop('name')).toEqual( + 'full name' + ); + }); + + it('it uses user email if fullName is not available', () => { + (useCurrentUser as jest.Mock).mockReturnValue({ + username: 'user', + email: 'email', + fullName: '', + roles: ['user-role'], + enabled: true, + authentication_realm: { name: 'native1', type: 'native' }, + lookup_realm: { name: 'native1', type: 'native' }, + authentication_provider: { type: 'basic', name: 'basic1' }, + authentication_type: 'realm', + elastic_cloud_user: false, + metadata: { _reserved: false }, + }); + + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemCommentAvatar"]').prop('name')).toEqual( + 'email' + ); + }); + + it('it uses username if fullName and email are not available', () => { + (useCurrentUser as jest.Mock).mockReturnValue({ + username: 'user', + email: '', + fullName: '', + roles: ['user-role'], + enabled: true, + authentication_realm: { name: 'native1', type: 'native' }, + lookup_realm: { name: 'native1', type: 'native' }, + authentication_provider: { type: 'basic', name: 'basic1' }, + authentication_type: 'realm', + elastic_cloud_user: false, + metadata: { _reserved: false }, + }); + + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemCommentAvatar"]').prop('name')).toEqual( + 'user' + ); + }); + + it('it renders new comment', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemCommentsAccordion"]').exists()).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="newExceptionItemCommentTextArea"]').at(1).props().value + ).toEqual('This is a new comment'); + }); + + it('it calls newCommentOnChange on comment update change', () => { + const mockOnCommentChange = jest.fn(); + const wrapper = mountWithIntl( + + + + ); + + ( + wrapper.find(EuiTextArea).at(0).props() as unknown as { + onChange: (e: React.ChangeEvent) => void; + } + ).onChange({ + target: { value: 'Updating my new comment' }, + } as React.ChangeEvent); + + expect(mockOnCommentChange).toHaveBeenCalledWith('Updating my new comment'); + }); + + it('it renders existing comments if any exist', () => { + const mockOnCommentChange = jest.fn(); + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionItemCommentsAccordion"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx index c83e729669813..fa514850e30b4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx @@ -18,13 +18,14 @@ import { EuiText, } from '@elastic/eui'; import type { Comment } from '@kbn/securitysolution-io-ts-list-types'; -import * as i18n from '../../utils/translations'; +import * as i18n from './translations'; import { useCurrentUser } from '../../../../common/lib/kibana'; import { getFormattedComments } from '../../utils/helpers'; interface ExceptionItemCommentsProps { exceptionItemComments?: Comment[]; newCommentValue: string; + accordionTitle?: JSX.Element; newCommentOnChange: (value: string) => void; } @@ -48,10 +49,27 @@ const CommentAccordion = styled(EuiAccordion)` export const ExceptionItemComments = memo(function ExceptionItemComments({ exceptionItemComments, newCommentValue, + accordionTitle, newCommentOnChange, }: ExceptionItemCommentsProps) { const [shouldShowComments, setShouldShowComments] = useState(false); const currentUser = useCurrentUser(); + const fullName = currentUser?.fullName; + const userName = currentUser?.username; + const userEmail = currentUser?.email; + const avatarName = useMemo(() => { + if (fullName && fullName.length > 0) { + return fullName; + } + + // Did email second because for cloud users, username is a uuid, + // so favor using name or email prior to using the cloud generated id + if (userEmail && userEmail.length > 0) { + return userEmail; + } + + return userName && userName.length > 0 ? userName : i18n.UNKNOWN_AVATAR_NAME; + }, [fullName, userEmail, userName]); const handleOnChange = useCallback( (event: React.ChangeEvent) => { @@ -64,7 +82,7 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ setShouldShowComments(isOpen); }, []); - const shouldShowAccordion: boolean = useMemo(() => { + const exceptionItemsExist: boolean = useMemo(() => { return exceptionItemComments != null && exceptionItemComments.length > 0; }, [exceptionItemComments]); @@ -92,12 +110,12 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ return (
- {shouldShowAccordion && ( + {exceptionItemsExist && ( handleTriggerOnClick(isOpen)} > @@ -105,10 +123,7 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ )} - + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts new file mode 100644 index 0000000000000..afe20c6aada98 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const UNKNOWN_AVATAR_NAME = i18n.translate( + 'xpack.securitySolution.rule_exceptions.itemComments.unknownAvatarName', + { + defaultMessage: 'Uknown', + } +); + +export const ADD_COMMENT_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.rule_exceptions.itemComments.addCommentPlaceholder', + { + defaultMessage: 'Add a new comment...', + } +); + +export const COMMENTS_SHOW = (comments: number) => + i18n.translate('xpack.securitySolution.rule_exceptions.itemComments.showCommentsLabel', { + values: { comments }, + defaultMessage: 'Show ({comments}) {comments, plural, =1 {Comment} other {Comments}}', + }); + +export const COMMENTS_HIDE = (comments: number) => + i18n.translate('xpack.securitySolution.rule_exceptions.itemComments.hideCommentsLabel', { + values: { comments }, + defaultMessage: 'Hide ({comments}) {comments, plural, =1 {Comment} other {Comments}}', + }); 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 index f7628f0014d23..a051f140ec2cb 100644 --- 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 @@ -5,51 +5,49 @@ * 2.0. */ -import { useEffect, useMemo, useState } from 'react'; -import type { ListArray } from '@kbn/securitysolution-io-ts-list-types'; +import { useEffect, useRef, useState } from 'react'; -import type { RuleReferenceSchema } from '../../../../common/detection_engine/schemas/response'; +import type { ExceptionListRuleReferencesSchema } 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, boolean, RuleReferences | null]; - export interface RuleReferences { - [key: string]: RuleReferenceSchema[]; + [key: string]: ExceptionListRuleReferencesSchema; } + +export type FetchReferencesFunc = ( + listsToFetch: FindRulesReferencedByExceptionsListProp[] +) => Promise; + +export type ReturnUseFindExceptionListReferences = [ + boolean, + boolean, + RuleReferences | null, + FetchReferencesFunc | null +]; + /** * 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 => { +export const useFindExceptionListReferences = (): ReturnUseFindExceptionListReferences => { const toasts = useToasts(); - const [isLoading, setIsLoading] = useState(false); - const [errorExists, setErrorExists] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [didError, setError] = 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]); + const findExceptionListAndReferencesRef = useRef(null); useEffect(() => { let isSubscribed = true; const abortCtrl = new AbortController(); - const findReferences = async () => { + const findReferences = async (listsToFetch: FindRulesReferencedByExceptionsListProp[]) => { try { setIsLoading(true); const { references: referencesResults } = await findRuleExceptionReferences({ - lists: listRefs, + lists: listsToFetch, signal: abortCtrl.signal, }); @@ -62,32 +60,26 @@ export const useFindExceptionListReferences = ( }, {}); if (isSubscribed) { - setErrorExists(false); setIsLoading(false); + setError(false); setReferences(results); } } catch (error) { if (isSubscribed) { - setErrorExists(true); + setError(true); setIsLoading(false); toasts.addError(error, { title: i18n.ERROR_FETCHING_REFERENCES_TITLE }); } } }; - if (listRefs.length === 0 && isSubscribed) { - setErrorExists(false); - setIsLoading(false); - setReferences(null); - } else { - findReferences(); - } + findExceptionListAndReferencesRef.current = findReferences; return (): void => { isSubscribed = false; abortCtrl.abort(); }; - }, [ruleExceptionLists, listRefs, toasts]); + }, [toasts]); - return [isLoading, errorExists, references]; + return [isLoading, didError, references, findExceptionListAndReferencesRef.current]; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx index e4087567de39c..f876f11be9e3d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx @@ -23,6 +23,7 @@ import type { ExceptionListItemSchema, CreateExceptionListItemSchema, UpdateExceptionListItemSchema, + ExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { comment, @@ -30,7 +31,10 @@ import { ListOperatorTypeEnum as OperatorTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; -import type { ExceptionsBuilderExceptionItem } from '@kbn/securitysolution-list-utils'; +import type { + ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, +} from '@kbn/securitysolution-list-utils'; import { getOperatorType, getNewExceptionItem, @@ -169,10 +173,10 @@ export const prepareExceptionItemsForBulkClose = ( * @param comments new Comment */ export const enrichNewExceptionItemsWithComments = ( - exceptionItems: Array, + exceptionItems: ExceptionsBuilderReturnExceptionItem[], comments: Array -): Array => { - return exceptionItems.map((item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptionItems.map((item: ExceptionsBuilderReturnExceptionItem) => { return { ...item, comments, @@ -197,9 +201,9 @@ export const buildGetAlertByIdQuery = (id: string | undefined) => ({ * and new comments */ export const enrichExistingExceptionItemWithComments = ( - exceptionItem: ExceptionListItemSchema | CreateExceptionListItemSchema, + exceptionItem: ExceptionsBuilderReturnExceptionItem, comments: Array -): ExceptionListItemSchema | CreateExceptionListItemSchema => { +): ExceptionsBuilderReturnExceptionItem => { const formattedComments = comments.map((item) => { if (comment.is(item)) { const { id, comment: existingComment } = item; @@ -226,10 +230,10 @@ export const enrichExistingExceptionItemWithComments = ( * @param osTypes array of os values */ export const enrichExceptionItemsWithOS = ( - exceptionItems: Array, + exceptionItems: ExceptionsBuilderReturnExceptionItem[], osTypes: OsTypeArray -): Array => { - return exceptionItems.map((item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptionItems.map((item: ExceptionsBuilderReturnExceptionItem) => { return { ...item, os_types: osTypes, @@ -255,8 +259,8 @@ export const retrieveAlertOsTypes = (alertData?: AlertData): OsTypeArray => { * Returns given exceptionItems with all hash-related entries lowercased */ export const lowercaseHashValues = ( - exceptionItems: Array -): Array => { + exceptionItems: ExceptionsBuilderReturnExceptionItem[] +): ExceptionsBuilderReturnExceptionItem[] => { return exceptionItems.map((item) => { const newEntries = item.entries.map((itemEntry) => { if (itemEntry.field.includes('.hash')) { @@ -281,9 +285,7 @@ export const lowercaseHashValues = ( }); }; -export const entryHasListType = ( - exceptionItems: Array -) => { +export const entryHasListType = (exceptionItems: ExceptionsBuilderReturnExceptionItem[]) => { for (const { entries } of exceptionItems) { for (const exceptionEntry of entries ?? []) { if (getOperatorType(exceptionEntry) === OperatorTypeEnum.LIST) { @@ -755,7 +757,7 @@ export const getPrepopulatedBehaviorException = ({ * Determines whether or not any entries within the given exceptionItems contain values not in the specified ECS mapping */ export const entryHasNonEcsType = ( - exceptionItems: Array, + exceptionItems: ExceptionsBuilderReturnExceptionItem[], indexPatterns: DataViewBase ): boolean => { const doesFieldNameExist = (exceptionEntry: Entry): boolean => { @@ -842,3 +844,57 @@ export const defaultEndpointExceptionItems = ( ); } }; + +/** + * Adds user defined name to all new exceptionItems + * @param exceptionItems new or existing ExceptionItem[] + * @param name new exception item name + */ +export const enrichNewExceptionItemsWithName = ( + exceptionItems: ExceptionsBuilderReturnExceptionItem[], + name: string +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptionItems.map((item: ExceptionsBuilderReturnExceptionItem) => { + return { + ...item, + name, + }; + }); +}; + +/** + * Modifies exception items to prepare for creating as rule_default + * list items + * @param exceptionItems new or existing ExceptionItem[] + */ +export const enrichRuleExceptions = ( + exceptionItems: ExceptionsBuilderReturnExceptionItem[] +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptionItems.map((item: ExceptionsBuilderReturnExceptionItem) => { + return { + ...item, + list_id: undefined, + namespace_type: 'single', + }; + }); +}; + +/** + * Prepares items to be added to shared exception lists + * @param exceptionItems new or existing ExceptionItem[] + * @param lists shared exception lists that were selected to add items to + */ +export const enrichSharedExceptions = ( + exceptionItems: ExceptionsBuilderReturnExceptionItem[], + lists: ExceptionListSchema[] +): ExceptionsBuilderReturnExceptionItem[] => { + return lists.flatMap((list) => { + return exceptionItems.map((item) => { + return { + ...item, + list_id: list.list_id, + namespace_type: list.namespace_type, + }; + }); + }); +}; 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 63754adc5e7c8..1a1238c36ca34 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 @@ -386,16 +386,23 @@ export const fetchInstalledIntegrations = async ({ export const findRuleExceptionReferences = async ({ lists, signal, -}: FindRulesReferencedByExceptionsProps): Promise => - KibanaServices.get().http.fetch( - DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, - { - method: 'GET', - query: { +}: FindRulesReferencedByExceptionsProps): Promise => { + const idsUndefined = lists.some(({ id }) => id === undefined); + const query = idsUndefined + ? { + namespace_types: lists.map(({ namespaceType }) => namespaceType).join(','), + } + : { ids: lists.map(({ id }) => id).join(','), list_ids: lists.map(({ listId }) => listId).join(','), namespace_types: lists.map(({ namespaceType }) => namespaceType).join(','), - }, + }; + return KibanaServices.get().http.fetch( + DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL, + { + method: 'GET', + query, 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 77811b3d8aa2d..842353a6799f8 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 @@ -344,8 +344,8 @@ export interface PrePackagedRulesStatusResponse { } export interface FindRulesReferencedByExceptionsListProp { - id: string; - listId: string; + id?: string; + listId?: string; namespaceType: NamespaceType; } diff --git a/x-pack/plugins/security_solution/public/landing_pages/links.ts b/x-pack/plugins/security_solution/public/landing_pages/links.ts index 30b2e22a0faed..479b6f1843bf5 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/links.ts +++ b/x-pack/plugins/security_solution/public/landing_pages/links.ts @@ -40,7 +40,6 @@ export const dashboardsLandingLinks: LinkItem = { entityAnalyticsLinks, ], skipUrlState: true, - hideTimeline: true, }; export const threatHuntingLandingLinks: LinkItem = { @@ -56,5 +55,4 @@ export const threatHuntingLandingLinks: LinkItem = { ], links: [hostsLinks, networkLinks, usersLinks], skipUrlState: true, - hideTimeline: true, }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx index 747d0990b8fc0..eb1e3dc746c7c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx @@ -6,13 +6,117 @@ */ import React, { memo } from 'react'; -import type { PackagePolicyCreateMultiStepExtensionComponentProps } from '@kbn/fleet-plugin/public'; +import { + EuiText, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiPanel, + EuiSpacer, + useEuiTheme, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { css } from '@emotion/css'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '../../../../../common/lib/kibana'; + +const CenteredEuiFlexItem = styled(EuiFlexItem)` + align-items: center; +`; /** - * TBD: A component to be displayed in multi step onboarding process. + * A component to be displayed in multi step onboarding process. */ -export const EndpointPolicyCreateMultiStepExtension = - memo(({ newPolicy }) => { - return <>; - }); +export const EndpointPolicyCreateMultiStepExtension = memo(() => { + const { docLinks } = useKibana().services; + const { size } = useEuiTheme().euiTheme; + + const title = ( + +

+ +

+
+ ); + + const logoSize = `calc(2 * ${size.xxxxl})`; + const securityLogo = ( + + ); + + const features = ( + + + + + + + +

+ +

+
+
+
+ ); + + const details = ( + +

+ +   + + + +

+
+ ); + + return ( + + + + {title} + + + {securityLogo} + + +
{features}
+
+
+ + {details} + + +
+ ); +}); EndpointPolicyCreateMultiStepExtension.displayName = 'EndpointPolicyCreateMultiStepExtension'; diff --git a/x-pack/plugins/security_solution/public/risk_score/components/enable_risk_score/translations.ts b/x-pack/plugins/security_solution/public/risk_score/components/enable_risk_score/translations.ts index 058898469f2e6..114f809eb4ad0 100644 --- a/x-pack/plugins/security_solution/public/risk_score/components/enable_risk_score/translations.ts +++ b/x-pack/plugins/security_solution/public/risk_score/components/enable_risk_score/translations.ts @@ -27,7 +27,7 @@ export const UPGRADE_RISK_SCORE_DESCRIPTION = i18n.translate( 'xpack.securitySolution.riskDeprecated.entity.upgradeRiskScoreDescription', { defaultMessage: - 'Current data is no longer supported. Please migrate your data and upgrade the module.', + 'Current data is no longer supported. Please migrate your data and upgrade the module. The data might need an hour to be generated after enabling the module.', } ); @@ -42,7 +42,7 @@ export const ENABLE_RISK_SCORE = (riskEntity: RiskScoreEntity) => export const ENABLE_RISK_SCORE_DESCRIPTION = (riskEntity: RiskScoreEntity) => i18n.translate('xpack.securitySolution.enableRiskScore.enableRiskScoreDescription', { defaultMessage: - 'Once you have enabled this feature you can get quick access to the {riskEntity} risk scores in this section.', + 'Once you have enabled this feature you can get quick access to the {riskEntity} risk scores in this section. The data might need an hour to be generated after enabling the module.', values: { riskEntity: getRiskEntityTranslation(riskEntity, true), }, diff --git a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/risk_score_restart_button.tsx b/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/risk_score_restart_button.tsx index 801e5763d7c72..7f4316b77ee97 100644 --- a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/risk_score_restart_button.tsx +++ b/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/risk_score_restart_button.tsx @@ -28,10 +28,10 @@ const RiskScoreRestartButtonComponent = ({ REQUEST_NAMES.REFRESH_RISK_SCORE, restartRiskScoreTransforms ); - const spaceId = useSpaceId(); + const { renderDocLink } = useRiskScoreToastContent(riskScoreEntity); - const { http, notifications, theme } = useKibana().services; + const { http, notifications } = useKibana().services; const onClick = useCallback(async () => { fetch({ @@ -41,9 +41,8 @@ const RiskScoreRestartButtonComponent = ({ renderDocLink, riskScoreEntity, spaceId, - theme, }); - }, [fetch, http, notifications, refetch, renderDocLink, riskScoreEntity, spaceId, theme]); + }, [fetch, http, notifications, refetch, renderDocLink, riskScoreEntity, spaceId]); return ( { - beforeAll(async () => { - await installRiskScoreModule({ - http: mockHttp, - refetch: mockRefetch, - spaceId: mockSpaceId, - timerange: mockTimerange, - riskScoreEntity: RiskScoreEntity.host, - }); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - it(`Create script: ml_${RiskScoreEntity.host}riskscore_levels_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create script: ml_${RiskScoreEntity.host}riskscore_init_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); - - it(`Create script: ml_${RiskScoreEntity.host}riskscore_map_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[2][0].options).toMatchSnapshot(); - }); - - it(`Create script: ml_${RiskScoreEntity.host}riskscore_reduce_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[3][0].options).toMatchSnapshot(); - }); - - it(`Create IngestPipeline: ml_${RiskScoreEntity.host}riskscore_ingest_pipeline_${mockSpaceId}`, async () => { - expect((api.createIngestPipeline as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create Index: ml_${RiskScoreEntity.host}_risk_score_${mockSpaceId}`, async () => { - expect((api.createIndices as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create Index: ml_${RiskScoreEntity.host}_risk_score_latest_${mockSpaceId}`, async () => { - expect((api.createIndices as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); - - it(`Create Transform: ml_${RiskScoreEntity.host}riskscore_pivot_transform_${mockSpaceId}`, async () => { - expect((api.createTransform as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create Transform: ml_${RiskScoreEntity.host}riskscore_latest_transform_${mockSpaceId}`, async () => { - expect((api.createTransform as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); - - it(`Start Transforms`, () => { - expect((api.startTransforms as jest.Mock).mock.calls[0][0].transformIds).toMatchSnapshot(); - }); - - it(`Create ${RiskScoreEntity.host} dashboards`, () => { - expect( - (bulkCreatePrebuiltSavedObjects as jest.Mock).mock.calls[0][0].options.templateName - ).toEqual(`${RiskScoreEntity.host}RiskScoreDashboards`); - }); - - it('Refresh module', () => { - expect(mockRefetch).toBeCalled(); - }); -}); - -describe(`installRiskScoreModule - ${RiskScoreEntity.user}`, () => { - beforeAll(async () => { - await installRiskScoreModule({ - http: mockHttp, - refetch: mockRefetch, - spaceId: mockSpaceId, - timerange: mockTimerange, - riskScoreEntity: RiskScoreEntity.user, +describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( + `installRiskScoreModule - %s`, + (riskScoreEntity) => { + beforeAll(async () => { + await installRiskScoreModule({ + http: mockHttp, + refetch: mockRefetch, + spaceId: mockSpaceId, + timerange: mockTimerange, + riskScoreEntity, + }); }); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - it(`Create script: ml_${RiskScoreEntity.user}riskscore_levels_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create script: ml_${RiskScoreEntity.user}riskscore_map_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); - - it(`Create script: ml_${RiskScoreEntity.user}riskscore_reduce_script_${mockSpaceId}`, async () => { - expect((api.createStoredScript as jest.Mock).mock.calls[2][0].options).toMatchSnapshot(); - }); - - it(`Create IngestPipeline: ml_${RiskScoreEntity.user}riskscore_ingest_pipeline_${mockSpaceId}`, async () => { - expect((api.createIngestPipeline as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create Index: ml_${RiskScoreEntity.user}_risk_score_${mockSpaceId}`, async () => { - expect((api.createIndices as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - - it(`Create Index: ml_${RiskScoreEntity.user}_risk_score_latest_${mockSpaceId}`, async () => { - expect((api.createIndices as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); - - it(`Create Transform: ml_${RiskScoreEntity.user}riskscore_pivot_transform_${mockSpaceId}`, async () => { - expect((api.createTransform as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); - }); - it(`Create Transform: ml_${RiskScoreEntity.user}riskscore_latest_transform_${mockSpaceId}`, async () => { - expect((api.createTransform as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); - }); + afterAll(() => { + jest.clearAllMocks(); + }); - it(`Start Transforms`, () => { - expect((api.startTransforms as jest.Mock).mock.calls[0][0].transformIds).toMatchSnapshot(); - }); + it(`installRiskScore`, () => { + expect((api.installRiskScore as jest.Mock).mock.calls[0][0].options.riskScoreEntity).toEqual( + riskScoreEntity + ); + }); - it(`Create Users dashboards`, () => { - expect( - (bulkCreatePrebuiltSavedObjects as jest.Mock).mock.calls[0][0].options.templateName - ).toEqual(`${RiskScoreEntity.user}RiskScoreDashboards`); - }); + it(`Create ${riskScoreEntity} dashboards`, () => { + expect( + (bulkCreatePrebuiltSavedObjects as jest.Mock).mock.calls[0][0].options.templateName + ).toEqual(`${riskScoreEntity}RiskScoreDashboards`); + }); - it('Refresh module', () => { - expect(mockRefetch).toBeCalled(); - }); -}); + it('Refresh module', () => { + expect(mockRefetch).toBeCalled(); + }); + } +); describe.each([[RiskScoreEntity.host], [RiskScoreEntity.user]])( 'uninstallLegacyRiskScoreModule - %s', @@ -225,9 +132,9 @@ describe.each([[RiskScoreEntity.host], [RiskScoreEntity.user]])( beforeAll(async () => { await restartRiskScoreTransforms({ http: mockHttp, - spaceId: 'customSpace', refetch: mockRefetch, riskScoreEntity, + spaceId: mockSpaceId, }); }); diff --git a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/utils.ts b/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/utils.ts index 5adfcd6b4e514..beceb9edbb7b5 100644 --- a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/utils.ts +++ b/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/utils.ts @@ -11,28 +11,22 @@ import * as utils from '../../../../common/utils/risk_score_modules'; import type { inputsModel } from '../../../common/store'; import { - createIngestPipeline, - createIndices, - createStoredScript, - createTransform, - startTransforms, deleteStoredScripts, deleteTransforms, deleteIngestPipelines, - stopTransforms, - bulkCreatePrebuiltSavedObjects, bulkDeletePrebuiltSavedObjects, + installRiskScore, + bulkCreatePrebuiltSavedObjects, + stopTransforms, + startTransforms, } from '../../containers/onboarding/api'; import { INGEST_PIPELINE_DELETION_ERROR_MESSAGE, - INSTALLATION_ERROR, - START_TRANSFORMS_ERROR_MESSAGE, - TRANSFORM_CREATION_ERROR_MESSAGE, TRANSFORM_DELETION_ERROR_MESSAGE, UNINSTALLATION_ERROR, } from '../../containers/onboarding/api/translations'; -interface InstallRiskyScoreModule { +interface InstallRiskScoreModule { dashboard?: DashboardStart; http: HttpSetup; notifications?: NotificationsStart; @@ -48,7 +42,7 @@ interface InstallRiskyScoreModule { }; } -type UpgradeRiskyScoreModule = InstallRiskyScoreModule; +type UpgradeRiskScoreModule = InstallRiskScoreModule; const installHostRiskScoreModule = async ({ dashboard, @@ -57,157 +51,16 @@ const installHostRiskScoreModule = async ({ refetch, renderDashboardLink, renderDocLink, - spaceId = 'default', theme, timerange, -}: InstallRiskyScoreModule) => { - /** - * console_templates/enable_host_risk_score.console - * Step 1 Upload script: ml_hostriskscore_levels_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskHostCreateLevelScriptOptions(spaceId), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 2 Upload script: ml_hostriskscore_init_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskHostCreateInitScriptOptions(spaceId), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 3 Upload script: ml_hostriskscore_map_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskHostCreateMapScriptOptions(spaceId), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 4 Upload script: ml_hostriskscore_reduce_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskHostCreateReduceScriptOptions(spaceId), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 5 Upload the ingest pipeline: ml_hostriskscore_ingest_pipeline_{spaceId} - */ - await createIngestPipeline({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskScoreIngestPipelineOptions(RiskScoreEntity.host, spaceId), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 6 create ml_host_risk_score_{spaceId} index - */ - await createIndices({ +}: InstallRiskScoreModule) => { + await installRiskScore({ http, - theme, renderDocLink, notifications, - options: utils.getCreateRiskScoreIndicesOptions({ - spaceId, - riskScoreEntity: RiskScoreEntity.host, - }), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 7 create transform: ml_hostriskscore_pivot_transform_{spaceId} - */ - await createTransform({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${INSTALLATION_ERROR} - ${TRANSFORM_CREATION_ERROR_MESSAGE}`, - transformId: utils.getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId), - options: utils.getCreateMLHostPivotTransformOptions({ spaceId }), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 9 create ml_host_risk_score_latest_{spaceId} index - */ - await createIndices({ - http, - theme, - renderDocLink, - notifications, - options: utils.getCreateRiskScoreLatestIndicesOptions({ - spaceId, - riskScoreEntity: RiskScoreEntity.host, - }), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 10 create transform: ml_hostriskscore_latest_transform_{spaceId} - */ - await createTransform({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${INSTALLATION_ERROR} - ${TRANSFORM_CREATION_ERROR_MESSAGE}`, - transformId: utils.getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId), - options: utils.getCreateLatestTransformOptions({ - spaceId, + options: { riskScoreEntity: RiskScoreEntity.host, - }), - }); - - /** - * console_templates/enable_host_risk_score.console - * Step 8 Start the pivot transform - * Step 11 Start the latest transform - */ - const transformIds = [ - utils.getRiskScorePivotTransformId(RiskScoreEntity.host, spaceId), - utils.getRiskScoreLatestTransformId(RiskScoreEntity.host, spaceId), - ]; - await startTransforms({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${INSTALLATION_ERROR} - ${START_TRANSFORMS_ERROR_MESSAGE(transformIds.length)}`, - transformIds, - }); - - await restartRiskScoreTransforms({ - http, - notifications, - refetch, - renderDocLink, - riskScoreEntity: RiskScoreEntity.host, - spaceId, - theme, + }, }); // Install dashboards and relevant saved objects @@ -239,144 +92,14 @@ const installUserRiskScoreModule = async ({ spaceId = 'default', theme, timerange, -}: InstallRiskyScoreModule) => { - /** - * console_templates/enable_user_risk_score.console - * Step 1 Upload script: ml_userriskscore_levels_script_{spaceId} - */ - await createStoredScript({ +}: InstallRiskScoreModule) => { + await installRiskScore({ http, - theme, renderDocLink, notifications, - options: utils.getRiskUserCreateLevelScriptOptions(spaceId), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 2 Upload script: ml_userriskscore_map_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskUserCreateMapScriptOptions(spaceId), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 3 Upload script: ml_userriskscore_reduce_script_{spaceId} - */ - await createStoredScript({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskUserCreateReduceScriptOptions(spaceId), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 4 Upload ingest pipeline: ml_userriskscore_ingest_pipeline_{spaceId} - */ - await createIngestPipeline({ - http, - theme, - renderDocLink, - notifications, - options: utils.getRiskScoreIngestPipelineOptions(RiskScoreEntity.user, spaceId), - }); - /** - * console_templates/enable_user_risk_score.console - * Step 5 create ml_user_risk_score_{spaceId} index - */ - await createIndices({ - http, - theme, - renderDocLink, - notifications, - options: utils.getCreateRiskScoreIndicesOptions({ - spaceId, - riskScoreEntity: RiskScoreEntity.user, - }), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 6 create Transform: ml_userriskscore_pivot_transform_{spaceId} - */ - await createTransform({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${INSTALLATION_ERROR} - ${TRANSFORM_CREATION_ERROR_MESSAGE}`, - transformId: utils.getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId), - options: utils.getCreateMLUserPivotTransformOptions({ spaceId }), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 8 create ml_user_risk_score_latest_{spaceId} index - */ - await createIndices({ - http, - theme, - renderDocLink, - notifications, - options: utils.getCreateRiskScoreLatestIndicesOptions({ - spaceId, - riskScoreEntity: RiskScoreEntity.user, - }), - }); - - /** - * console_templates/enable_user_risk_score.console - * Step 9 create Transform: ml_userriskscore_latest_transform_{spaceId} - */ - await createTransform({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${INSTALLATION_ERROR} - ${TRANSFORM_CREATION_ERROR_MESSAGE}`, - transformId: utils.getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId), - options: utils.getCreateLatestTransformOptions({ - spaceId, + options: { riskScoreEntity: RiskScoreEntity.user, - }), - }); - /** - * console_templates/enable_user_risk_score.console - * Step 7 Start the pivot transform - * Step 10 Start the latest transform - */ - const transformIds = [ - utils.getRiskScorePivotTransformId(RiskScoreEntity.user, spaceId), - utils.getRiskScoreLatestTransformId(RiskScoreEntity.user, spaceId), - ]; - await startTransforms({ - errorMessage: `${INSTALLATION_ERROR} - ${START_TRANSFORMS_ERROR_MESSAGE(transformIds.length)}`, - http, - notifications, - renderDocLink, - theme, - transformIds, - }); - - /** - * Restart transform immediately to force it pick up the alerts data. - * This can effectively reduce the chance of no data appears once installation complete. - * */ - await restartRiskScoreTransforms({ - http, - notifications, - refetch, - renderDocLink, - riskScoreEntity: RiskScoreEntity.user, - spaceId, - theme, + }, }); // Install dashboards and relevant saved objects @@ -398,7 +121,7 @@ const installUserRiskScoreModule = async ({ } }; -export const installRiskScoreModule = async (settings: InstallRiskyScoreModule) => { +export const installRiskScoreModule = async (settings: InstallRiskScoreModule) => { if (settings.riskScoreEntity === RiskScoreEntity.user) { await installUserRiskScoreModule(settings); } else { @@ -442,68 +165,67 @@ export const uninstallLegacyRiskScoreModule = async ({ const legacyIngestPipelineNames = [utils.getLegacyIngestPipelineName(riskScoreEntity)]; - /** - * Intended not to pass notification to bulkDeletePrebuiltSavedObjects. - * As the only error it can happen is saved object not found, and - * that is what bulkDeletePrebuiltSavedObjects wants. - * (Before 8.5 once an saved object was created, it was shared across different spaces. - * If it has been upgrade in one space, "saved object not found" will happen when upgrading other spaces. - * Or it could be users manually deleted the saved object.) - */ - await bulkDeletePrebuiltSavedObjects({ - http, - options: { - templateName: `${riskScoreEntity}RiskScoreDashboards`, - }, - }); - - await deleteTransforms({ - http, - theme, - renderDocLink, - notifications, - errorMessage: `${UNINSTALLATION_ERROR} - ${TRANSFORM_DELETION_ERROR_MESSAGE( - legacyTransformIds.length - )}`, - transformIds: legacyTransformIds, - options: { - deleteDestIndex: true, - deleteDestDataView: true, - forceDelete: false, - }, - }); - - /** - * Intended not to pass notification to deleteIngestPipelines. - * As the only error it can happen is ingest pipeline not found, and - * that is what deleteIngestPipelines wants. - * (Before 8.5 once an ingest pipeline was created, it was shared across different spaces. - * If it has been upgrade in one space, "ingest pipeline not found" will happen when upgrading other spaces. - * Or it could be users manually deleted the ingest pipeline.) - */ - await deleteIngestPipelines({ - http, - errorMessage: `${UNINSTALLATION_ERROR} - ${INGEST_PIPELINE_DELETION_ERROR_MESSAGE( - legacyIngestPipelineNames.length - )}`, - names: legacyIngestPipelineNames.join(','), - }); - - /** - * Intended not to pass notification to deleteStoredScripts. - * As the only error it can happen is script not found, and - * that is what deleteStoredScripts wants. - * (Before 8.5 once a script was created, it was shared across different spaces. - * If it has been upgrade in one space, "script not found" will happen when upgrading other spaces. - * Or it could be users manually deleted the script.) - */ - await deleteStoredScripts({ - http, - ids: - riskScoreEntity === RiskScoreEntity.user - ? legacyRiskScoreUsersScriptIds - : legacyRiskScoreHostsScriptIds, - }); + await Promise.all([ + /** + * Intended not to pass notification to bulkDeletePrebuiltSavedObjects. + * As the only error it can happen is saved object not found, and + * that is what bulkDeletePrebuiltSavedObjects wants. + * (Before 8.5 once an saved object was created, it was shared across different spaces. + * If it has been upgrade in one space, "saved object not found" will happen when upgrading other spaces. + * Or it could be users manually deleted the saved object.) + */ + bulkDeletePrebuiltSavedObjects({ + http, + options: { + templateName: `${riskScoreEntity}RiskScoreDashboards`, + }, + }), + deleteTransforms({ + http, + theme, + renderDocLink, + notifications, + errorMessage: `${UNINSTALLATION_ERROR} - ${TRANSFORM_DELETION_ERROR_MESSAGE( + legacyTransformIds.length + )}`, + transformIds: legacyTransformIds, + options: { + deleteDestIndex: true, + deleteDestDataView: true, + forceDelete: false, + }, + }), + /** + * Intended not to pass notification to deleteIngestPipelines. + * As the only error it can happen is ingest pipeline not found, and + * that is what deleteIngestPipelines wants. + * (Before 8.5 once an ingest pipeline was created, it was shared across different spaces. + * If it has been upgrade in one space, "ingest pipeline not found" will happen when upgrading other spaces. + * Or it could be users manually deleted the ingest pipeline.) + */ + deleteIngestPipelines({ + http, + errorMessage: `${UNINSTALLATION_ERROR} - ${INGEST_PIPELINE_DELETION_ERROR_MESSAGE( + legacyIngestPipelineNames.length + )}`, + names: legacyIngestPipelineNames.join(','), + }), + /** + * Intended not to pass notification to deleteStoredScripts. + * As the only error it can happen is script not found, and + * that is what deleteStoredScripts wants. + * (Before 8.5 once a script was created, it was shared across different spaces. + * If it has been upgrade in one space, "script not found" will happen when upgrading other spaces. + * Or it could be users manually deleted the script.) + */ + deleteStoredScripts({ + http, + ids: + riskScoreEntity === RiskScoreEntity.user + ? legacyRiskScoreUsersScriptIds + : legacyRiskScoreHostsScriptIds, + }), + ]); if (refetch) { refetch(); @@ -520,7 +242,7 @@ export const upgradeHostRiskScoreModule = async ({ spaceId = 'default', theme, timerange, -}: UpgradeRiskyScoreModule) => { +}: UpgradeRiskScoreModule) => { await uninstallLegacyRiskScoreModule({ http, notifications, @@ -553,7 +275,7 @@ export const upgradeUserRiskScoreModule = async ({ spaceId = 'default', theme, timerange, -}: UpgradeRiskyScoreModule) => { +}: UpgradeRiskScoreModule) => { await uninstallLegacyRiskScoreModule({ http, notifications, @@ -583,7 +305,6 @@ export const restartRiskScoreTransforms = async ({ renderDocLink, riskScoreEntity, spaceId, - theme, }: { http: HttpSetup; notifications?: NotificationsStart; @@ -591,7 +312,6 @@ export const restartRiskScoreTransforms = async ({ renderDocLink?: (message: string) => React.ReactNode; riskScoreEntity: RiskScoreEntity; spaceId?: string; - theme?: ThemeServiceStart; }) => { const transformIds = [ utils.getRiskScorePivotTransformId(riskScoreEntity, spaceId), @@ -602,7 +322,6 @@ export const restartRiskScoreTransforms = async ({ http, notifications, renderDocLink, - theme, transformIds, }); @@ -610,7 +329,6 @@ export const restartRiskScoreTransforms = async ({ http, notifications, renderDocLink, - theme, transformIds, }); diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/index.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/index.ts index dc36a4be30a92..cc25b1c874a89 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/index.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/index.ts @@ -9,3 +9,4 @@ export * from './ingest_pipelines'; export * from './transforms'; export * from './stored_scripts'; export * from './saved_objects'; +export * from './onboarding'; diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/onboarding.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/onboarding.ts new file mode 100644 index 0000000000000..a7ff58cd76fe1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/onboarding.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 { HttpSetup, NotificationsStart } from '@kbn/core/public'; + +import { INTERNAL_RISK_SCORE_URL } from '../../../../../common/constants'; + +import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import { + HOST_RISK_SCORES_ENABLED_TITLE, + INSTALLATION_ERROR, + RISK_SCORES_ENABLED_TEXT, + USER_RISK_SCORES_ENABLED_TITLE, +} from './translations'; + +interface Options { + riskScoreEntity: RiskScoreEntity; +} + +type Response = Record; +const toastLifeTimeMs = 600000; + +export const installRiskScore = ({ + errorMessage, + http, + notifications, + options, + renderDocLink, + signal, +}: { + errorMessage?: string; + http: HttpSetup; + notifications?: NotificationsStart; + options: Options; + renderDocLink?: (message: string) => React.ReactNode; + signal?: AbortSignal; +}) => { + return http + .post(INTERNAL_RISK_SCORE_URL, { + body: JSON.stringify(options), + signal, + }) + .then((result) => { + const resp = result.reduce( + (acc, curr) => { + const [[key, res]] = Object.entries(curr); + if (res.success) { + return res.success != null ? { ...acc, success: [...acc.success, `${key}`] } : acc; + } else { + return res.error != null + ? { ...acc, error: [...acc.error, `${key}: ${res?.error?.message}`] } + : acc; + } + }, + { success: [] as string[], error: [] as string[] } + ); + + if (resp.error.length > 0) { + notifications?.toasts?.addError(new Error(errorMessage ?? INSTALLATION_ERROR), { + title: errorMessage ?? INSTALLATION_ERROR, + toastMessage: renderDocLink + ? (renderDocLink(resp.error.join(', ')) as unknown as string) + : resp.error.join(', '), + toastLifeTimeMs, + }); + } else { + notifications?.toasts?.addSuccess({ + 'data-test-subj': `${options.riskScoreEntity}EnableSuccessToast`, + title: + options.riskScoreEntity === RiskScoreEntity.user + ? USER_RISK_SCORES_ENABLED_TITLE + : HOST_RISK_SCORES_ENABLED_TITLE, + text: RISK_SCORES_ENABLED_TEXT(resp.success.join(', ')), + }); + } + }) + .catch((e) => { + notifications?.toasts?.addError(new Error(errorMessage ?? INSTALLATION_ERROR), { + title: errorMessage ?? INSTALLATION_ERROR, + toastMessage: renderDocLink ? renderDocLink(e?.body?.message) : e?.body?.message, + toastLifeTimeMs, + }); + }); +}; diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts index 5907e6b78c3b9..06c7157da472b 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/saved_objects.ts @@ -5,13 +5,7 @@ * 2.0. */ -import type { - HttpSetup, - NotificationsStart, - SavedObject, - SavedObjectAttributes, - ThemeServiceStart, -} from '@kbn/core/public'; +import type { HttpSetup, NotificationsStart, ThemeServiceStart } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import { RISKY_HOSTS_DASHBOARD_TITLE, RISKY_USERS_DASHBOARD_TITLE } from '../../../constants'; @@ -30,8 +24,10 @@ import { const toastLifeTimeMs = 600000; +type DashboardsSavedObjectTemplate = `${RiskScoreEntity}RiskScoreDashboards`; + interface Options { - templateName: string; + templateName: DashboardsSavedObjectTemplate; } export const bulkCreatePrebuiltSavedObjects = async ({ @@ -58,20 +54,24 @@ export const bulkCreatePrebuiltSavedObjects = async ({ theme?: ThemeServiceStart; }) => { const res = await http - .post<{ saved_objects: Array> }>( - prebuiltSavedObjectsBulkCreateUrl(options.templateName) - ) + .post< + Record< + DashboardsSavedObjectTemplate, + { + success?: boolean; + error: Error; + body?: Array<{ type: string; title: string; id: string; name: string }>; + } + > + >(prebuiltSavedObjectsBulkCreateUrl(options.templateName)) .then((result) => { - const errors = result.saved_objects.reduce((acc, o) => { - return o.error != null ? [...acc, `${o.id}: ${o.error.message}`] : acc; - }, []); + const response = result[options.templateName]; + const error = response?.error?.message; - if (errors.length > 0) { + if (error) { notifications?.toasts?.addError(new Error(errorMessage ?? IMPORT_SAVED_OBJECTS_FAILURE), { title: errorMessage ?? IMPORT_SAVED_OBJECTS_FAILURE, - toastMessage: renderDocLink - ? (renderDocLink(errors.join(', ')) as unknown as string) - : errors.join(', '), + toastMessage: renderDocLink ? (renderDocLink(error) as unknown as string) : error, toastLifeTimeMs, }); } else { @@ -80,8 +80,8 @@ export const bulkCreatePrebuiltSavedObjects = async ({ ? RISKY_USERS_DASHBOARD_TITLE : RISKY_HOSTS_DASHBOARD_TITLE; - const targetDashboard = result.saved_objects.find( - (obj) => obj.type === 'dashboard' && obj?.attributes?.title === dashboardTitle + const targetDashboard = response?.body?.find( + (obj) => obj.type === 'dashboard' && obj?.title === dashboardTitle ); let targetUrl; @@ -95,12 +95,15 @@ export const bulkCreatePrebuiltSavedObjects = async ({ }); } - const successMessage = result.saved_objects - .map((o) => o?.attributes?.title ?? o?.attributes?.name) - .join(', '); + const successMessage = response?.body?.map((o) => o?.title ?? o?.name).join(', '); + + if (successMessage == null || response?.body?.length == null) { + return; + } notifications?.toasts?.addSuccess({ - title: IMPORT_SAVED_OBJECTS_SUCCESS(result.saved_objects.length), + 'data-test-subj': `${options.templateName}SuccessToast`, + title: IMPORT_SAVED_OBJECTS_SUCCESS(response?.body?.length), text: toMountPoint( renderDashboardLink && targetUrl ? renderDashboardLink(successMessage, targetUrl) @@ -109,6 +112,7 @@ export const bulkCreatePrebuiltSavedObjects = async ({ theme$: theme?.theme$, } ), + toastLifeTimeMs, }); } }) diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/transforms.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/transforms.ts index da6dd0d7ddf6c..f237ee82af87d 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/transforms.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/transforms.ts @@ -5,9 +5,11 @@ * 2.0. */ +import { RISK_SCORE_RESTART_TRANSFORMS } from '../../../../../common/constants'; import { GET_TRANSFORM_STATE_ERROR_MESSAGE, GET_TRANSFORM_STATE_NOT_FOUND_MESSAGE, + RESTART_TRANSFORMS_ERROR_MESSAGE, START_TRANSFORMS_ERROR_MESSAGE, STOP_TRANSFORMS_ERROR_MESSAGE, TRANSFORM_CREATION_ERROR_MESSAGE, @@ -20,6 +22,8 @@ import type { DeleteTransformsResult, GetTransformsState, GetTransformState, + RestartTransforms, + RestartTransformResult, StartTransforms, StartTransformsResult, StopTransforms, @@ -318,3 +322,49 @@ export async function deleteTransforms({ return res; } + +export async function restartTransforms({ + http, + notifications, + renderDocLink, + signal, + errorMessage, + riskScoreEntity, +}: RestartTransforms) { + const res = await http + .post(`${RISK_SCORE_RESTART_TRANSFORMS}`, { + body: JSON.stringify({ riskScoreEntity }), + signal, + }) + .then((result) => { + const failedIds = result.reduce((acc, curr) => { + const [[key, val]] = Object.entries(curr); + return !val.success + ? [...acc, val?.error?.message ? `${key}: ${val?.error?.message}` : key] + : acc; + }, []); + const errorMessageTitle = errorMessage ?? RESTART_TRANSFORMS_ERROR_MESSAGE(failedIds.length); + + if (failedIds.length > 0) { + notifications?.toasts?.addError(new Error(errorMessageTitle), { + title: errorMessageTitle, + toastMessage: getErrorToastMessage({ + messageBody: failedIds.join(', '), + renderDocLink, + }), + toastLifeTimeMs, + }); + } + + return result; + }) + .catch((e) => { + notifications?.toasts?.addError(e, { + title: errorMessage ?? RESTART_TRANSFORMS_ERROR_MESSAGE(), + toastMessage: getErrorToastMessage({ messageBody: e?.body?.message, renderDocLink }), + toastLifeTimeMs, + }); + }); + + return res; +} diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/translations.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/translations.ts index 935d1388acdd0..830614f25e466 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/translations.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/translations.ts @@ -80,6 +80,12 @@ export const START_TRANSFORMS_ERROR_MESSAGE = (totalCount: number) => defaultMessage: `Failed to start {totalCount, plural, =1 {Transform} other {Transforms}}`, }); +export const RESTART_TRANSFORMS_ERROR_MESSAGE = (totalCount?: number) => + i18n.translate('xpack.securitySolution.riskScore.api.transforms.start.errorMessageTitle', { + values: { totalCount }, + defaultMessage: `Failed to start {totalCount, plural, =1 {Transform} other {Transforms}}`, + }); + export const STOP_TRANSFORMS_ERROR_MESSAGE = (totalCount: number) => i18n.translate('xpack.securitySolution.riskScore.api.transforms.stop.errorMessageTitle', { values: { totalCount }, @@ -119,3 +125,23 @@ export const DELETE_SAVED_OBJECTS_FAILURE = i18n.translate( defaultMessage: `Failed to delete saved objects`, } ); + +export const HOST_RISK_SCORES_ENABLED_TITLE = i18n.translate( + 'xpack.securitySolution.riskScore.hostRiskScoresEnabledTitle', + { + defaultMessage: `Host Risk Scores enabled`, + } +); + +export const USER_RISK_SCORES_ENABLED_TITLE = i18n.translate( + 'xpack.securitySolution.riskScore.userRiskScoresEnabledTitle', + { + defaultMessage: `User Risk Scores enabled`, + } +); + +export const RISK_SCORES_ENABLED_TEXT = (items: string) => + i18n.translate('xpack.securitySolution.riskScore.savedObjects.enableRiskScoreSuccessTitle', { + values: { items }, + defaultMessage: `{items} imported successfully`, + }); diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/types.ts b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/types.ts index 91789251307cc..1f95545258260 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/types.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/onboarding/api/types.ts @@ -5,6 +5,8 @@ * 2.0. */ import type { HttpSetup, NotificationsStart, ThemeServiceStart } from '@kbn/core/public'; +import type { OutputError } from '@kbn/securitysolution-es-utils'; +import type { RiskScoreEntity } from '../../../../../common/search_strategy/security_solution/risk_score/common'; interface RiskyScoreApiBase { errorMessage?: string; @@ -71,6 +73,10 @@ export interface StartTransforms extends RiskyScoreApiBase { transformIds: string[]; } +export interface RestartTransforms extends RiskyScoreApiBase { + riskScoreEntity: RiskScoreEntity; +} + interface TransformResult { success: boolean; error?: { root_cause?: unknown; type?: string; reason?: string }; @@ -78,6 +84,11 @@ interface TransformResult { export type StartTransformsResult = Record; +export type RestartTransformResult = Record< + string, + { success: boolean; error: OutputError | null } +>; + export interface StopTransforms extends RiskyScoreApiBase { transformIds: string[]; } diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index 43a837344215b..f317e6a898df0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -30,6 +30,8 @@ jest.mock('../timeline', () => ({ StatefulTimeline: () =>
, })); +jest.mock('../../../common/hooks/timeline/use_timeline_save_prompt'); + describe('Flyout', () => { const props = { onAppLeave: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx index dfb2fd0cdd927..4c783c1f9d954 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx @@ -5,18 +5,16 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { EuiFocusTrap, EuiOutsideClickDetector } from '@elastic/eui'; import React, { useEffect, useMemo, useCallback, useState, useRef } from 'react'; -import { useDispatch } from 'react-redux'; import type { AppLeaveHandler } from '@kbn/core/public'; -import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; +import type { TimelineId } from '../../../../common/types/timeline'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { timelineActions } from '../../store/timeline'; import { FlyoutBottomBar } from './bottom_bar'; import { Pane } from './pane'; import { getTimelineShowStatusByIdSelector } from './selectors'; +import { useTimelineSavePrompt } from '../../../common/hooks/timeline/use_timeline_save_prompt'; interface OwnProps { timelineId: TimelineId; @@ -26,13 +24,8 @@ interface OwnProps { type VoidFunc = () => void; const FlyoutComponent: React.FC = ({ timelineId, onAppLeave }) => { - const dispatch = useDispatch(); const getTimelineShowStatus = useMemo(() => getTimelineShowStatusByIdSelector(), []); - const { - show, - status: timelineStatus, - updated, - } = useDeepEqualSelector((state) => getTimelineShowStatus(state, timelineId)); + const { show } = useDeepEqualSelector((state) => getTimelineShowStatus(state, timelineId)); const [focusOwnership, setFocusOwnership] = useState(true); const [triggerOnBlur, setTriggerOnBlur] = useState(true); @@ -58,6 +51,8 @@ const FlyoutComponent: React.FC = ({ timelineId, onAppLeave }) => { } }, []); + useTimelineSavePrompt(timelineId, onAppLeave); + useEffect(() => { if (searchRef.current != null) { if (callbackRef.current !== null) { @@ -73,48 +68,6 @@ const FlyoutComponent: React.FC = ({ timelineId, onAppLeave }) => { }; }, [handleSearch, triggerOnBlur]); - useEffect(() => { - onAppLeave((actions, nextAppId) => { - if (show) { - dispatch(timelineActions.showTimeline({ id: TimelineId.active, show: false })); - } - // Confirm when the user has made any changes to a timeline - if ( - !(nextAppId ?? '').includes('securitySolution') && - timelineStatus === TimelineStatus.draft && - updated != null - ) { - const showSaveTimelineModal = () => { - dispatch(timelineActions.showTimeline({ id: TimelineId.active, show: true })); - dispatch( - timelineActions.setActiveTabTimeline({ - id: TimelineId.active, - activeTab: TimelineTabs.query, - }) - ); - dispatch( - timelineActions.toggleModalSaveTimeline({ - id: TimelineId.active, - showModalSaveTimeline: true, - }) - ); - }; - - return actions.confirm( - i18n.translate('xpack.securitySolution.timeline.unsavedWorkMessage', { - defaultMessage: 'Leave Timeline with unsaved work?', - }), - i18n.translate('xpack.securitySolution.timeline.unsavedWorkTitle', { - defaultMessage: 'Unsaved changes', - }), - showSaveTimelineModal - ); - } else { - return actions.default(); - } - }); - }, [dispatch, onAppLeave, show, timelineStatus, updated]); - return ( <> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx index bb1047b9bb9d0..5071bed48c243 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx @@ -128,23 +128,30 @@ describe('useCreateTimelineButton', () => { wrapper.find('[data-test-subj="timeline-new"]').first().simulate('click'); + expect(mockDispatch.mock.calls.length).toBe(6); + expect(mockDispatch.mock.calls[0][0].type).toEqual( 'x-pack/security_solution/local/sourcerer/SET_SELECTED_DATA_VIEW' ); expect(mockDispatch.mock.calls[1][0].type).toEqual( 'x-pack/security_solution/local/timeline/CREATE_TIMELINE' ); + expect(mockDispatch.mock.calls[2][0].type).toEqual( + 'x-pack/security_solution/local/timeline/SET_TIMELINE_UPDATED_AT' + ); + + expect(mockDispatch.mock.calls[3][0].type).toEqual( 'x-pack/security_solution/local/inputs/ADD_LINK_TO' ); - expect(mockDispatch.mock.calls[2][0].payload).toEqual([ + expect(mockDispatch.mock.calls[3][0].payload).toEqual([ InputsModelId.global, InputsModelId.timeline, ]); - expect(mockDispatch.mock.calls[3][0].type).toEqual( + expect(mockDispatch.mock.calls[4][0].type).toEqual( 'x-pack/security_solution/local/app/ADD_NOTE' ); - expect(mockDispatch.mock.calls[4][0].type).toEqual( + expect(mockDispatch.mock.calls[5][0].type).toEqual( 'x-pack/security_solution/local/inputs/SET_RELATIVE_RANGE_DATE_PICKER' ); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx index cb6e89e7527ac..7c83007858ae0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx @@ -57,6 +57,13 @@ export const useCreateTimeline = ({ timelineId, timelineType, closeGearMenu }: P timelineType, }) ); + + dispatch( + timelineActions.setTimelineUpdatedAt({ + id: TimelineId.active, + updated: undefined, + }) + ); dispatch(inputsActions.addLinkTo([InputsModelId.global, InputsModelId.timeline])); dispatch(appActions.addNotes({ notes: [] })); if (globalTimeRange.kind === 'absolute') { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index c49323b745a41..8282143d8fe71 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -70,7 +70,7 @@ export const createTimeline = actionCreator('CREATE_TIMELI export const pinEvent = actionCreator<{ id: string; eventId: string }>('PIN_EVENT'); -export const setTimelineUpdatedAt = actionCreator<{ id: string; updated: number }>( +export const setTimelineUpdatedAt = actionCreator<{ id: string; updated: number | undefined }>( 'SET_TIMELINE_UPDATED_AT' ); diff --git a/x-pack/plugins/security_solution/public/users/components/all_users/index.test.tsx b/x-pack/plugins/security_solution/public/users/components/all_users/index.test.tsx index 95cc6ceef4e62..0696dada85693 100644 --- a/x-pack/plugins/security_solution/public/users/components/all_users/index.test.tsx +++ b/x-pack/plugins/security_solution/public/users/components/all_users/index.test.tsx @@ -12,10 +12,16 @@ import { TestProviders } from '../../../common/mock'; import { UsersTable } from '.'; import { usersModel } from '../../store'; -import { Direction } from '../../../../common/search_strategy'; +import { Direction, RiskSeverity } from '../../../../common/search_strategy'; import { UsersFields } from '../../../../common/search_strategy/security_solution/users/common'; import { render } from '@testing-library/react'; +const mockUseMlCapabilities = jest.fn().mockReturnValue({ isPlatinumOrTrialLicense: false }); + +jest.mock('../../../common/components/ml/hooks/use_ml_capabilities', () => ({ + useMlCapabilities: () => mockUseMlCapabilities(), +})); + describe('Users Table Component', () => { const loadPage = jest.fn(); @@ -72,5 +78,73 @@ describe('Users Table Component', () => { expect(getByTestId('table-allUsers-loading-false')).toHaveTextContent('(Empty string)'); }); + + test('it renders "Host Risk classfication" column when "isPlatinumOrTrialLicense" is truthy', () => { + mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: true }); + + const { getAllByRole, getByText } = render( + + {}} + /> + + ); + + expect(getAllByRole('columnheader').length).toBe(4); + expect(getByText('Critical')).toBeInTheDocument(); + }); + + test("it doesn't renders 'Host Risk classfication' column when 'isPlatinumOrTrialLicense' is falsy", () => { + mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: false }); + + const { getAllByRole, queryByText } = render( + + {}} + /> + + ); + + expect(getAllByRole('columnheader').length).toBe(3); + expect(queryByText('Critical')).not.toBeInTheDocument(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx index 6d30abce0f506..d8ad4b168adb4 100644 --- a/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx @@ -8,9 +8,10 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; +import { EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; import { UserDetailsLink } from '../../../common/components/links'; -import { getOrEmptyTagFromValue } from '../../../common/components/empty_value'; +import { getEmptyTagValue, getOrEmptyTagFromValue } from '../../../common/components/empty_value'; import type { Columns, Criteria, ItemsPerRow } from '../../../common/components/paginated_table'; import { PaginatedTable } from '../../../common/components/paginated_table'; @@ -22,6 +23,13 @@ import * as i18n from './translations'; import { usersActions, usersModel, usersSelectors } from '../../store'; import type { User } from '../../../../common/search_strategy/security_solution/users/all'; import type { SortUsersField } from '../../../../common/search_strategy/security_solution/users/common'; +import type { RiskSeverity } from '../../../../common/search_strategy'; +import { RiskScore } from '../../../common/components/severity/common'; +import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities'; +import { VIEW_USERS_BY_SEVERITY } from '../user_risk_score_table/translations'; +import { SecurityPageName } from '../../../app/types'; +import { UsersTableType } from '../../store/model'; +import { useNavigateTo } from '../../../common/lib/kibana'; const tableType = usersModel.UsersTableType.allUsers; @@ -41,7 +49,8 @@ interface UsersTableProps { export type UsersTableColumns = [ Columns, Columns, - Columns + Columns, + Columns? ]; const rowItems: ItemsPerRow[] = [ @@ -55,51 +64,89 @@ const rowItems: ItemsPerRow[] = [ }, ]; -const getUsersColumns = (): UsersTableColumns => [ - { - field: 'name', - name: i18n.USER_NAME, - truncateText: false, - sortable: true, - mobileOptions: { show: true }, - render: (name) => - name != null && name.length > 0 - ? getRowItemDraggables({ - rowItems: [name], - attrName: 'user.name', - idPrefix: `users-table-${name}-name`, - render: (item) => , - isAggregatable: true, - fieldType: 'keyword', - }) - : getOrEmptyTagFromValue(name), - }, - { - field: 'lastSeen', - name: i18n.LAST_SEEN, - sortable: true, - truncateText: false, - mobileOptions: { show: true }, - render: (lastSeen) => , - }, - { - field: 'domain', - name: i18n.DOMAIN, - sortable: false, - truncateText: false, - mobileOptions: { show: true }, - render: (domain) => - domain != null && domain.length > 0 - ? getRowItemDraggables({ - rowItems: [domain], - attrName: 'user.domain', - idPrefix: `users-table-${domain}-domain`, - isAggregatable: true, - fieldType: 'keyword', - }) - : getOrEmptyTagFromValue(domain), - }, -]; +const getUsersColumns = ( + showRiskColumn: boolean, + dispatchSeverityUpdate: (s: RiskSeverity) => void +): UsersTableColumns => { + const columns: UsersTableColumns = [ + { + field: 'name', + name: i18n.USER_NAME, + truncateText: false, + sortable: true, + mobileOptions: { show: true }, + render: (name) => + name != null && name.length > 0 + ? getRowItemDraggables({ + rowItems: [name], + attrName: 'user.name', + idPrefix: `users-table-${name}-name`, + render: (item) => , + isAggregatable: true, + fieldType: 'keyword', + }) + : getOrEmptyTagFromValue(name), + }, + { + field: 'lastSeen', + name: i18n.LAST_SEEN, + sortable: true, + truncateText: false, + mobileOptions: { show: true }, + render: (lastSeen) => , + }, + { + field: 'domain', + name: i18n.DOMAIN, + sortable: false, + truncateText: false, + mobileOptions: { show: true }, + render: (domain) => + domain != null && domain.length > 0 + ? getRowItemDraggables({ + rowItems: [domain], + attrName: 'user.domain', + idPrefix: `users-table-${domain}-domain`, + isAggregatable: true, + fieldType: 'keyword', + }) + : getOrEmptyTagFromValue(domain), + }, + ]; + + if (showRiskColumn) { + columns.push({ + field: 'risk', + name: ( + + <> + {i18n.USER_RISK} + + + ), + truncateText: false, + mobileOptions: { show: true }, + sortable: false, + render: (riskScore: RiskSeverity) => { + if (riskScore != null) { + return ( + dispatchSeverityUpdate(riskScore)}> + {VIEW_USERS_BY_SEVERITY(riskScore.toLowerCase())} + + } + severity={riskScore} + /> + ); + } + return getEmptyTagValue(); + }, + }); + } + + return columns; +}; const UsersTableComponent: React.FC = ({ users, @@ -116,6 +163,8 @@ const UsersTableComponent: React.FC = ({ const dispatch = useDispatch(); const getUsersSelector = useMemo(() => usersSelectors.allUsersSelector(), []); const { activePage, limit } = useDeepEqualSelector((state) => getUsersSelector(state)); + const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense; + const { navigateTo } = useNavigateTo(); const updateLimitPagination = useCallback( (newLimit) => { @@ -159,7 +208,26 @@ const UsersTableComponent: React.FC = ({ }, [dispatch, sort] ); - const columns = useMemo(() => getUsersColumns(), []); + + const dispatchSeverityUpdate = useCallback( + (s: RiskSeverity) => { + dispatch( + usersActions.updateUserRiskScoreSeverityFilter({ + severitySelection: [s], + }) + ); + navigateTo({ + deepLinkId: SecurityPageName.users, + path: UsersTableType.risk, + }); + }, + [dispatch, navigateTo] + ); + + const columns = useMemo( + () => getUsersColumns(isPlatinumOrTrialLicense, dispatchSeverityUpdate), + [isPlatinumOrTrialLicense, dispatchSeverityUpdate] + ); return ( values: { totalCount }, defaultMessage: `{totalCount, plural, =1 {user} other {users}}`, }); + +export const USER_RISK_TOOLTIP = i18n.translate( + 'xpack.securitySolution.usersTable.userRiskToolTip', + { + defaultMessage: + 'User risk classification is determined by user risk score. Users classified as Critical or High are indicated as risky.', + } +); + +export const USER_RISK = i18n.translate('xpack.securitySolution.usersTable.riskTitle', { + defaultMessage: 'User risk classification', +}); 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 index 1ca0fd4dabf60..39d2dca25f050 100644 --- 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 @@ -14,10 +14,18 @@ import { import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { findRuleExceptionReferencesRoute } from './find_rule_exceptions_route'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; describe('findRuleExceptionReferencesRoute', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); + const mockList = { + ...getExceptionListSchemaMock(), + type: 'detection', + id: '4656dc92-5832-11ea-8e2d-0242ac130003', + list_id: 'my_default_list', + namespace_type: 'single', + }; beforeEach(() => { server = serverMock.create(); @@ -42,6 +50,10 @@ describe('findRuleExceptionReferencesRoute', () => { ], }); + (clients.lists.exceptionListClient.findExceptionList as jest.Mock).mockResolvedValue({ + data: [mockList], + }); + findRuleExceptionReferencesRoute(server.router); }); @@ -62,21 +74,24 @@ describe('findRuleExceptionReferencesRoute', () => { 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', - }, - ], + my_default_list: { + ...mockList, + referenced_rules: [ + { + 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', + }, + ], + }, }, ], }); @@ -101,7 +116,10 @@ describe('findRuleExceptionReferencesRoute', () => { expect(response.body).toEqual({ references: [ { - my_default_list: [], + my_default_list: { + ...mockList, + referenced_rules: [], + }, }, ], }); 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 index 8ac258322e260..531a33b253084 100644 --- 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 @@ -8,7 +8,6 @@ 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'; @@ -43,39 +42,67 @@ export const findRuleExceptionReferencesRoute = (router: SecuritySolutionPluginR const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); const rulesClient = ctx.alerting.getRulesClient(); + const listsClient = ctx.securitySolution.getExceptionListClient(); - if (ids.length !== namespaceTypes.length || ids.length !== listIds.length) { + if ( + ids != null && + listIds != null && + (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({ + const fetchExact = ids != null && listIds != null; + + const foundExceptionLists = await listsClient?.findExceptionList({ + filter: fetchExact + ? `(${listIds + .map((listId) => `exception-list.attributes.list_id:${listId}`) + .join(' OR ')})` + : undefined, + namespaceType: ['agnostic', 'single'], + page: 1, + perPage: 10000, + sortField: undefined, + sortOrder: undefined, + }); + + if (foundExceptionLists == null) { + return response.ok({ body: { references: [] } }); + } + + const references: RuleReferencesSchema[] = await Promise.all( + foundExceptionLists.data.map(async (list, index) => { + const foundRules = await rulesClient.find({ options: { perPage: 10000, filter: enrichFilterWithRuleTypeMapping(null), hasReference: { - id, + id: list.id, type: getSavedObjectType({ namespaceType: namespaceTypes[index] }), }, }, }); + + const ruleData = foundRules.data.map(({ name, id, params }) => ({ + name, + id, + rule_id: params.ruleId, + exception_lists: params.exceptionsList, + })); + + return { + [list.list_id]: { + ...list, + referenced_rules: ruleData, + }, + }; }) ); - 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) { diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.test.ts index e8ee87a4e7680..bdc97de6cee17 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.test.ts @@ -10,7 +10,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { Logger } from '@kbn/core/server'; import { serverMock, requestContextMock, @@ -19,35 +19,37 @@ import { import { createEsIndexRoute } from './create_index_route'; import { RISK_SCORE_CREATE_INDEX } from '../../../../common/constants'; import { createIndex } from './lib/create_index'; +import { transformError } from '@kbn/securitysolution-es-utils'; +const testIndex = 'test-index'; jest.mock('./lib/create_index', () => { const actualModule = jest.requireActual('./lib/create_index'); return { ...actualModule, - createIndex: jest.fn(), + createIndex: jest.fn().mockResolvedValue({ [testIndex]: { success: true, error: null } }), }; }); describe('createEsIndexRoute', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); - + const logger = { error: jest.fn() } as unknown as Logger; beforeEach(() => { - jest.resetModules(); - jest.resetAllMocks(); + jest.clearAllMocks(); server = serverMock.create(); ({ context } = requestContextMock.createTools()); - createEsIndexRoute(server.router); + createEsIndexRoute(server.router, logger); }); it('create index', async () => { const request = requestMock.create({ method: 'put', path: RISK_SCORE_CREATE_INDEX, - body: { index: 'test-index', mappings: {} }, + body: { index: testIndex, mappings: {} }, }); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(createIndex).toHaveBeenCalled(); expect(response.status).toEqual(200); @@ -63,4 +65,20 @@ describe('createEsIndexRoute', () => { expect(result.ok).not.toHaveBeenCalled(); }); + + it('return error if failed to create index', async () => { + (createIndex as jest.Mock).mockResolvedValue({ + [testIndex]: { success: false, error: transformError(new Error('unknown error')) }, + }); + const request = requestMock.create({ + method: 'put', + path: RISK_SCORE_CREATE_INDEX, + body: { index: testIndex, mappings: {} }, + }); + + const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(createIndex).toHaveBeenCalled(); + expect(response.status).toEqual(500); + expect(response.body).toEqual({ message: 'unknown error', status_code: 500 }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.ts index c96ef222316cb..a22a503a071e3 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/indices/create_index_route.ts @@ -5,13 +5,15 @@ * 2.0. */ -import { transformError } from '@kbn/securitysolution-es-utils'; import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import type { Logger } from '@kbn/core/server'; + +import { transformError } from '@kbn/securitysolution-es-utils'; import { RISK_SCORE_CREATE_INDEX } from '../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { createEsIndexBodySchema, createIndex } from './lib/create_index'; -export const createEsIndexRoute = (router: SecuritySolutionPluginRouter) => { +export const createEsIndexRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.put( { path: RISK_SCORE_CREATE_INDEX, @@ -23,15 +25,27 @@ export const createEsIndexRoute = (router: SecuritySolutionPluginRouter) => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); const { client } = (await context.core).elasticsearch; + const esClient = client.asCurrentUser; const options = request.body; + try { - await createIndex({ - client, + const result = await createIndex({ + esClient, + logger, options, }); - return response.ok({ body: options }); - } catch (err) { - const error = transformError(err); + const error = result[options.index].error; + + if (error != null) { + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } else { + return response.ok({ body: options }); + } + } catch (e) { + const error = transformError(e); return siemResponse.error({ body: error.message, statusCode: error.statusCode, diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/indices/lib/create_index.ts b/x-pack/plugins/security_solution/server/lib/risk_score/indices/lib/create_index.ts index 7ab28d2964f98..035be5e7d0e8d 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/indices/lib/create_index.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/indices/lib/create_index.ts @@ -6,21 +6,38 @@ */ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { transformError } from '@kbn/securitysolution-es-utils'; export const createEsIndexBodySchema = schema.object({ index: schema.string({ minLength: 1 }), - mappings: schema.maybe(schema.recordOf(schema.string({ minLength: 1 }), schema.any())), + mappings: schema.maybe( + schema.oneOf([schema.string(), schema.recordOf(schema.string({ minLength: 1 }), schema.any())]) + ), }); type CreateEsIndexBodySchema = TypeOf; export const createIndex = async ({ - client, + esClient, + logger, options, }: { - client: IScopedClusterClient; + esClient: ElasticsearchClient; + logger: Logger; options: CreateEsIndexBodySchema; }) => { - await client.asCurrentUser.indices.create(options); + try { + await esClient.indices.create({ + index: options.index, + mappings: + typeof options.mappings === 'string' ? JSON.parse(options.mappings) : options.mappings, + }); + return { [options.index]: { success: true, error: null } }; + } catch (err) { + const error = transformError(err); + logger.error(`Failed to create index: ${options.index}: ${error.message}`); + + return { [options.index]: { success: false, error } }; + } }; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/ingest_pipeline.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/ingest_pipeline.ts new file mode 100644 index 0000000000000..4b4158aa3af01 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/ingest_pipeline.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 { transformError } from '@kbn/securitysolution-es-utils'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { Pipeline } from '../../../../../common/types/risk_scores'; + +export const createIngestPipeline = async ({ + esClient, + logger, + options, +}: { + esClient: ElasticsearchClient; + logger: Logger; + options: Pipeline; +}) => { + const processors = + typeof options.processors === 'string' ? JSON.parse(options.processors) : options.processors; + // eslint-disable-next-line @typescript-eslint/naming-convention + const { name, description, version, on_failure } = options; + + try { + await esClient.ingest.putPipeline({ + id: name, + body: { + description, + processors, + version, + on_failure, + }, + }); + + return { [name]: { success: true, error: null } }; + } catch (err) { + const error = transformError(err); + logger.error(`Failed to create ingest pipeline: ${name}: ${error.message}`); + + return { [name]: { success: false, error } }; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/install_risk_score_module.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/install_risk_score_module.ts new file mode 100644 index 0000000000000..1b4a8c583b8b5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/helpers/install_risk_score_module.ts @@ -0,0 +1,313 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import { + getCreateLatestTransformOptions, + getCreateMLHostPivotTransformOptions, + getCreateMLUserPivotTransformOptions, + getCreateRiskScoreIndicesOptions, + getCreateRiskScoreLatestIndicesOptions, + getRiskHostCreateInitScriptOptions, + getRiskHostCreateLevelScriptOptions, + getRiskHostCreateMapScriptOptions, + getRiskHostCreateReduceScriptOptions, + getRiskScoreIngestPipelineOptions, + getRiskScoreLatestTransformId, + getRiskScorePivotTransformId, + getRiskUserCreateLevelScriptOptions, + getRiskUserCreateMapScriptOptions, + getRiskUserCreateReduceScriptOptions, +} from '../../../../../common/utils/risk_score_modules'; +import { createIndex } from '../../indices/lib/create_index'; +import { createStoredScript } from '../../stored_scripts/lib/create_script'; +import { createAndStartTransform } from '../../transform/helpers/transforms'; +import { createIngestPipeline } from './ingest_pipeline'; + +interface InstallRiskScoreModule { + esClient: ElasticsearchClient; + logger: Logger; + riskScoreEntity: RiskScoreEntity; + spaceId: string; +} + +const createHostRiskScoreIngestPipelineGrouping = ({ + esClient, + logger, + riskScoreEntity, + spaceId, +}: InstallRiskScoreModule) => { + /** + * console_templates/enable_host_risk_score.console + * Step 1 Upload script: ml_hostriskscore_levels_script_{spaceId} + */ + const createLevelScriptOptions = getRiskHostCreateLevelScriptOptions(spaceId); + + return createStoredScript({ + esClient, + logger, + options: createLevelScriptOptions, + }).then((createStoredScriptResult) => { + if (createStoredScriptResult[createLevelScriptOptions.id].success) { + /** + * console_templates/enable_host_risk_score.console + * Step 5 Upload the ingest pipeline: ml_hostriskscore_ingest_pipeline_{spaceId} + */ + const createIngestPipelineOptions = getRiskScoreIngestPipelineOptions( + riskScoreEntity, + spaceId + ); + return createIngestPipeline({ + esClient, + logger, + options: createIngestPipelineOptions, + }).then((createIngestPipelineResult) => { + return [createStoredScriptResult, createIngestPipelineResult]; + }); + } else { + return [createStoredScriptResult]; + } + }); +}; + +const installHostRiskScoreModule = async ({ + esClient, + riskScoreEntity, + logger, + spaceId, +}: InstallRiskScoreModule) => { + const result = await Promise.all([ + /** + * console_templates/enable_host_risk_score.console + * Step 1 Upload script: ml_hostriskscore_levels_script_{spaceId} + * Step 5 Upload the ingest pipeline: ml_hostriskscore_ingest_pipeline_{spaceId} + */ + createHostRiskScoreIngestPipelineGrouping({ + esClient, + logger, + riskScoreEntity, + spaceId, + }), + /** + * console_templates/enable_host_risk_score.console + * Step 2 Upload script: ml_hostriskscore_init_script_{spaceId} + */ + createStoredScript({ + esClient, + logger, + options: getRiskHostCreateInitScriptOptions(spaceId), + }), + + /** + * console_templates/enable_host_risk_score.console + * Step 3 Upload script: ml_hostriskscore_map_script_{spaceId} + */ + createStoredScript({ + esClient, + logger, + options: getRiskHostCreateMapScriptOptions(spaceId), + }), + + /** + * console_templates/enable_host_risk_score.console + * Step 4 Upload script: ml_hostriskscore_reduce_script_{spaceId} + */ + createStoredScript({ + esClient, + logger, + options: getRiskHostCreateReduceScriptOptions(spaceId), + }), + + /** + * console_templates/enable_host_risk_score.console + * Step 6 create ml_host_risk_score_{spaceId} index + */ + createIndex({ + esClient, + logger, + options: getCreateRiskScoreIndicesOptions({ + spaceId, + riskScoreEntity, + }), + }), + + /** + * console_templates/enable_host_risk_score.console + * Step 9 create ml_host_risk_score_latest_{spaceId} index + */ + createIndex({ + esClient, + logger, + options: getCreateRiskScoreLatestIndicesOptions({ + spaceId, + riskScoreEntity, + }), + }), + ]); + + /** + * console_templates/enable_host_risk_score.console + * Step 7 create transform: ml_hostriskscore_pivot_transform_{spaceId} + * Step 8 Start the pivot transform + */ + const createAndStartPivotTransformResult = await createAndStartTransform({ + esClient, + logger, + transformId: getRiskScorePivotTransformId(riskScoreEntity, spaceId), + options: getCreateMLHostPivotTransformOptions({ spaceId }), + }); + + /** + * console_templates/enable_host_risk_score.console + * Step 10 create transform: ml_hostriskscore_latest_transform_{spaceId} + * Step 11 Start the latest transform + */ + const createAndStartLatestTransformResult = await createAndStartTransform({ + esClient, + logger, + transformId: getRiskScoreLatestTransformId(riskScoreEntity, spaceId), + options: getCreateLatestTransformOptions({ riskScoreEntity, spaceId }), + }); + + return [ + ...result, + createAndStartPivotTransformResult, + createAndStartLatestTransformResult, + ].flat(); +}; + +const createUserRiskScoreIngestPipelineGrouping = async ({ + esClient, + logger, + riskScoreEntity, + spaceId, +}: InstallRiskScoreModule) => { + /** + * console_templates/enable_user_risk_score.console + * Step 1 Upload script: ml_userriskscore_levels_script_{spaceId} + */ + const createLevelScriptOptions = getRiskUserCreateLevelScriptOptions(spaceId); + + const createStoredScriptResult = await createStoredScript({ + esClient, + logger, + options: createLevelScriptOptions, + }); + + /** + * console_templates/enable_user_risk_score.console + * Step 4 Upload ingest pipeline: ml_userriskscore_ingest_pipeline_{spaceId} + */ + const createIngestPipelineOptions = getRiskScoreIngestPipelineOptions(riskScoreEntity, spaceId); + const createIngestPipelineResult = await createIngestPipeline({ + esClient, + logger, + options: createIngestPipelineOptions, + }); + + return [createStoredScriptResult, createIngestPipelineResult]; +}; + +const installUserRiskScoreModule = async ({ + esClient, + logger, + riskScoreEntity, + spaceId, +}: InstallRiskScoreModule) => { + const result = await Promise.all([ + /** + * console_templates/enable_user_risk_score.console + * Step 1 Upload script: ml_userriskscore_levels_script_{spaceId} + * Step 4 Upload ingest pipeline: ml_userriskscore_ingest_pipeline_{spaceId} + */ + createUserRiskScoreIngestPipelineGrouping({ esClient, logger, riskScoreEntity, spaceId }), + /** + * console_templates/enable_user_risk_score.console + * Step 2 Upload script: ml_userriskscore_map_script_{spaceId} + */ + createStoredScript({ + esClient, + logger, + options: getRiskUserCreateMapScriptOptions(spaceId), + }), + + /** + * console_templates/enable_user_risk_score.console + * Step 3 Upload script: ml_userriskscore_reduce_script_{spaceId} + */ + createStoredScript({ + esClient, + logger, + options: getRiskUserCreateReduceScriptOptions(spaceId), + }), + + /** + * console_templates/enable_user_risk_score.console + * Step 5 create ml_user_risk_score_{spaceId} index + */ + createIndex({ + esClient, + logger, + options: getCreateRiskScoreIndicesOptions({ + spaceId, + riskScoreEntity, + }), + }), + /** + * console_templates/enable_user_risk_score.console + * Step 8 create ml_user_risk_score_latest_{spaceId} index + */ + createIndex({ + esClient, + logger, + options: getCreateRiskScoreLatestIndicesOptions({ + spaceId, + riskScoreEntity, + }), + }), + ]); + + /** + * console_templates/enable_user_risk_score.console + * Step 6 create Transform: ml_userriskscore_pivot_transform_{spaceId} + * Step 7 Start the pivot transform + */ + const createAndStartPivotTransformResult = await createAndStartTransform({ + esClient, + logger, + transformId: getRiskScorePivotTransformId(riskScoreEntity, spaceId), + options: getCreateMLUserPivotTransformOptions({ spaceId }), + }); + + /** + * console_templates/enable_user_risk_score.console + * Step 9 create Transform: ml_userriskscore_latest_transform_{spaceId} + * Step 10 Start the latest transform + */ + const createAndStartLatestTransformResult = await createAndStartTransform({ + esClient, + logger, + transformId: getRiskScoreLatestTransformId(riskScoreEntity, spaceId), + options: getCreateLatestTransformOptions({ riskScoreEntity, spaceId }), + }); + + return [ + ...result, + createAndStartPivotTransformResult, + createAndStartLatestTransformResult, + ].flat(); +}; + +export const installRiskScoreModule = async (settings: InstallRiskScoreModule) => { + if (settings.riskScoreEntity === RiskScoreEntity.user) { + const result = await installUserRiskScoreModule(settings); + return result; + } else { + const result = await installHostRiskScoreModule(settings); + return result; + } +}; diff --git a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/__snapshots__/utils.test.ts.snap b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/__snapshots__/install_risk_scores.test.ts.snap similarity index 82% rename from x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/__snapshots__/utils.test.ts.snap rename to x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/__snapshots__/install_risk_scores.test.ts.snap index ac18307ba384c..523cc405eb294 100644 --- a/x-pack/plugins/security_solution/public/risk_score/components/risk_score_onboarding/__snapshots__/utils.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/__snapshots__/install_risk_scores.test.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`installRiskScoreModule - host Create Index: ml_host_risk_score_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create Index: ml_host_risk_score_latest_mockSpaceId 1`] = ` Object { - "index": "ml_host_risk_score_customSpace", + "index": "ml_host_risk_score_latest_default", "mappings": Object { "properties": Object { "@timestamp": Object { @@ -54,9 +54,9 @@ Object { } `; -exports[`installRiskScoreModule - host Create Index: ml_host_risk_score_latest_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create Index: ml_host_risk_score_mockSpaceId 1`] = ` Object { - "index": "ml_host_risk_score_latest_customSpace", + "index": "ml_host_risk_score_default", "mappings": Object { "properties": Object { "@timestamp": Object { @@ -108,9 +108,9 @@ Object { } `; -exports[`installRiskScoreModule - host Create IngestPipeline: ml_hostriskscore_ingest_pipeline_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create IngestPipeline: ml_hostriskscore_ingest_pipeline_mockSpaceId 1`] = ` Object { - "name": "ml_hostriskscore_ingest_pipeline_customSpace", + "name": "ml_hostriskscore_ingest_pipeline_default", "processors": Array [ Object { "set": Object { @@ -130,7 +130,7 @@ Object { }, Object { "script": Object { - "id": "ml_hostriskscore_levels_script_customSpace", + "id": "ml_hostriskscore_levels_script_default", "params": Object { "risk_score": "host.risk.calculated_score_norm", }, @@ -140,10 +140,10 @@ Object { } `; -exports[`installRiskScoreModule - host Create Transform: ml_hostriskscore_latest_transform_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create and start Transform: ml_hostriskscore_latest_transform_mockSpaceId 1`] = ` Object { "dest": Object { - "index": "ml_host_risk_score_latest_customSpace", + "index": "ml_host_risk_score_latest_default", }, "frequency": "1h", "latest": Object { @@ -154,7 +154,7 @@ Object { }, "source": Object { "index": Array [ - "ml_host_risk_score_customSpace", + "ml_host_risk_score_default", ], }, "sync": Object { @@ -166,11 +166,11 @@ Object { } `; -exports[`installRiskScoreModule - host Create Transform: ml_hostriskscore_pivot_transform_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create and start Transform: ml_hostriskscore_pivot_transform_mockSpaceId 1`] = ` Object { "dest": Object { - "index": "ml_host_risk_score_customSpace", - "pipeline": "ml_hostriskscore_ingest_pipeline_customSpace", + "index": "ml_host_risk_score_default", + "pipeline": "ml_hostriskscore_ingest_pipeline_default", }, "frequency": "1h", "pivot": Object { @@ -184,10 +184,10 @@ Object { "scripted_metric": Object { "combine_script": "return state", "init_script": Object { - "id": "ml_hostriskscore_init_script_customSpace", + "id": "ml_hostriskscore_init_script_default", }, "map_script": Object { - "id": "ml_hostriskscore_map_script_customSpace", + "id": "ml_hostriskscore_map_script_default", }, "params": Object { "lookback_time": 72, @@ -215,7 +215,7 @@ Object { "zeta_constant": 2.612, }, "reduce_script": Object { - "id": "ml_hostriskscore_reduce_script_customSpace", + "id": "ml_hostriskscore_reduce_script_default", }, }, }, @@ -230,7 +230,7 @@ Object { }, "source": Object { "index": Array [ - ".alerts-security.alerts-customSpace", + ".alerts-security.alerts-default", ], "query": Object { "bool": Object { @@ -255,9 +255,9 @@ Object { } `; -exports[`installRiskScoreModule - host Create script: ml_hostriskscore_init_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create script: ml_hostriskscore_init_script_mockSpaceId 1`] = ` Object { - "id": "ml_hostriskscore_init_script_customSpace", + "id": "ml_hostriskscore_init_script_default", "script": Object { "lang": "painless", "source": "state.rule_risk_stats = new HashMap(); @@ -268,9 +268,9 @@ state.tactic_ids = new HashSet();", } `; -exports[`installRiskScoreModule - host Create script: ml_hostriskscore_levels_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create script: ml_hostriskscore_levels_script_mockSpaceId 1`] = ` Object { - "id": "ml_hostriskscore_levels_script_customSpace", + "id": "ml_hostriskscore_levels_script_default", "script": Object { "lang": "painless", "source": "double risk_score = (def)ctx.getByPath(params.risk_score); @@ -293,9 +293,9 @@ else if (risk_score >= 90) { } `; -exports[`installRiskScoreModule - host Create script: ml_hostriskscore_map_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create script: ml_hostriskscore_map_script_mockSpaceId 1`] = ` Object { - "id": "ml_hostriskscore_map_script_customSpace", + "id": "ml_hostriskscore_map_script_default", "script": Object { "lang": "painless", "source": "// Get the host variant @@ -324,9 +324,9 @@ state.rule_risk_stats.put(rule_name, stats);", } `; -exports[`installRiskScoreModule - host Create script: ml_hostriskscore_reduce_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - host Create script: ml_hostriskscore_reduce_script_mockSpaceId 1`] = ` Object { - "id": "ml_hostriskscore_reduce_script_customSpace", + "id": "ml_hostriskscore_reduce_script_default", "script": Object { "lang": "painless", "source": "// Consolidating time decayed risks and tactics from across all shards @@ -407,16 +407,9 @@ return [\\"calculated_score_norm\\": final_risk, \\"rule_risks\\": rule_stats, \ } `; -exports[`installRiskScoreModule - host Start Transforms 1`] = ` -Array [ - "ml_hostriskscore_pivot_transform_customSpace", - "ml_hostriskscore_latest_transform_customSpace", -] -`; - -exports[`installRiskScoreModule - user Create Index: ml_user_risk_score_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create Index: ml_user_risk_score_latest_mockSpaceId 1`] = ` Object { - "index": "ml_user_risk_score_customSpace", + "index": "ml_user_risk_score_latest_default", "mappings": Object { "properties": Object { "@timestamp": Object { @@ -468,9 +461,9 @@ Object { } `; -exports[`installRiskScoreModule - user Create Index: ml_user_risk_score_latest_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create Index: ml_user_risk_score_mockSpaceId 1`] = ` Object { - "index": "ml_user_risk_score_latest_customSpace", + "index": "ml_user_risk_score_default", "mappings": Object { "properties": Object { "@timestamp": Object { @@ -522,9 +515,9 @@ Object { } `; -exports[`installRiskScoreModule - user Create IngestPipeline: ml_userriskscore_ingest_pipeline_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create IngestPipeline: ml_userriskscore_ingest_pipeline_mockSpaceId 1`] = ` Object { - "name": "ml_userriskscore_ingest_pipeline_customSpace", + "name": "ml_userriskscore_ingest_pipeline_default", "processors": Array [ Object { "set": Object { @@ -544,7 +537,7 @@ Object { }, Object { "script": Object { - "id": "ml_userriskscore_levels_script_customSpace", + "id": "ml_userriskscore_levels_script_default", "params": Object { "risk_score": "user.risk.calculated_score_norm", }, @@ -554,10 +547,10 @@ Object { } `; -exports[`installRiskScoreModule - user Create Transform: ml_userriskscore_latest_transform_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create Transform: ml_userriskscore_latest_transform_mockSpaceId 1`] = ` Object { "dest": Object { - "index": "ml_user_risk_score_latest_customSpace", + "index": "ml_user_risk_score_latest_default", }, "frequency": "1h", "latest": Object { @@ -568,7 +561,7 @@ Object { }, "source": Object { "index": Array [ - "ml_user_risk_score_customSpace", + "ml_user_risk_score_default", ], }, "sync": Object { @@ -580,11 +573,11 @@ Object { } `; -exports[`installRiskScoreModule - user Create Transform: ml_userriskscore_pivot_transform_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create Transform: ml_userriskscore_pivot_transform_mockSpaceId 1`] = ` Object { "dest": Object { - "index": "ml_user_risk_score_customSpace", - "pipeline": "ml_userriskscore_ingest_pipeline_customSpace", + "index": "ml_user_risk_score_default", + "pipeline": "ml_userriskscore_ingest_pipeline_default", }, "frequency": "1h", "pivot": Object { @@ -599,7 +592,7 @@ Object { "combine_script": "return state", "init_script": "state.rule_risk_stats = new HashMap();", "map_script": Object { - "id": "ml_userriskscore_map_script_customSpace", + "id": "ml_userriskscore_map_script_default", }, "params": Object { "max_risk": 100, @@ -607,7 +600,7 @@ Object { "zeta_constant": 2.612, }, "reduce_script": Object { - "id": "ml_userriskscore_reduce_script_customSpace", + "id": "ml_userriskscore_reduce_script_default", }, }, }, @@ -622,7 +615,7 @@ Object { }, "source": Object { "index": Array [ - ".alerts-security.alerts-customSpace", + ".alerts-security.alerts-default", ], "query": Object { "bool": Object { @@ -652,9 +645,9 @@ Object { } `; -exports[`installRiskScoreModule - user Create script: ml_userriskscore_levels_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create script: ml_userriskscore_levels_script_mockSpaceId 1`] = ` Object { - "id": "ml_userriskscore_levels_script_customSpace", + "id": "ml_userriskscore_levels_script_default", "script": Object { "lang": "painless", "source": "double risk_score = (def)ctx.getByPath(params.risk_score); @@ -677,9 +670,9 @@ else if (risk_score >= 90) { } `; -exports[`installRiskScoreModule - user Create script: ml_userriskscore_map_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create script: ml_userriskscore_map_script_mockSpaceId 1`] = ` Object { - "id": "ml_userriskscore_map_script_customSpace", + "id": "ml_userriskscore_map_script_default", "script": Object { "lang": "painless", "source": "// Get running sum of risk score per rule name per shard\\\\\\\\ @@ -691,9 +684,9 @@ state.rule_risk_stats.put(rule_name, stats);", } `; -exports[`installRiskScoreModule - user Create script: ml_userriskscore_reduce_script_customSpace 1`] = ` +exports[`installRiskScoresRoute - user Create script: ml_userriskscore_reduce_script_mockSpaceId 1`] = ` Object { - "id": "ml_userriskscore_reduce_script_customSpace", + "id": "ml_userriskscore_reduce_script_default", "script": Object { "lang": "painless", "source": "// Consolidating time decayed risks from across all shards @@ -741,10 +734,3 @@ return [\\"calculated_score_norm\\": total_norm_risk, \\"rule_risks\\": rule_sta }, } `; - -exports[`installRiskScoreModule - user Start Transforms 1`] = ` -Array [ - "ml_userriskscore_pivot_transform_customSpace", - "ml_userriskscore_latest_transform_customSpace", -] -`; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts new file mode 100644 index 0000000000000..526f3d595d5a0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/core/server'; +import { + serverMock, + requestContextMock, + requestMock, +} from '../../../detection_engine/routes/__mocks__'; +import { INTERNAL_RISK_SCORE_URL } from '../../../../../common/constants'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import { installRiskScoresRoute } from './install_risk_scores'; +import { createIngestPipeline } from '../helpers/ingest_pipeline'; +import { createStoredScript } from '../../stored_scripts/lib/create_script'; +import { createIndex } from '../../indices/lib/create_index'; +import { createAndStartTransform } from '../../transform/helpers/transforms'; + +jest.mock('../../stored_scripts/lib/create_script', () => ({ + createStoredScript: jest + .fn() + .mockImplementation(({ options }) => + Promise.resolve({ [options.id]: { success: true, error: null } }) + ), +})); + +jest.mock('../helpers/ingest_pipeline', () => ({ + createIngestPipeline: jest + .fn() + .mockImplementation(({ options }) => + Promise.resolve({ [options.name]: { success: true, error: null } }) + ), +})); + +jest.mock('../../indices/lib/create_index', () => ({ + createIndex: jest + .fn() + .mockImplementation(({ options }) => + Promise.resolve({ [options.index]: { success: true, error: null } }) + ), +})); + +jest.mock('../../transform/helpers/transforms', () => ({ + createAndStartTransform: jest + .fn() + .mockImplementation(({ transformId }) => + Promise.resolve({ [transformId]: { success: true, error: null } }) + ), +})); + +describe(`installRiskScoresRoute - ${RiskScoreEntity.host}`, () => { + let server: ReturnType; + let { context } = requestContextMock.createTools(); + const logger = { error: jest.fn() } as unknown as Logger; + const security = undefined; + const mockSpaceId = 'mockSpaceId'; + + beforeAll(async () => { + jest.clearAllMocks(); + + server = serverMock.create(); + ({ context } = requestContextMock.createTools()); + const request = requestMock.create({ + method: 'post', + path: INTERNAL_RISK_SCORE_URL, + body: { + riskScoreEntity: RiskScoreEntity.host, + }, + }); + + installRiskScoresRoute(server.router, logger, security); + await server.inject(request, requestContextMock.convertContext(context)); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it(`Create script: ml_${RiskScoreEntity.host}riskscore_levels_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create IngestPipeline: ml_${RiskScoreEntity.host}riskscore_ingest_pipeline_${mockSpaceId}`, async () => { + expect((createIngestPipeline as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create script: ml_${RiskScoreEntity.host}riskscore_init_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); + + it(`Create script: ml_${RiskScoreEntity.host}riskscore_map_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[2][0].options).toMatchSnapshot(); + }); + + it(`Create script: ml_${RiskScoreEntity.host}riskscore_reduce_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[3][0].options).toMatchSnapshot(); + }); + + it(`Create Index: ml_${RiskScoreEntity.host}_risk_score_${mockSpaceId}`, async () => { + expect((createIndex as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create Index: ml_${RiskScoreEntity.host}_risk_score_latest_${mockSpaceId}`, async () => { + expect((createIndex as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); + + it(`Create and start Transform: ml_${RiskScoreEntity.host}riskscore_pivot_transform_${mockSpaceId}`, async () => { + expect((createAndStartTransform as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create and start Transform: ml_${RiskScoreEntity.host}riskscore_latest_transform_${mockSpaceId}`, async () => { + expect((createAndStartTransform as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); +}); + +describe(`installRiskScoresRoute - ${RiskScoreEntity.user}`, () => { + let server: ReturnType; + let { context } = requestContextMock.createTools(); + const logger = { error: jest.fn() } as unknown as Logger; + const security = undefined; + const mockSpaceId = 'mockSpaceId'; + + beforeAll(async () => { + jest.clearAllMocks(); + + server = serverMock.create(); + ({ context } = requestContextMock.createTools()); + const request = requestMock.create({ + method: 'post', + path: INTERNAL_RISK_SCORE_URL, + body: { + riskScoreEntity: RiskScoreEntity.user, + }, + }); + + installRiskScoresRoute(server.router, logger, security); + await server.inject(request, requestContextMock.convertContext(context)); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it(`Create script: ml_${RiskScoreEntity.user}riskscore_levels_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create IngestPipeline: ml_${RiskScoreEntity.user}riskscore_ingest_pipeline_${mockSpaceId}`, async () => { + expect((createIngestPipeline as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create script: ml_${RiskScoreEntity.user}riskscore_map_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); + + it(`Create script: ml_${RiskScoreEntity.user}riskscore_reduce_script_${mockSpaceId}`, async () => { + expect((createStoredScript as jest.Mock).mock.calls[2][0].options).toMatchSnapshot(); + }); + + it(`Create Index: ml_${RiskScoreEntity.user}_risk_score_${mockSpaceId}`, async () => { + expect((createIndex as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create Index: ml_${RiskScoreEntity.user}_risk_score_latest_${mockSpaceId}`, async () => { + expect((createIndex as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); + + it(`Create Transform: ml_${RiskScoreEntity.user}riskscore_pivot_transform_${mockSpaceId}`, async () => { + expect((createAndStartTransform as jest.Mock).mock.calls[0][0].options).toMatchSnapshot(); + }); + + it(`Create Transform: ml_${RiskScoreEntity.user}riskscore_latest_transform_${mockSpaceId}`, async () => { + expect((createAndStartTransform as jest.Mock).mock.calls[1][0].options).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts new file mode 100644 index 0000000000000..09bab87caf748 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { Logger } from '@kbn/core/server'; + +import { INTERNAL_RISK_SCORE_URL } from '../../../../../common/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; + +import type { SetupPlugins } from '../../../../plugin'; + +import { buildSiemResponse } from '../../../detection_engine/routes/utils'; + +import { installRiskScoreModule } from '../helpers/install_risk_score_module'; +import { onboardingRiskScoreSchema } from '../schema'; + +export const installRiskScoresRoute = ( + router: SecuritySolutionPluginRouter, + logger: Logger, + security: SetupPlugins['security'] +) => { + router.post( + { + path: INTERNAL_RISK_SCORE_URL, + validate: onboardingRiskScoreSchema, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const { riskScoreEntity } = request.body; + + try { + const securitySolution = await context.securitySolution; + + const spaceId = securitySolution?.getSpaceId(); + + const { client } = (await context.core).elasticsearch; + const esClient = client.asCurrentUser; + const res = await installRiskScoreModule({ + esClient, + logger, + riskScoreEntity, + spaceId, + }); + + return response.ok({ + body: res, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/schema.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/schema.ts new file mode 100644 index 0000000000000..0fdcfee33d2c9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/schema.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 { schema } from '@kbn/config-schema'; +import { RiskScoreEntity } from '../../../../common/search_strategy'; + +export const onboardingRiskScoreSchema = { + body: schema.object({ + riskScoreEntity: schema.oneOf([ + schema.literal(RiskScoreEntity.host), + schema.literal(RiskScoreEntity.user), + ]), + }), +}; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/bulk_create_saved_objects.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/bulk_create_saved_objects.ts index de24578a26a7e..e85e7df7a9f3c 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/bulk_create_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/bulk_create_saved_objects.ts @@ -5,29 +5,64 @@ * 2.0. */ import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { Logger } from '@kbn/core/server'; + import uuid from 'uuid'; +import { i18n } from '@kbn/i18n'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import * as savedObjectsToCreate from '../saved_object'; -import type { SavedObjectTemplate } from '../types'; +import type { BulkCreateSavedObjectsResult, SavedObjectTemplate } from '../types'; import { findOrCreateRiskScoreTag } from './find_or_create_tag'; -export const bulkCreateSavedObjects = async ({ +export const bulkCreateSavedObjects = async ({ + logger, savedObjectsClient, spaceId, savedObjectTemplate, }: { + logger: Logger; savedObjectsClient: SavedObjectsClientContract; spaceId?: string; savedObjectTemplate: SavedObjectTemplate; -}) => { +}): Promise => { const regex = //g; const riskScoreEntity = savedObjectTemplate === 'userRiskScoreDashboards' ? RiskScoreEntity.user : RiskScoreEntity.host; - const tag = await findOrCreateRiskScoreTag({ riskScoreEntity, savedObjectsClient, spaceId }); + + const tagResponse = await findOrCreateRiskScoreTag({ + riskScoreEntity, + logger, + savedObjectsClient, + spaceId, + }); + + const tagResult = tagResponse?.hostRiskScoreDashboards ?? tagResponse?.userRiskScoreDashboards; + + if (!tagResult?.success) { + return tagResponse; + } const mySavedObjects = savedObjectsToCreate[savedObjectTemplate]; + if (!mySavedObjects) { + logger.error(`${savedObjectTemplate} template not found`); + return { + [savedObjectTemplate]: { + success: false, + error: transformError( + new Error( + i18n.translate('xpack.securitySolution.riskScore.savedObjects.templateNotFoundTitle', { + values: { savedObjectTemplate }, + defaultMessage: `Failed to import saved objects: {savedObjectTemplate} were not created as template not found`, + }) + ) + ), + }, + }; + } + const idReplaceMappings: Record = {}; mySavedObjects.forEach((so) => { if (so.id.startsWith('(JSON.parse(replacedSO), { + overwrite: true, + }); - return createSO; + return { + [savedObjectTemplate]: { + success: true, + error: null, + body: result.saved_objects.map(({ id, type, attributes: { title, name } }) => ({ + id, + type, + title, + name, + })), + }, + }; + } catch (error) { + const err = transformError(error); + logger.error(`Failed to create saved object: ${savedObjectTemplate}: ${err.message}`); + return { [savedObjectTemplate]: { success: false, error: err } }; + } }; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/find_or_create_tag.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/find_or_create_tag.ts index 2972a4488fd99..9bb15266f2f71 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/find_or_create_tag.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/helpers/find_or_create_tag.ts @@ -4,10 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { Logger } from '@kbn/core/server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { i18n } from '@kbn/i18n'; import type { RiskScoreEntity } from '../../../../../common/search_strategy'; import type { Tag } from './utils'; import { RISK_SCORE_TAG_DESCRIPTION, getRiskScoreTagName } from './utils'; +import type { BulkCreateSavedObjectsResult } from '../types'; export const findRiskScoreTag = async ({ savedObjectsClient, @@ -39,15 +43,17 @@ export const findRiskScoreTag = async ({ export const findOrCreateRiskScoreTag = async ({ riskScoreEntity, + logger, savedObjectsClient, spaceId = 'default', }: { + logger: Logger; riskScoreEntity: RiskScoreEntity; savedObjectsClient: SavedObjectsClientContract; spaceId?: string; -}) => { +}): Promise => { const tagName = getRiskScoreTagName(riskScoreEntity, spaceId); - + const savedObjectTemplate = `${riskScoreEntity}RiskScoreDashboards`; const existingRiskScoreTag = await findRiskScoreTag({ savedObjectsClient, search: tagName, @@ -61,15 +67,59 @@ export const findOrCreateRiskScoreTag = async ({ }; if (existingRiskScoreTag?.id != null) { - return tag; + logger.error(`${savedObjectTemplate} already exists`); + return { + [savedObjectTemplate]: { + success: false, + error: transformError( + new Error( + i18n.translate( + 'xpack.securitySolution.riskScore.savedObjects.templateAlreadyExistsTitle', + { + values: { savedObjectTemplate }, + defaultMessage: `Failed to import saved objects: {savedObjectTemplate} were not created as already exist`, + } + ) + ) + ), + }, + }; } else { - const { id: tagId } = await savedObjectsClient.create('tag', { - name: tagName, - description: RISK_SCORE_TAG_DESCRIPTION, - color: '#6edb7f', - }); + try { + const { id: tagId } = await savedObjectsClient.create('tag', { + name: tagName, + description: RISK_SCORE_TAG_DESCRIPTION, + color: '#6edb7f', + }); - return { ...tag, id: tagId }; + return { + [savedObjectTemplate]: { + success: true, + error: null, + body: { ...tag, id: tagId }, + }, + }; + } catch (e) { + logger.error( + `${savedObjectTemplate} cannot be installed as failed to create the tag: ${tagName}` + ); + return { + [savedObjectTemplate]: { + success: false, + error: transformError( + new Error( + i18n.translate( + 'xpack.securitySolution.riskScore.savedObjects.failedToCreateTagTitle', + { + values: { savedObjectTemplate, tagName }, + defaultMessage: `Failed to import saved objects: {savedObjectTemplate} were not created as failed to create the tag: {tagName}`, + } + ) + ) + ), + }, + }; + } } }; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/__snapshots__/create_prebuilt_saved_objects.test.ts.snap b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/__snapshots__/create_prebuilt_saved_objects.test.ts.snap index bcc3240778155..014e7ccc66ade 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/__snapshots__/create_prebuilt_saved_objects.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/__snapshots__/create_prebuilt_saved_objects.test.ts.snap @@ -17,7 +17,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -136,7 +136,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -159,7 +159,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -190,7 +190,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -221,7 +221,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -252,7 +252,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -270,7 +270,7 @@ Array [ "id": "id-7", "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -323,7 +323,7 @@ Array [ "type": "tag", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -346,7 +346,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -459,7 +459,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -502,7 +502,7 @@ Array [ "type": "tag", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -531,7 +531,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -649,7 +649,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -673,7 +673,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -798,7 +798,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -822,7 +822,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -853,7 +853,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -884,7 +884,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -915,7 +915,7 @@ Array [ "type": "index-pattern", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -936,7 +936,7 @@ Array [ }, "references": Array [ Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -995,7 +995,7 @@ Array [ "type": "tag", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, @@ -1044,7 +1044,7 @@ Array [ "type": "tag", }, Object { - "id": "tagID", + "id": "mockTagId", "name": "my tag", "type": "tag", }, diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts index 6df7ab2b4a566..9101c4c854e17 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { Logger } from '@kbn/core/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { PREBUILT_SAVED_OBJECTS_BULK_CREATE } from '../../../../../common/constants'; import { @@ -13,18 +14,14 @@ import { requestMock, } from '../../../detection_engine/routes/__mocks__'; import { getEmptySavedObjectsResponse } from '../../../detection_engine/routes/__mocks__/request_responses'; +import { findOrCreateRiskScoreTag } from '../helpers/find_or_create_tag'; import { createPrebuiltSavedObjectsRoute } from './create_prebuilt_saved_objects'; jest.mock('../helpers/find_or_create_tag', () => { const actual = jest.requireActual('../helpers/find_or_create_tag'); return { ...actual, - findOrCreateRiskScoreTag: jest.fn().mockResolvedValue({ - id: 'tagID', - name: 'my tag', - description: 'description', - type: 'tag', - }), + findOrCreateRiskScoreTag: jest.fn(), }; }); @@ -57,6 +54,7 @@ describe('createPrebuiltSavedObjects', () => { let server: ReturnType; let securitySetup: SecurityPluginSetup; let { clients, context } = requestContextMock.createTools(); + const logger = { error: jest.fn() } as unknown as Logger; beforeEach(() => { jest.clearAllMocks(); @@ -73,14 +71,26 @@ describe('createPrebuiltSavedObjects', () => { clients.savedObjectsClient.bulkCreate.mockResolvedValue(getEmptySavedObjectsResponse()); - createPrebuiltSavedObjectsRoute(server.router, securitySetup); + createPrebuiltSavedObjectsRoute(server.router, logger, securitySetup); }); it.each([['hostRiskScoreDashboards'], ['userRiskScoreDashboards']])( 'should create saved objects from given template - %p', - async (object) => { + async (templateName) => { + (findOrCreateRiskScoreTag as jest.Mock).mockResolvedValue({ + [templateName]: { + success: true, + error: null, + body: { + id: 'mockTagId', + name: 'my tag', + description: 'description', + type: 'tag', + }, + }, + }); const response = await server.inject( - createPrebuiltSavedObjectsRequest(object), + createPrebuiltSavedObjectsRequest(templateName), requestContextMock.convertContext(context) ); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts index fad59e9dd5c77..ffdb693278dd1 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { transformError } from '@kbn/securitysolution-es-utils'; +import type { Logger } from '@kbn/core/server'; import { PREBUILT_SAVED_OBJECTS_BULK_CREATE } from '../../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../../types'; @@ -20,6 +20,7 @@ import { createPrebuiltSavedObjectsSchema } from '../schema'; export const createPrebuiltSavedObjectsRoute = ( router: SecuritySolutionPluginRouter, + logger: Logger, security: SetupPlugins['security'] ) => { router.post( @@ -34,29 +35,24 @@ export const createPrebuiltSavedObjectsRoute = ( const siemResponse = buildSiemResponse(response); const { template_name: templateName } = request.params; - try { - const securitySolution = await context.securitySolution; - - const spaceId = securitySolution?.getSpaceId(); - - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; - - const res = await bulkCreateSavedObjects({ - savedObjectsClient, - spaceId, - savedObjectTemplate: templateName, - }); - - return response.ok({ - body: res, - }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + const securitySolution = await context.securitySolution; + + const spaceId = securitySolution?.getSpaceId(); + + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; + const result = await bulkCreateSavedObjects({ + savedObjectsClient, + logger, + spaceId, + savedObjectTemplate: templateName, + }); + const error = + result?.hostRiskScoreDashboards?.error || result?.userRiskScoreDashboards?.error; + if (error != null) { + return siemResponse.error({ statusCode: error.statusCode, body: error.message }); + } else { + return response.ok({ body: result }); } } ); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/types.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/types.ts index 172634e1a7652..dd3735c15bec6 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/types.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/types.ts @@ -5,4 +5,19 @@ * 2.0. */ +import type { OutputError } from '@kbn/securitysolution-es-utils'; + export type SavedObjectTemplate = 'hostRiskScoreDashboards' | 'userRiskScoreDashboards'; + +export interface BulkCreateSavedObjectsResult { + hostRiskScoreDashboards?: { + success: boolean; + error: OutputError; + body?: { id: string; name: string; type: string }; + }; + userRiskScoreDashboards?: { + success: boolean; + error: OutputError; + body?: { id: string; name: string; type: string }; + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/routes/index.ts b/x-pack/plugins/security_solution/server/lib/risk_score/routes/index.ts index 6bce81064326e..c55c9086bb447 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/routes/index.ts @@ -16,3 +16,6 @@ export { createStoredScriptRoute } from '../stored_scripts/create_script_route'; export { deleteStoredScriptRoute } from '../stored_scripts/delete_script_route'; export { getRiskScoreIndexStatusRoute } from '../index_status'; + +export { installRiskScoresRoute } from '../onboarding/routes/install_risk_scores'; +export { restartTransformRoute } from '../transform/restart_transform'; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.test.ts index e1d84f278f647..89d2618b74a72 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.test.ts @@ -10,7 +10,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { Logger } from '@kbn/core/server'; import { serverMock, requestContextMock, @@ -19,27 +19,34 @@ import { import { createStoredScriptRoute } from './create_script_route'; import { RISK_SCORE_CREATE_STORED_SCRIPT } from '../../../../common/constants'; import { createStoredScript } from './lib/create_script'; +import { transformError } from '@kbn/securitysolution-es-utils'; + +const testScriptId = 'test-script'; +const testScriptSource = + 'if (state.host_variant_set == false) {\n if (doc.containsKey("host.os.full") && doc["host.os.full"].size() != 0) {\n state.host_variant = doc["host.os.full"].value;\n state.host_variant_set = true;\n }\n}\n// Aggregate all the tactics seen on the host\nif (doc.containsKey("signal.rule.threat.tactic.id") && doc["signal.rule.threat.tactic.id"].size() != 0) {\n state.tactic_ids.add(doc["signal.rule.threat.tactic.id"].value);\n}\n// Get running sum of time-decayed risk score per rule name per shard\nString rule_name = doc["signal.rule.name"].value;\ndef stats = state.rule_risk_stats.getOrDefault(rule_name, [0.0,"",false]);\nint time_diff = (int)((System.currentTimeMillis() - doc["@timestamp"].value.toInstant().toEpochMilli()) / (1000.0 * 60.0 * 60.0));\ndouble risk_derate = Math.min(1, Math.exp((params.lookback_time - time_diff) / params.time_decay_constant));\nstats[0] = Math.max(stats[0], doc["signal.rule.risk_score"].value * risk_derate);\nif (stats[2] == false) {\n stats[1] = doc["kibana.alert.rule.uuid"].value;\n stats[2] = true;\n}\nstate.rule_risk_stats.put(rule_name, stats);'; jest.mock('./lib/create_script', () => { const actualModule = jest.requireActual('./lib/create_script'); return { ...actualModule, - createStoredScript: jest.fn(), + createStoredScript: jest + .fn() + .mockResolvedValue({ [testScriptId]: { success: true, error: null } }), }; }); describe('createStoredScriptRoute', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); + const logger = { error: jest.fn() } as unknown as Logger; beforeEach(() => { - jest.resetModules(); - jest.resetAllMocks(); + jest.clearAllMocks(); server = serverMock.create(); ({ context } = requestContextMock.createTools()); - createStoredScriptRoute(server.router); + createStoredScriptRoute(server.router, logger); }); it('Create stored script', async () => { @@ -47,11 +54,10 @@ describe('createStoredScriptRoute', () => { method: 'put', path: RISK_SCORE_CREATE_STORED_SCRIPT, body: { - id: 'test-script', + id: testScriptId, script: { lang: 'painless', - source: - 'if (state.host_variant_set == false) {\n if (doc.containsKey("host.os.full") && doc["host.os.full"].size() != 0) {\n state.host_variant = doc["host.os.full"].value;\n state.host_variant_set = true;\n }\n}\n// Aggregate all the tactics seen on the host\nif (doc.containsKey("signal.rule.threat.tactic.id") && doc["signal.rule.threat.tactic.id"].size() != 0) {\n state.tactic_ids.add(doc["signal.rule.threat.tactic.id"].value);\n}\n// Get running sum of time-decayed risk score per rule name per shard\nString rule_name = doc["signal.rule.name"].value;\ndef stats = state.rule_risk_stats.getOrDefault(rule_name, [0.0,"",false]);\nint time_diff = (int)((System.currentTimeMillis() - doc["@timestamp"].value.toInstant().toEpochMilli()) / (1000.0 * 60.0 * 60.0));\ndouble risk_derate = Math.min(1, Math.exp((params.lookback_time - time_diff) / params.time_decay_constant));\nstats[0] = Math.max(stats[0], doc["signal.rule.risk_score"].value * risk_derate);\nif (stats[2] == false) {\n stats[1] = doc["kibana.alert.rule.uuid"].value;\n stats[2] = true;\n}\nstate.rule_risk_stats.put(rule_name, stats);', + source: testScriptSource, }, }, }); @@ -70,4 +76,26 @@ describe('createStoredScriptRoute', () => { expect(result.ok).not.toHaveBeenCalled(); }); + + it('return error if failed to create stored script', async () => { + (createStoredScript as jest.Mock).mockResolvedValue({ + [testScriptId]: { success: false, error: transformError(new Error('unknown error')) }, + }); + const request = requestMock.create({ + method: 'put', + path: RISK_SCORE_CREATE_STORED_SCRIPT, + body: { + id: testScriptId, + script: { + lang: 'painless', + source: testScriptSource, + }, + }, + }); + + const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(createStoredScript).toHaveBeenCalled(); + expect(response.status).toEqual(500); + expect(response.body).toEqual({ message: 'unknown error', status_code: 500 }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.ts b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.ts index 5719ca56a224d..0e19e160ea944 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/create_script_route.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { transformError } from '@kbn/securitysolution-es-utils'; +import type { Logger } from '@kbn/core/server'; import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; import { RISK_SCORE_CREATE_STORED_SCRIPT } from '../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { createStoredScriptBodySchema, createStoredScript } from './lib/create_script'; -export const createStoredScriptRoute = (router: SecuritySolutionPluginRouter) => { +export const createStoredScriptRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.put( { path: RISK_SCORE_CREATE_STORED_SCRIPT, @@ -23,19 +24,25 @@ export const createStoredScriptRoute = (router: SecuritySolutionPluginRouter) => async (context, request, response) => { const siemResponse = buildSiemResponse(response); const { client } = (await context.core).elasticsearch; + const esClient = client.asCurrentUser; const options = request.body; + try { - await createStoredScript({ - client, + const result = await createStoredScript({ + esClient, + logger, options, }); - return response.ok({ body: options }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + + const error = result[options.id].error; + if (error != null) { + return siemResponse.error({ statusCode: error.statusCode, body: error.message }); + } else { + return response.ok({ body: options }); + } + } catch (e) { + const error = transformError(e); + return siemResponse.error({ statusCode: error.statusCode, body: error.message }); } } ); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/lib/create_script.ts b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/lib/create_script.ts index e1380852f8df6..6617021d92d6d 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/lib/create_script.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/stored_scripts/lib/create_script.ts @@ -6,7 +6,8 @@ */ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { transformError } from '@kbn/securitysolution-es-utils'; export const createStoredScriptBodySchema = schema.object({ id: schema.string({ minLength: 1 }), @@ -26,11 +27,20 @@ export const createStoredScriptBodySchema = schema.object({ type CreateStoredScriptBodySchema = TypeOf; export const createStoredScript = async ({ - client, + esClient, + logger, options, }: { - client: IScopedClusterClient; + esClient: ElasticsearchClient; + logger: Logger; options: CreateStoredScriptBodySchema; }) => { - await client.asCurrentUser.putScript(options); + try { + await esClient.putScript(options); + return { [options.id]: { success: true, error: null } }; + } catch (error) { + const createScriptError = transformError(error); + logger.error(`Failed to create stored script: ${options.id}: ${createScriptError.message}`); + return { [options.id]: { success: false, error: createScriptError } }; + } }; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/transform/helpers/transforms.ts b/x-pack/plugins/security_solution/server/lib/risk_score/transform/helpers/transforms.ts new file mode 100644 index 0000000000000..eddd6e52251e9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/transform/helpers/transforms.ts @@ -0,0 +1,241 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { i18n } from '@kbn/i18n'; + +export const TRANSFORM_STATE = { + ABORTING: 'aborting', + FAILED: 'failed', + INDEXING: 'indexing', + STARTED: 'started', + STOPPED: 'stopped', + STOPPING: 'stopping', + WAITING: 'waiting', +} as const; + +export const createAndStartTransform = ({ + esClient, + transformId, + options, + logger, +}: { + esClient: ElasticsearchClient; + transformId: string; + options: string | Omit; + logger: Logger; +}) => { + const transformOptions = typeof options === 'string' ? JSON.parse(options) : options; + const transform = { + transform_id: transformId, + ...transformOptions, + }; + return createTransformIfNotExists(esClient, transform, logger).then((result) => { + if (result[transform.transform_id].success) { + return startTransformIfNotStarted(esClient, transform.transform_id, logger); + } else { + return result; + } + }); +}; +/** + * Checks if a transform exists, And if not creates it + * @param transform - the transform to create. If a transform with the same transform_id already exists, nothing is created. + */ +export const createTransformIfNotExists = async ( + esClient: ElasticsearchClient, + transform: TransformPutTransformRequest, + logger: Logger +) => { + try { + await esClient.transform.getTransform({ + transform_id: transform.transform_id, + }); + + logger.error(`Transform ${transform.transform_id} already exists`); + return { + [transform.transform_id]: { + success: false, + error: transformError( + new Error( + i18n.translate('xpack.securitySolution.riskScore.transform.transformExistsTitle', { + values: { transformId: transform.transform_id }, + defaultMessage: `Failed to create Transform as {transformId} already exists`, + }) + ) + ), + }, + }; + } catch (existErr) { + const existError = transformError(existErr); + if (existError.statusCode === 404) { + try { + await esClient.transform.putTransform(transform); + + return { [transform.transform_id]: { success: true, error: null } }; + } catch (createErr) { + const createError = transformError(createErr); + logger.error( + `Failed to create transform ${transform.transform_id}: ${createError.message}` + ); + return { [transform.transform_id]: { success: false, error: createError } }; + } + } else { + logger.error( + `Failed to check if transform ${transform.transform_id} exists before creation: ${existError.message}` + ); + return { [transform.transform_id]: { success: false, error: existError } }; + } + } +}; + +const checkTransformState = async ( + esClient: ElasticsearchClient, + transformId: string, + logger: Logger +) => { + try { + const transformStats = await esClient.transform.getTransformStats({ + transform_id: transformId, + }); + if (transformStats.count <= 0) { + logger.error(`Failed to check ${transformId} state: couldn't find transform`); + + return { + [transformId]: { + success: false, + error: transformError( + new Error( + i18n.translate('xpack.securitySolution.riskScore.transform.notFoundTitle', { + values: { transformId }, + defaultMessage: `Failed to check Transform state as {transformId} not found`, + }) + ) + ), + }, + }; + } + + return transformStats.transforms[0]; + } catch (statsErr) { + const statsError = transformError(statsErr); + + logger.error(`Failed to check if transform ${transformId} is started: ${statsError.message}`); + return { + [transformId]: { + success: false, + error: statsErr, + }, + }; + } +}; + +export const startTransformIfNotStarted = async ( + esClient: ElasticsearchClient, + transformId: string, + logger: Logger +) => { + const fetchedTransformStats = await checkTransformState(esClient, transformId, logger); + if (fetchedTransformStats.state === 'stopped') { + try { + await esClient.transform.startTransform({ transform_id: transformId }); + return { [transformId]: { success: true, error: null } }; + } catch (startErr) { + const startError = transformError(startErr); + + logger.error(`Failed starting transform ${transformId}: ${startError.message}`); + return { + [transformId]: { + success: false, + error: startError, + }, + }; + } + } else if ( + fetchedTransformStats.state === TRANSFORM_STATE.STOPPING || + fetchedTransformStats.state === TRANSFORM_STATE.ABORTING || + fetchedTransformStats.state === TRANSFORM_STATE.FAILED + ) { + logger.error( + `Not starting transform ${transformId} since it's state is: ${fetchedTransformStats.state}` + ); + return { + [transformId]: { + success: false, + error: transformError( + new Error( + i18n.translate('xpack.securitySolution.riskScore.transform.start.stateConflictTitle', { + values: { transformId, state: fetchedTransformStats.state }, + defaultMessage: `Not starting transform {transformId} since it's state is: {state}`, + }) + ) + ), + }, + }; + } +}; + +const stopTransform = async ( + esClient: ElasticsearchClient, + transformId: string, + logger: Logger +) => { + const fetchedTransformStats = await checkTransformState(esClient, transformId, logger); + if (fetchedTransformStats.state) { + try { + await esClient.transform.stopTransform({ + transform_id: transformId, + force: fetchedTransformStats.state === TRANSFORM_STATE.FAILED, + wait_for_completion: true, + }); + return { [transformId]: { success: true, error: null } }; + } catch (startErr) { + const startError = transformError(startErr); + + logger.error(`Failed stopping transform ${transformId}: ${startError.message}`); + return { + [transformId]: { + success: false, + error: startError, + }, + }; + } + } else { + logger.error( + `Not stopping transform ${transformId} since it's state is: ${fetchedTransformStats.state}` + ); + return { + [transformId]: { + success: false, + error: transformError( + new Error( + i18n.translate('xpack.securitySolution.riskScore.transform.stop.stateConflictTitle', { + values: { transformId, state: fetchedTransformStats.state }, + defaultMessage: `Not stopping transform {transformId} since it's state is: {state}`, + }) + ) + ), + }, + }; + } +}; + +export const restartTransform = ( + esClient: ElasticsearchClient, + transformId: string, + logger: Logger +) => { + return stopTransform(esClient, transformId, logger).then((result) => { + if (result[transformId].success) { + return startTransformIfNotStarted(esClient, transformId, logger); + } else { + return result; + } + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/transform/restart_transform.ts b/x-pack/plugins/security_solution/server/lib/risk_score/transform/restart_transform.ts new file mode 100644 index 0000000000000..9b08ea40da9bc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/risk_score/transform/restart_transform.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { Logger } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; + +import { RISK_SCORE_RESTART_TRANSFORMS } from '../../../../common/constants'; +import type { SecuritySolutionPluginRouter } from '../../../types'; + +import { buildSiemResponse } from '../../detection_engine/routes/utils'; + +import { RiskScoreEntity } from '../../../../common/search_strategy'; +import { restartTransform } from './helpers/transforms'; +import { + getRiskScoreLatestTransformId, + getRiskScorePivotTransformId, +} from '../../../../common/utils/risk_score_modules'; + +const restartRiskScoreTransformsSchema = { + body: schema.object({ + riskScoreEntity: schema.oneOf([ + schema.literal(RiskScoreEntity.host), + schema.literal(RiskScoreEntity.user), + ]), + }), +}; + +export const restartTransformRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { + router.post( + { + path: RISK_SCORE_RESTART_TRANSFORMS, + validate: restartRiskScoreTransformsSchema, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const { riskScoreEntity } = request.body; + + try { + const securitySolution = await context.securitySolution; + + const spaceId = securitySolution?.getSpaceId(); + + const { client } = (await context.core).elasticsearch; + const esClient = client.asCurrentUser; + const restartPivotTransformResult = await restartTransform( + esClient, + getRiskScorePivotTransformId(riskScoreEntity, spaceId), + logger + ); + + const restartLatestTransformResult = await restartTransform( + esClient, + getRiskScoreLatestTransformId(riskScoreEntity, spaceId), + logger + ); + + return response.ok({ + body: [restartPivotTransformResult, restartLatestTransformResult], + }); + } 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 449277cc53ed6..485047721ab18 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -82,7 +82,9 @@ import { deletePrebuiltSavedObjectsRoute, deleteStoredScriptRoute, getRiskScoreIndexStatusRoute, + installRiskScoresRoute, readPrebuiltDevToolContentRoute, + restartTransformRoute, } from '../lib/risk_score/routes'; export const initRoutes = ( router: SecuritySolutionPluginRouter, @@ -185,15 +187,16 @@ export const initRoutes = ( getSourcererDataViewRoute(router, getStartServices); // risky score module - createEsIndexRoute(router); + createEsIndexRoute(router, logger); deleteEsIndicesRoute(router); - createStoredScriptRoute(router); + createStoredScriptRoute(router, logger); deleteStoredScriptRoute(router); readPrebuiltDevToolContentRoute(router); - createPrebuiltSavedObjectsRoute(router, security); + createPrebuiltSavedObjectsRoute(router, logger, security); deletePrebuiltSavedObjectsRoute(router, security); getRiskScoreIndexStatusRoute(router); - + installRiskScoresRoute(router, logger, security); + restartTransformRoute(router, logger); const { previewTelemetryUrlEnabled } = config.experimentalFeatures; if (previewTelemetryUrlEnabled) { // telemetry preview endpoint for e2e integration tests only at the moment. diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts index 48c58a031db4a..f808f0fdf7a5f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts @@ -5,11 +5,17 @@ * 2.0. */ +import type { KibanaRequest } from '@kbn/core-http-server'; import type { IEsSearchResponse } from '@kbn/data-plugin/common'; +import { allowedExperimentalValues } from '../../../../../../../common/experimental_features'; import { Direction } from '../../../../../../../common/search_strategy'; import { UsersQueries } from '../../../../../../../common/search_strategy/security_solution/users'; import type { UsersRequestOptions } from '../../../../../../../common/search_strategy/security_solution/users/all'; import { UsersFields } from '../../../../../../../common/search_strategy/security_solution/users/common'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import type { EndpointAppContextService } from '../../../../../../endpoint/endpoint_app_context_services'; +import type { EndpointAppContext } from '../../../../../../endpoint/types'; export const mockOptions: UsersRequestOptions = { defaultIndex: ['test_indices*'], @@ -47,14 +53,14 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { }, aggregations: { user_count: { - value: 1, + value: 2, }, user_data: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ { - key: 'vagrant', + key: 'jose52', doc_count: 780, lastSeen: { value: 1644837532000, @@ -85,6 +91,38 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { }, }, }, + { + key: 'danny', + doc_count: 781, + lastSeen: { + value: 1644837532000, + value_as_string: '2022-02-14T11:18:52.000Z', + }, + domain: { + hits: { + total: { + value: 100, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: 'endgame-00001', + _id: 'inT0934BjUd1_U2597Vv', + _score: null, + fields: { + 'user.name': ['danny'], + '@timestamp': ['2022-04-13T17:16:34.540Z'], + 'user.id': ['18'], + 'user.email': ['danny@barrett.com'], + 'user.domain': ['ENDPOINT-W-8-04'], + }, + sort: [1644837532000], + }, + ], + }, + }, + }, ], }, }, @@ -94,3 +132,20 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { total: 2, loaded: 2, }; + +export const mockDeps = () => ({ + esClient: elasticsearchServiceMock.createScopedClusterClient(), + savedObjectsClient: {} as SavedObjectsClientContract, + endpointContext: { + logFactory: { + get: jest.fn(), + }, + config: jest.fn().mockResolvedValue({}), + experimentalFeatures: { + ...allowedExperimentalValues, + }, + service: {} as EndpointAppContextService, + } as EndpointAppContext, + request: {} as KibanaRequest, + spaceId: 'test-space', +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap index 6383808293a9d..138cc45011c8c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap @@ -7,17 +7,24 @@ Array [ "ENDPOINT-W-8-03", ], "lastSeen": "2022-02-14T11:18:52.000Z", - "name": "vagrant", + "name": "jose52", + }, + Object { + "domain": Array [ + "ENDPOINT-W-8-04", + ], + "lastSeen": "2022-02-14T11:18:52.000Z", + "name": "danny", }, ] `; -exports[`allHosts search strategy parse should parse data correctly 2`] = `1`; +exports[`allHosts search strategy parse should parse data correctly 2`] = `2`; exports[`allHosts search strategy parse should parse data correctly 3`] = ` Object { "activePage": 0, - "fakeTotalCount": 1, + "fakeTotalCount": 2, "showMorePagesIndicator": false, } `; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.test.ts index 7ac81811ff903..1f17bbfc870fc 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.test.ts @@ -9,8 +9,20 @@ import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants import * as buildQuery from './query.all_users.dsl'; import { allUsers } from '.'; -import { mockOptions, mockSearchStrategyResponse } from './__mocks__'; +import { mockDeps, mockOptions, mockSearchStrategyResponse } from './__mocks__'; import type { UsersRequestOptions } from '../../../../../../common/search_strategy/security_solution/users/all'; +import * as buildRiskQuery from '../../risk_score/all/query.risk_score.dsl'; + +import { get } from 'lodash/fp'; + +class IndexNotFoundException extends Error { + meta: { body: { error: { type: string } } }; + + constructor() { + super(); + this.meta = { body: { error: { type: 'index_not_found_exception' } } }; + } +} describe('allHosts search strategy', () => { const buildAllHostsQuery = jest.spyOn(buildQuery, 'buildUsersQuery'); @@ -47,5 +59,74 @@ describe('allHosts search strategy', () => { expect(result.totalCount).toMatchSnapshot(); expect(result.pageInfo).toMatchSnapshot(); }); + + test('should enhance data with risk score', async () => { + const risk = 'TEST_RISK_SCORE'; + const userName: string = get( + `aggregations.user_data.buckets[0].domain.hits.hits[0].fields['user.name']`, + mockSearchStrategyResponse.rawResponse + ); + + const mockedDeps = mockDeps(); + + mockedDeps.esClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [ + // @ts-expect-error incomplete type + { + _source: { + risk, + user: { + name: userName, + risk: { + multipliers: [], + calculated_score_norm: 9999, + calculated_level: risk, + rule_risks: [], + }, + }, + }, + }, + ], + }, + }); + + const result = await allUsers.parse(mockOptions, mockSearchStrategyResponse, mockedDeps); + + expect(result.users[0].risk).toBe(risk); + }); + + test('should query host risk only for hostNames in the current page', async () => { + const buildHostsRiskQuery = jest.spyOn(buildRiskQuery, 'buildRiskScoreQuery'); + const mockedDeps = mockDeps(); + // @ts-expect-error incomplete type + mockedDeps.esClient.asCurrentUser.search.mockResponse({ hits: { hits: [] } }); + + const userName: string = get( + `aggregations.user_data.buckets[1].domain.hits.hits[0].fields['user.name']`, + mockSearchStrategyResponse.rawResponse + ); + + // 2 pages with one item on each + const pagination = { activePage: 1, cursorStart: 1, fakePossibleCount: 5, querySize: 2 }; + + await allUsers.parse({ ...mockOptions, pagination }, mockSearchStrategyResponse, mockedDeps); + + expect(buildHostsRiskQuery).toHaveBeenCalledWith({ + defaultIndex: ['ml_user_risk_score_latest_test-space'], + filterQuery: { terms: { 'user.name': userName } }, + }); + }); + + test("should not enhance data when index doesn't exist", async () => { + const mockedDeps = mockDeps(); + mockedDeps.esClient.asCurrentUser.search.mockImplementation(() => { + throw new IndexNotFoundException(); + }); + + const result = await allUsers.parse(mockOptions, mockSearchStrategyResponse, mockedDeps); + + expect(result.users[0].risk).toBeUndefined(); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts index 254cc9f06879f..d5c13e0d2b52e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts @@ -8,6 +8,7 @@ import { getOr } from 'lodash/fp'; import type { IEsSearchResponse } from '@kbn/data-plugin/common'; +import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { inspectStringifyObject } from '../../../../../utils/build_query'; @@ -15,10 +16,14 @@ import type { SecuritySolutionFactory } from '../../types'; import { buildUsersQuery } from './query.all_users.dsl'; import type { UsersQueries } from '../../../../../../common/search_strategy/security_solution/users'; import type { + User, UsersRequestOptions, UsersStrategyResponse, } from '../../../../../../common/search_strategy/security_solution/users/all'; import type { AllUsersAggEsItem } from '../../../../../../common/search_strategy/security_solution/users/common'; +import { buildRiskScoreQuery } from '../../risk_score/all/query.risk_score.dsl'; +import type { RiskSeverity, UserRiskScore } from '../../../../../../common/search_strategy'; +import { buildUserNamesFilter, getUserRiskIndex } from '../../../../../../common/search_strategy'; export const allUsers: SecuritySolutionFactory = { buildDsl: (options: UsersRequestOptions) => { @@ -29,7 +34,12 @@ export const allUsers: SecuritySolutionFactory = { }, parse: async ( options: UsersRequestOptions, - response: IEsSearchResponse + response: IEsSearchResponse, + deps?: { + esClient: IScopedClusterClient; + spaceId?: string; + // endpointContext: EndpointAppContext; + } ): Promise => { const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; const inspect = { @@ -46,7 +56,7 @@ export const allUsers: SecuritySolutionFactory = { const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; - const users = buckets.map( + const users: User[] = buckets.map( (bucket: AllUsersAggEsItem) => ({ name: bucket.key, lastSeen: getOr(null, `lastSeen.value_as_string`, bucket), @@ -56,11 +66,18 @@ export const allUsers: SecuritySolutionFactory = { ); const showMorePagesIndicator = totalCount > fakeTotalCount; + + const edges = users.splice(cursorStart, querySize - cursorStart); + const userNames = edges.map(({ name }) => name); + const enhancedEdges = deps?.spaceId + ? await enhanceEdges(edges, userNames, deps.spaceId, deps.esClient) + : edges; + return { ...response, inspect, totalCount, - users: users.splice(cursorStart, querySize - cursorStart), + users: enhancedEdges, pageInfo: { activePage: activePage ?? 0, fakeTotalCount, @@ -69,3 +86,50 @@ export const allUsers: SecuritySolutionFactory = { }; }, }; + +async function enhanceEdges( + edges: User[], + userNames: string[], + spaceId: string, + esClient: IScopedClusterClient +): Promise { + const userRiskData = await getUserRiskData(esClient, spaceId, userNames); + const usersRiskByUserName: Record | undefined = + userRiskData?.hits.hits.reduce( + (acc, hit) => ({ + ...acc, + [hit._source?.user.name ?? '']: hit._source?.user?.risk?.calculated_level, + }), + {} + ); + + return usersRiskByUserName + ? edges.map(({ name, lastSeen, domain }) => ({ + name, + lastSeen, + domain, + risk: usersRiskByUserName[name ?? ''], + })) + : edges; +} + +async function getUserRiskData( + esClient: IScopedClusterClient, + spaceId: string, + userNames: string[] +) { + try { + const userRiskResponse = await esClient.asCurrentUser.search( + buildRiskScoreQuery({ + defaultIndex: [getUserRiskIndex(spaceId)], + filterQuery: buildUserNamesFilter(userNames), + }) + ); + return userRiskResponse; + } catch (error) { + if (error?.meta?.body?.error?.type !== 'index_not_found_exception') { + throw error; + } + return undefined; + } +} diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index 0b753a80a707c..cd3c87debf990 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -99,8 +99,8 @@ export function ProcessTreeNode({ }, [selectedProcess, process, childrenExpanded]); const alerts = process.getAlerts(); - const hasAlerts = useMemo(() => !!alerts.length, [alerts]); - const hasOutputs = useMemo(() => process.hasOutput(), [process]); + const hasAlerts = !!alerts.length; + const hasOutputs = process.hasOutput(); const hasInvestigatedAlert = useMemo( () => !!( diff --git a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts index aaa5addcc6358..159576fa10dce 100644 --- a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts +++ b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts @@ -23,6 +23,7 @@ export interface BuildSortedEventsQuery extends BuildSortedEventsQueryOpts { timeField: string; fields?: string[]; runtime_mappings?: unknown; + _source?: unknown; } export const buildSortedEventsQuery = ({ @@ -40,6 +41,7 @@ export const buildSortedEventsQuery = ({ fields, // eslint-disable-next-line @typescript-eslint/naming-convention runtime_mappings, + _source, }: BuildSortedEventsQuery): ESSearchRequest => { const sortField = timeField; const docFields = [timeField].map((tstamp) => ({ @@ -89,6 +91,7 @@ export const buildSortedEventsQuery = ({ }, ...(runtime_mappings ? { runtime_mappings } : {}), ...(fields ? { fields } : {}), + ...(_source != null ? { _source } : {}), }; if (searchAfterSortId) { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/executor.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/executor.ts index 5f33eeb0af845..b52f5803405a7 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/executor.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/executor.ts @@ -29,6 +29,8 @@ export async function executor( const currentTimestamp = new Date().toISOString(); const publicBaseUrl = core.http.basePath.publicBaseUrl ?? ''; + const alertLimit = alertFactory.alertLimit.getValue(); + const compareFn = ComparatorFns.get(params.thresholdComparator); if (compareFn == null) { throw new Error(getInvalidComparatorError(params.thresholdComparator)); @@ -91,6 +93,12 @@ export async function executor( if (firstValidTimefieldSort) { latestTimestamp = firstValidTimefieldSort; } + + // we only create one alert if the condition is met, so we would only ever + // reach the alert limit if the limit is less than 1 + alertFactory.alertLimit.setLimitReached(alertLimit < 1); + } else { + alertFactory.alertLimit.setLimitReached(false); } const { getRecoveredAlerts } = alertFactory.done(); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts index d6f373feb65f6..3e94745d295fb 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts @@ -28,7 +28,7 @@ export async function fetchEsQuery( const esClient = scopedClusterClient.asCurrentUser; const { // eslint-disable-next-line @typescript-eslint/naming-convention - parsedQuery: { query, fields, runtime_mappings }, + parsedQuery: { query, fields, runtime_mappings, _source }, dateStart, dateEnd, } = getSearchParams(params); @@ -76,6 +76,7 @@ export async function fetchEsQuery( track_total_hits: true, fields, runtime_mappings, + _source, }); logger.debug( diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.test.ts index 4d8c1dc3d9b9f..0df74bb2f89c5 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.test.ts @@ -6,7 +6,7 @@ */ import { BaseActionContext, addMessages } from './action_context'; -import { ParamsSchema } from './alert_type_params'; +import { ParamsSchema } from './rule_type_params'; describe('ActionContext', () => { it('generates expected properties if aggField is null', async () => { @@ -28,10 +28,10 @@ describe('ActionContext', () => { value: 42, conditions: 'count greater than 4', }; - const context = addMessages({ name: '[alert-name]' }, base, params); - expect(context.title).toMatchInlineSnapshot(`"alert [alert-name] group [group] met threshold"`); + const context = addMessages({ name: '[rule-name]' }, base, params); + expect(context.title).toMatchInlineSnapshot(`"alert [rule-name] group [group] met threshold"`); expect(context.message).toEqual( - `alert '[alert-name]' is active for group '[group]': + `alert '[rule-name]' is active for group '[group]': - Value: 42 - Conditions Met: count greater than 4 over 5m @@ -59,10 +59,10 @@ describe('ActionContext', () => { value: 42, conditions: 'avg([aggField]) greater than 4.2', }; - const context = addMessages({ name: '[alert-name]' }, base, params); - expect(context.title).toMatchInlineSnapshot(`"alert [alert-name] group [group] met threshold"`); + const context = addMessages({ name: '[rule-name]' }, base, params); + expect(context.title).toMatchInlineSnapshot(`"alert [rule-name] group [group] met threshold"`); expect(context.message).toEqual( - `alert '[alert-name]' is active for group '[group]': + `alert '[rule-name]' is active for group '[group]': - Value: 42 - Conditions Met: avg([aggField]) greater than 4.2 over 5m @@ -89,10 +89,10 @@ describe('ActionContext', () => { value: 4, conditions: 'count between 4 and 5', }; - const context = addMessages({ name: '[alert-name]' }, base, params); - expect(context.title).toMatchInlineSnapshot(`"alert [alert-name] group [group] met threshold"`); + const context = addMessages({ name: '[rule-name]' }, base, params); + expect(context.title).toMatchInlineSnapshot(`"alert [rule-name] group [group] met threshold"`); expect(context.message).toEqual( - `alert '[alert-name]' is active for group '[group]': + `alert '[rule-name]' is active for group '[group]': - Value: 4 - Conditions Met: count between 4 and 5 over 5m @@ -119,10 +119,10 @@ describe('ActionContext', () => { value: 'unknown', conditions: 'count between 4 and 5', }; - const context = addMessages({ name: '[alert-name]' }, base, params); - expect(context.title).toMatchInlineSnapshot(`"alert [alert-name] group [group] met threshold"`); + const context = addMessages({ name: '[rule-name]' }, base, params); + expect(context.title).toMatchInlineSnapshot(`"alert [rule-name] group [group] met threshold"`); expect(context.message).toEqual( - `alert '[alert-name]' is active for group '[group]': + `alert '[rule-name]' is active for group '[group]': - Value: unknown - Conditions Met: count between 4 and 5 over 5m diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.ts index 94e7f9b8501a4..36ed27d8a7391 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/action_context.ts @@ -7,9 +7,9 @@ import { i18n } from '@kbn/i18n'; import { RuleExecutorOptions, AlertInstanceContext } from '@kbn/alerting-plugin/server'; -import { Params } from './alert_type_params'; +import { Params } from './rule_type_params'; -// alert type context provided to actions +// rule type context provided to actions type RuleInfo = Pick; @@ -21,10 +21,10 @@ export interface ActionContext extends BaseActionContext { } export interface BaseActionContext extends AlertInstanceContext { - // the aggType used in the alert + // the aggType used in the rule // the value of the aggField, if used, otherwise 'all documents' group: string; - // the date the alert was run as an ISO date + // the date the rule was run as an ISO date date: string; // the value that met the threshold value: number | string; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts index 065ef6b5ee22c..449c6528798a6 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts @@ -7,7 +7,7 @@ import { Logger } from '@kbn/core/server'; import { AlertingSetup, StackAlertsStartDeps } from '../../types'; -import { getAlertType } from './alert_type'; +import { getRuleType } from './rule_type'; // future enhancement: make these configurable? export const MAX_INTERVALS = 1000; @@ -22,5 +22,5 @@ interface RegisterParams { export function register(params: RegisterParams) { const { logger, data, alerting } = params; - alerting.registerType(getAlertType(logger, data)); + alerting.registerType(getRuleType(logger, data)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.test.ts similarity index 73% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts rename to x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.test.ts index 1751fc6fc2344..656a1e5f275e5 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.test.ts @@ -6,34 +6,44 @@ */ import uuid from 'uuid'; +import sinon from 'sinon'; import type { Writable } from '@kbn/utility-types'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { RuleExecutorServices } from '@kbn/alerting-plugin/server'; -import { getAlertType, ActionGroupId } from './alert_type'; +import { getRuleType, ActionGroupId } from './rule_type'; import { ActionContext } from './action_context'; -import { Params } from './alert_type_params'; +import { Params } from './rule_type_params'; +import { TIME_SERIES_BUCKET_SELECTOR_FIELD } from '@kbn/triggers-actions-ui-plugin/server'; import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { Comparator } from '../../../common/comparator_types'; -describe('alertType', () => { +let fakeTimer: sinon.SinonFakeTimers; + +describe('ruleType', () => { const logger = loggingSystemMock.create().get(); const data = { timeSeriesQuery: jest.fn(), }; const alertServices: RuleExecutorServicesMock = alertsMock.createRuleExecutorServices(); - const alertType = getAlertType(logger, Promise.resolve(data)); + const ruleType = getRuleType(logger, Promise.resolve(data)); + + beforeAll(() => { + fakeTimer = sinon.useFakeTimers(); + }); afterEach(() => { data.timeSeriesQuery.mockReset(); }); - it('alert type creation structure is the expected value', async () => { - expect(alertType.id).toBe('.index-threshold'); - expect(alertType.name).toBe('Index threshold'); - expect(alertType.actionGroups).toEqual([{ id: 'threshold met', name: 'Threshold met' }]); + afterAll(() => fakeTimer.restore()); - expect(alertType.actionVariables).toMatchInlineSnapshot(` + it('rule type creation structure is the expected value', async () => { + expect(ruleType.id).toBe('.index-threshold'); + expect(ruleType.name).toBe('Index threshold'); + expect(ruleType.actionGroups).toEqual([{ id: 'threshold met', name: 'Threshold met' }]); + + expect(ruleType.actionVariables).toMatchInlineSnapshot(` Object { "context": Array [ Object { @@ -123,11 +133,11 @@ describe('alertType', () => { threshold: [0], }; - expect(alertType.validate?.params?.validate(params)).toBeTruthy(); + expect(ruleType.validate?.params?.validate(params)).toBeTruthy(); }); it('validator fails with invalid params', async () => { - const paramsSchema = alertType.validate?.params; + const paramsSchema = ruleType.validate?.params; if (!paramsSchema) throw new Error('params validator not set'); const params: Partial> = { @@ -168,7 +178,7 @@ describe('alertType', () => { threshold: [1], }; - await alertType.executor({ + await ruleType.executor({ alertId: uuid.v4(), executionId: uuid.v4(), startedAt: new Date(), @@ -234,7 +244,7 @@ describe('alertType', () => { threshold: [1], }; - await alertType.executor({ + await ruleType.executor({ alertId: uuid.v4(), executionId: uuid.v4(), startedAt: new Date(), @@ -300,7 +310,7 @@ describe('alertType', () => { threshold: [1], }; - await alertType.executor({ + await ruleType.executor({ alertId: uuid.v4(), executionId: uuid.v4(), startedAt: new Date(), @@ -342,4 +352,90 @@ describe('alertType', () => { expect(customAlertServices.alertFactory.create).not.toHaveBeenCalled(); }); + + it('should correctly pass comparator script to timeSeriesQuery', async () => { + data.timeSeriesQuery.mockImplementation((...args) => { + return { + results: [ + { + group: 'all documents', + metrics: [['2021-07-14T14:49:30.978Z', 0]], + }, + ], + }; + }); + const params: Params = { + index: 'index-name', + timeField: 'time-field', + aggType: 'foo', + groupBy: 'all', + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: Comparator.LT, + threshold: [1], + }; + + await ruleType.executor({ + alertId: uuid.v4(), + executionId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: alertServices as unknown as RuleExecutorServices< + {}, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + rule: { + name: uuid.v4(), + tags: [], + consumer: '', + producer: '', + ruleTypeId: '', + ruleTypeName: '', + enabled: true, + schedule: { + interval: '1h', + }, + actions: [], + createdBy: null, + updatedBy: null, + createdAt: new Date(), + updatedAt: new Date(), + throttle: null, + notifyWhen: null, + }, + }); + + expect(data.timeSeriesQuery).toHaveBeenCalledWith( + expect.objectContaining({ + query: { + aggField: undefined, + aggType: 'foo', + dateEnd: '1970-01-01T00:00:00.000Z', + dateStart: '1970-01-01T00:00:00.000Z', + groupBy: 'all', + index: 'index-name', + interval: undefined, + termField: undefined, + termSize: undefined, + timeField: 'time-field', + timeWindowSize: 5, + timeWindowUnit: 'm', + }, + condition: { + conditionScript: `${TIME_SERIES_BUCKET_SELECTOR_FIELD} < 1L`, + resultLimit: 1000, + }, + }) + ); + }); }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.ts similarity index 88% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts rename to x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.ts index 58d680794aedf..3b3480407fcfc 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.ts @@ -10,21 +10,23 @@ import { Logger } from '@kbn/core/server'; import { CoreQueryParamsSchemaProperties, TimeSeriesQuery, + TIME_SERIES_BUCKET_SELECTOR_FIELD, } from '@kbn/triggers-actions-ui-plugin/server'; import { RuleType, RuleExecutorOptions, StackAlertsStartDeps } from '../../types'; -import { Params, ParamsSchema } from './alert_type_params'; +import { Params, ParamsSchema } from './rule_type_params'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { ComparatorFns, getHumanReadableComparator } from '../lib'; +import { getComparatorScript } from '../lib/comparator'; export const ID = '.index-threshold'; export const ActionGroupId = 'threshold met'; -export function getAlertType( +export function getRuleType( logger: Logger, data: Promise ): RuleType { - const alertTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { + const ruleTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { defaultMessage: 'Index threshold', }); @@ -92,7 +94,7 @@ export function getAlertType( } ); - const alertParamsVariables = Object.keys(CoreQueryParamsSchemaProperties).map( + const ruleParamsVariables = Object.keys(CoreQueryParamsSchemaProperties).map( (propKey: string) => { return { name: propKey, @@ -103,7 +105,7 @@ export function getAlertType( return { id: ID, - name: alertTypeName, + name: ruleTypeName, actionGroups: [{ id: ActionGroupId, name: actionGroupName }], defaultActionGroupId: ActionGroupId, validate: { @@ -121,7 +123,7 @@ export function getAlertType( params: [ { name: 'threshold', description: actionVariableContextThresholdLabel }, { name: 'thresholdComparator', description: actionVariableContextThresholdComparatorLabel }, - ...alertParamsVariables, + ...ruleParamsVariables, ], }, minimumLicenseRequired: 'basic', @@ -137,6 +139,8 @@ export function getAlertType( const { alertId: ruleId, name, services, params } = options; const { alertFactory, scopedClusterClient } = services; + const alertLimit = alertFactory.alertLimit.getValue(); + const compareFn = ComparatorFns.get(params.thresholdComparator); if (compareFn == null) { throw new Error( @@ -173,9 +177,19 @@ export function getAlertType( logger, esClient, query: queryParams, + condition: { + resultLimit: alertLimit, + conditionScript: getComparatorScript( + params.thresholdComparator, + params.threshold, + TIME_SERIES_BUCKET_SELECTOR_FIELD + ), + }, }); logger.debug(`rule ${ID}:${ruleId} "${name}" query result: ${JSON.stringify(result)}`); + const isGroupAgg = !!queryParams.termField; + const unmetGroupValues: Record = {}; const agg = params.aggField ? `${params.aggType}(${params.aggField})` : `${params.aggType}`; @@ -196,7 +210,10 @@ export function getAlertType( continue; } - const met = compareFn(value, params.threshold); + // group aggregations use the bucket selector agg to compare conditions + // within the ES query, so only 'met' results are returned, therefore we don't need + // to use the compareFn + const met = isGroupAgg ? true : compareFn(value, params.threshold); if (!met) { unmetGroupValues[alertId] = value; @@ -219,6 +236,8 @@ export function getAlertType( logger.debug(`scheduled actionGroup: ${JSON.stringify(actionContext)}`); } + alertFactory.alertLimit.setLimitReached(result.truncated); + const { getRecoveredAlerts } = services.alertFactory.done(); for (const recoveredAlert of getRecoveredAlerts()) { const alertId = recoveredAlert.getId(); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.test.ts similarity index 98% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.test.ts rename to x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.test.ts index 7bcf84db20a1f..c4dd8f2149255 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ParamsSchema, Params } from './alert_type_params'; +import { ParamsSchema, Params } from './rule_type_params'; import { ObjectType, TypeOf } from '@kbn/config-schema'; import type { Writable } from '@kbn/utility-types'; import { CoreQueryParams, MAX_GROUPS } from '@kbn/triggers-actions-ui-plugin/server'; @@ -22,7 +22,7 @@ const DefaultParams: Writable> = { threshold: [0], }; -describe('alertType Params validate()', () => { +describe('ruleType Params validate()', () => { runTests(ParamsSchema, DefaultParams); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.ts similarity index 98% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts rename to x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.ts index 5783b6a64ab83..9018d915f4e48 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type_params.ts @@ -15,7 +15,7 @@ import { ComparatorFnNames } from '../lib'; import { Comparator } from '../../../common/comparator_types'; import { getComparatorSchemaType } from '../lib/comparator'; -// alert type parameters +// rule type parameters export type Params = TypeOf; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.test.ts new file mode 100644 index 0000000000000..62447c12fdf49 --- /dev/null +++ b/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.test.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getComparatorScript } from './comparator'; +import { Comparator } from '../../../common/comparator_types'; + +describe('getComparatorScript', () => { + it('correctly returns script when comparator is LT', () => { + expect(getComparatorScript(Comparator.LT, [10], 'fieldName')).toEqual(`fieldName < 10L`); + }); + it('correctly returns script when comparator is LT_OR_EQ', () => { + expect(getComparatorScript(Comparator.LT_OR_EQ, [10], 'fieldName')).toEqual(`fieldName <= 10L`); + }); + it('correctly returns script when comparator is GT', () => { + expect(getComparatorScript(Comparator.GT, [10], 'fieldName')).toEqual(`fieldName > 10L`); + }); + it('correctly returns script when comparator is GT_OR_EQ', () => { + expect(getComparatorScript(Comparator.GT_OR_EQ, [10], 'fieldName')).toEqual(`fieldName >= 10L`); + }); + it('correctly returns script when comparator is BETWEEN', () => { + expect(getComparatorScript(Comparator.BETWEEN, [10, 100], 'fieldName')).toEqual( + `fieldName >= 10L && fieldName <= 100L` + ); + }); + it('correctly returns script when comparator is NOT_BETWEEN', () => { + expect(getComparatorScript(Comparator.NOT_BETWEEN, [10, 100], 'fieldName')).toEqual( + `fieldName < 10L || fieldName > 100L` + ); + }); + it('correctly returns script when threshold is float', () => { + expect(getComparatorScript(Comparator.LT, [3.5454], 'fieldName')).toEqual(`fieldName < 3.5454`); + }); + it('throws error when threshold is empty', () => { + expect(() => { + getComparatorScript(Comparator.LT, [], 'fieldName'); + }).toThrowErrorMatchingInlineSnapshot(`"Threshold value required"`); + }); + it('throws error when comparator requires two thresholds and two thresholds are not defined', () => { + expect(() => { + getComparatorScript(Comparator.BETWEEN, [1], 'fieldName'); + }).toThrowErrorMatchingInlineSnapshot(`"Threshold values required"`); + }); +}); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.ts b/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.ts index 524e3a7554dc2..dbdc310b88038 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/lib/comparator.ts @@ -34,6 +34,45 @@ export const ComparatorFns = new Map([ ], ]); +export const getComparatorScript = ( + comparator: Comparator, + threshold: number[], + fieldName: string +) => { + if (threshold.length === 0) { + throw new Error('Threshold value required'); + } + + function getThresholdString(thresh: number) { + return Number.isInteger(thresh) ? `${thresh}L` : `${thresh}`; + } + + switch (comparator) { + case Comparator.LT: + return `${fieldName} < ${getThresholdString(threshold[0])}`; + case Comparator.LT_OR_EQ: + return `${fieldName} <= ${getThresholdString(threshold[0])}`; + case Comparator.GT: + return `${fieldName} > ${getThresholdString(threshold[0])}`; + case Comparator.GT_OR_EQ: + return `${fieldName} >= ${getThresholdString(threshold[0])}`; + case Comparator.BETWEEN: + if (threshold.length < 2) { + throw new Error('Threshold values required'); + } + return `${fieldName} >= ${getThresholdString( + threshold[0] + )} && ${fieldName} <= ${getThresholdString(threshold[1])}`; + case Comparator.NOT_BETWEEN: + if (threshold.length < 2) { + throw new Error('Threshold values required'); + } + return `${fieldName} < ${getThresholdString( + threshold[0] + )} || ${fieldName} > ${getThresholdString(threshold[1])}`; + } +}; + export const getComparatorSchemaType = (validate: (comparator: Comparator) => string | void) => schema.oneOf( [ diff --git a/x-pack/plugins/stack_alerts/server/feature.ts b/x-pack/plugins/stack_alerts/server/feature.ts index f7257651f2aeb..9005a8435657b 100644 --- a/x-pack/plugins/stack_alerts/server/feature.ts +++ b/x-pack/plugins/stack_alerts/server/feature.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { KibanaFeatureConfig } from '@kbn/features-plugin/common'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import { TRANSFORM_RULE_TYPE } from '@kbn/transform-plugin/common'; -import { ID as IndexThreshold } from './alert_types/index_threshold/alert_type'; +import { ID as IndexThreshold } from './alert_types/index_threshold/rule_type'; import { GEO_CONTAINMENT_ID as GeoContainment } from './alert_types/geo_containment/alert_type'; import { ES_QUERY_ID as ElasticsearchQuery } from './alert_types/es_query/constants'; import { STACK_ALERTS_FEATURE_ID } from '../common'; diff --git a/x-pack/plugins/stack_alerts/server/index.ts b/x-pack/plugins/stack_alerts/server/index.ts index 8f18ac9a4df9a..5483124209028 100644 --- a/x-pack/plugins/stack_alerts/server/index.ts +++ b/x-pack/plugins/stack_alerts/server/index.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; import { AlertingBuiltinsPlugin } from './plugin'; import { configSchema, Config } from '../common/config'; -export { ID as INDEX_THRESHOLD_ID } from './alert_types/index_threshold/alert_type'; +export { ID as INDEX_THRESHOLD_ID } from './alert_types/index_threshold/rule_type'; export const config: PluginConfigDescriptor = { exposeToBrowser: {}, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/stack/es_index/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/es_index/index.ts index 6346e166f97ac..d66f60233f49f 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/stack/es_index/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/es_index/index.ts @@ -148,7 +148,7 @@ async function executor( function renderParameterTemplates( params: ActionParamsType, variables: Record, - actionId: string + actionId?: string ): ActionParamsType { const { documents, indexOverride } = renderMustacheObject(params, variables); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/single_ping_result.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/single_ping_result.tsx index 89fb255dc80ef..bb0666abbab26 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/single_ping_result.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/single_ping_result.tsx @@ -24,7 +24,7 @@ export const SinglePingResult = ({ ping, loading }: { ping: Ping; loading: boole const responseStatus = !loading ? ping?.http?.response?.status_code : undefined; return ( - + IP {ip} {DURATION_LABEL} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_details_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_details_panel.tsx index 3d8013c79beff..2fd702a187e36 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_details_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_details_panel.tsx @@ -55,7 +55,7 @@ export const MonitorDetailsPanel = () => { return (
- + {ENABLED_LABEL} {monitor && ( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/step_duration_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/step_duration_panel.tsx index 671e661f4eb06..120449b88bd04 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/step_duration_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/step_duration_panel.tsx @@ -12,6 +12,7 @@ import { ReportTypes } from '@kbn/observability-plugin/public'; import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import { useSelectedMonitor } from '../hooks/use_selected_monitor'; import { ClientPluginsStart } from '../../../../../plugin'; export const StepDurationPanel = () => { const { observability } = useKibana().services; @@ -20,12 +21,16 @@ export const StepDurationPanel = () => { const { monitorId } = useParams<{ monitorId: string }>(); + const { monitor } = useSelectedMonitor(); + + const isBrowser = monitor?.type === 'browser'; + return ( -

{DURATION_BY_STEP_LABEL}

+

{isBrowser ? DURATION_BY_STEP_LABEL : DURATION_BY_LOCATION}

@@ -43,10 +48,10 @@ export const StepDurationPanel = () => { { name: DURATION_BY_STEP_LABEL, reportDefinitions: { 'monitor.id': [monitorId] }, - selectedMetricField: 'synthetics.step.duration.us', + selectedMetricField: isBrowser ? 'synthetics.step.duration.us' : 'monitor.duration.us', dataType: 'synthetics', time: { from: 'now-24h/h', to: 'now' }, - breakdown: 'synthetics.step.name.keyword', + breakdown: isBrowser ? 'synthetics.step.name.keyword' : 'observer.geo.name', operationType: 'last_value', seriesType: 'area_stacked', }, @@ -60,6 +65,10 @@ const DURATION_BY_STEP_LABEL = i18n.translate('xpack.synthetics.detailsPanel.dur defaultMessage: 'Duration by step', }); +const DURATION_BY_LOCATION = i18n.translate('xpack.synthetics.detailsPanel.durationByLocation', { + defaultMessage: 'Duration by location', +}); + const LAST_24H_LABEL = i18n.translate('xpack.synthetics.detailsPanel.last24Hours', { defaultMessage: 'Last 24 hours', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts index f2541b119e56d..80713e587cefa 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts @@ -51,8 +51,8 @@ export const fetchSyntheticsMonitor = async ({ )) as SavedObject; return { - id: savedObject.id, ...savedObject.attributes, + id: savedObject.id, updated_at: savedObject.updated_at, } as EncryptedSyntheticsSavedMonitor; }; diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 9a47ad9de093e..890e4c8a5e4f1 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -4109,6 +4109,9 @@ "items": { "type": "keyword" } + }, + "total": { + "type": "long" } } }, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx index 70fe613c181ac..b63e33fbe51e8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx @@ -8,6 +8,7 @@ import React, { useState, VFC } from 'react'; import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { CopyToClipboardContextMenu } from '../../copy_to_clipboard'; import { FilterInContextMenu } from '../../../../query_bar/components/filter_in'; import { FilterOutContextMenu } from '../../../../query_bar/components/filter_out'; import { AddToTimelineContextMenu } from '../../../../timeline/components/add_to_timeline'; @@ -16,6 +17,7 @@ export const POPOVER_BUTTON_TEST_ID = 'tiBarchartPopoverButton'; export const TIMELINE_BUTTON_TEST_ID = 'tiBarchartTimelineButton'; export const FILTER_IN_BUTTON_TEST_ID = 'tiBarchartFilterInButton'; export const FILTER_OUT_BUTTON_TEST_ID = 'tiBarchartFilterOutButton'; +export const COPY_TO_CLIPBOARD_BUTTON_TEST_ID = 'tiBarchartCopyToClipboardButton'; const BUTTON_LABEL = i18n.translate('xpack.threatIntelligence.indicator.barChart.popover', { defaultMessage: 'More actions', @@ -42,6 +44,7 @@ export const IndicatorBarchartLegendAction: VFC, , , + , ]; return ( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/__snapshots__/copy_to_clipboard.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/__snapshots__/copy_to_clipboard.test.tsx.snap new file mode 100644 index 0000000000000..ee6b9bd2b42f1 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/__snapshots__/copy_to_clipboard.test.tsx.snap @@ -0,0 +1,225 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render one EuiButtonEmtpy 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.stories.tsx new file mode 100644 index 0000000000000..8c9a3ccbeee81 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.stories.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Story } from '@storybook/react'; +import { EuiContextMenuPanel } from '@elastic/eui'; +import { CopyToClipboardButtonEmpty, CopyToClipboardContextMenu } from '.'; + +export default { + title: 'CopyToClipboard', +}; + +const mockValue: string = 'Text copied!'; + +export const ButtonEmpty: Story = () => { + return ; +}; + +export const ContextMenu: Story = () => { + const items = []; + + return ; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.test.tsx new file mode 100644 index 0000000000000..c54c205274b4c --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.test.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { CopyToClipboardButtonEmpty, CopyToClipboardContextMenu } from '.'; + +const mockValue: string = 'Text copied'; + +const mockTestId: string = 'abc'; + +describe(' ', () => { + it('should render one EuiButtonEmtpy', () => { + const component = render( + + ); + + expect(component.getByTestId(mockTestId)).toBeInTheDocument(); + expect(component).toMatchSnapshot(); + }); + + it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.tsx new file mode 100644 index 0000000000000..6041a10cddac7 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/copy_to_clipboard.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { VFC } from 'react'; +import { EuiButtonEmpty, EuiContextMenuItem, EuiCopy } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +const COPY_ICON = 'copyClipboard'; +const COPY_TITLE = i18n.translate( + 'xpack.threatIntelligence.indicators.table.copyToClipboardLabel', + { + defaultMessage: 'Copy to clipboard', + } +); + +export interface CopyToClipboardProps { + /** + * Value to copy to clipboard. + */ + value: string; + /** + * Used for unit and e2e tests. + */ + ['data-test-subj']?: string; +} + +/** + * Takes a string and copies it to the clipboard. + * + * This component renders an {@link EuiButtonEmpty}. + * + * @returns An EuiCopy element + */ +export const CopyToClipboardButtonEmpty: VFC = ({ + value, + 'data-test-subj': dataTestSub, +}) => ( + + {(copy) => ( + + {COPY_TITLE} + + )} + +); + +/** + * Takes a string and copies it to the clipboard. + * + * This component is to be used in an EuiContextMenu. + * + * @returns filter in {@link EuiContextMenuItem} for a context menu + */ +export const CopyToClipboardContextMenu: VFC = ({ + value, + 'data-test-subj': dataTestSub, +}) => ( + + {(copy) => ( + + {COPY_TITLE} + + )} + +); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/index.ts new file mode 100644 index 0000000000000..c465f27bc6638 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/copy_to_clipboard/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './copy_to_clipboard'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx index 0ee9ae050aa98..473bc5d0088f7 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx @@ -5,17 +5,31 @@ * 2.0. */ -import React, { VFC } from 'react'; -import { EuiFlexGroup } from '@elastic/eui'; +import React, { useState, VFC } from 'react'; +import { + EuiButtonIcon, + EuiContextMenuPanel, + EuiFlexGroup, + EuiPopover, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Indicator } from '../../../../../../common/types/indicator'; import { FilterInButtonIcon } from '../../../../query_bar/components/filter_in'; import { FilterOutButtonIcon } from '../../../../query_bar/components/filter_out'; -import { AddToTimelineButtonIcon } from '../../../../timeline/components/add_to_timeline'; +import { AddToTimelineContextMenu } from '../../../../timeline/components/add_to_timeline'; import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../../utils/field_value'; +import { CopyToClipboardContextMenu } from '../../copy_to_clipboard'; export const TIMELINE_BUTTON_TEST_ID = 'TimelineButton'; export const FILTER_IN_BUTTON_TEST_ID = 'FilterInButton'; export const FILTER_OUT_BUTTON_TEST_ID = 'FilterOutButton'; +export const COPY_TO_CLIPBOARD_BUTTON_TEST_ID = 'CopyToClipboardButton'; +export const POPOVER_BUTTON_TEST_ID = 'PopoverButton'; + +const MORE_ACTIONS_BUTTON_LABEL = i18n.translate('xpack.threatIntelligence.more-actions.popover', { + defaultMessage: 'More actions', +}); interface IndicatorValueActions { /** @@ -37,6 +51,8 @@ export const IndicatorValueActions: VFC = ({ field, 'data-test-subj': dataTestSubj, }) => { + const [isPopoverOpen, setPopover] = useState(false); + const { key, value } = getIndicatorFieldAndValue(indicator, field); if (!fieldAndValueValid(key, value)) { return null; @@ -45,12 +61,39 @@ export const IndicatorValueActions: VFC = ({ const filterInTestId = `${dataTestSubj}${FILTER_IN_BUTTON_TEST_ID}`; const filterOutTestId = `${dataTestSubj}${FILTER_OUT_BUTTON_TEST_ID}`; const timelineTestId = `${dataTestSubj}${TIMELINE_BUTTON_TEST_ID}`; + const copyToClipboardTestId = `${dataTestSubj}${COPY_TO_CLIPBOARD_BUTTON_TEST_ID}`; + const popoverTestId = `${dataTestSubj}${POPOVER_BUTTON_TEST_ID}`; + + const popoverItems = [ + , + , + ]; return ( - + + setPopover((prevIsPopoverOpen) => !prevIsPopoverOpen)} + style={{ height: '100%' }} + /> + + } + isOpen={isPopoverOpen} + closePopover={() => setPopover(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/cell_popover_renderer.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/cell_popover_renderer.tsx new file mode 100644 index 0000000000000..f86c3b5b35df8 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/cell_popover_renderer.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiDataGridCellPopoverElementProps, + EuiFlexGroup, + EuiFlexItem, + EuiPopoverTitle, +} from '@elastic/eui'; +import React from 'react'; +import { CopyToClipboardButtonEmpty } from '../../copy_to_clipboard/copy_to_clipboard'; +import { FilterInButtonEmpty } from '../../../../query_bar/components/filter_in'; +import { FilterOutButtonEmpty } from '../../../../query_bar/components/filter_out'; +import { AddToTimelineButtonEmpty } from '../../../../timeline/components/add_to_timeline'; +import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../../utils/field_value'; +import { Indicator } from '../../../../../../common/types/indicator'; +import { Pagination } from '../../../services/fetch_indicators'; +import { useStyles } from './styles'; + +export const CELL_POPOVER_TIMELINE_BUTTON_TEST_ID = 'tiIndicatorsTableCellPopoverTimelineButton'; +export const CELL_POPOVER_FILTER_IN_BUTTON_TEST_ID = 'tiIndicatorsTableCellPopoverFilterInButton'; +export const CELL_POPOVER_FILTER_OUT_BUTTON_TEST_ID = 'tiIndicatorsTableCellPopoverFilterOutButton'; + +/** + * Used for the Indicators table cellActions column property. + * + * @param indicators array of {@link Indicator} + * @param pagination information about table current page + */ +export const cellPopoverRendererFactory = + (indicators: Indicator[], pagination: Pagination) => + (props: EuiDataGridCellPopoverElementProps) => { + const styles = useStyles(); + + const { rowIndex, columnId } = props; + + const indicator = indicators[rowIndex % pagination.pageSize]; + const { key, value } = getIndicatorFieldAndValue(indicator, columnId); + if (!fieldAndValueValid(key, value)) { + return <>; + } + + return ( + <> + + {value} + + + + + + + + + + + + + + + + + + + + + ); + }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.test.tsx index f8a0e9b9f99c6..26b25ad88bd6e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.test.tsx @@ -36,7 +36,9 @@ describe('', () => { columnIds: [], onResetColumns: stub, onToggleColumn: stub, - options: {}, + options: { + preselectedCategoryIds: ['threat'], + }, }) ); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.tsx index fae02b89cefc3..8a5a95f04b441 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/field_browser/field_browser.tsx @@ -29,6 +29,8 @@ export const IndicatorsFieldBrowser: VFC = ({ columnIds, onResetColumns, onToggleColumn, - options: {}, + options: { + preselectedCategoryIds: ['threat'], + }, }); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/index.ts index d4cb33c72afb6..6aeb80a25b059 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/index.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/index.ts @@ -8,5 +8,6 @@ export * from './actions_row_cell'; export * from './cell_actions'; export * from './cell_renderer'; +export * from './cell_popover_renderer'; export * from './field_browser'; export * from './open_flyout_button'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/styles.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/styles.ts new file mode 100644 index 0000000000000..f44872e4d0428 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/components/styles.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CSSObject } from '@emotion/react'; + +export const useStyles = () => { + const popoverMaxWidth: CSSObject = { + 'max-width': '240px', + 'word-break': 'break-word', + }; + + return { + popoverMaxWidth, + }; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/table.tsx index aa1afd0d5b11c..78fe1aaab2ea0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/table.tsx @@ -19,7 +19,7 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { EuiDataGridColumn } from '@elastic/eui/src/components/datagrid/data_grid_types'; -import { CellActions, cellRendererFactory } from './components'; +import { CellActions, cellPopoverRendererFactory, cellRendererFactory } from './components'; import { BrowserFields, SecuritySolutionDataViewBase } from '../../../../types'; import { Indicator, RawIndicatorFieldId } from '../../../../../common/types/indicator'; import { EmptyState } from '../../../../components/empty_state'; @@ -77,6 +77,11 @@ export const IndicatorsTable: VFC = ({ [pagination.pageIndex, pagination.pageSize] ); + const renderCellPopoverValue = useMemo( + () => cellPopoverRendererFactory(indicators, pagination), + [indicators, pagination] + ); + const indicatorTableContextValue = useMemo( () => ({ expanded, setExpanded, indicators }), [expanded, indicators] @@ -177,6 +182,7 @@ export const IndicatorsTable: VFC = ({ leadingControlColumns={leadingControlColumns} rowCount={indicatorCount} renderCellValue={renderCellValue} + renderCellPopover={renderCellPopoverValue} toolbarVisibility={toolbarOptions} pagination={{ ...pagination, @@ -197,6 +203,7 @@ export const IndicatorsTable: VFC = ({ isFetching, leadingControlColumns, renderCellValue, + renderCellPopoverValue, toolbarOptions, pagination, onChangeItemsPerPage, diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap index b9a31b09a40e8..7698e2fd6e31a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap @@ -223,6 +223,119 @@ Object { } `; +exports[` should render one EuiButtonEmpty 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + exports[` should render one EuiButtonIcon 1`] = ` Object { "asFragment": [Function], @@ -340,9 +453,7 @@ Object { - - Filter In - + Filter In @@ -364,9 +475,7 @@ Object { - - Filter In - + Filter In diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx index 0abfa06522301..29349b5442790 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx @@ -11,7 +11,12 @@ import { EuiButtonIcon } from '@elastic/eui'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; -import { FilterInButtonIcon, FilterInContextMenu, FilterInCellAction } from '.'; +import { + FilterInButtonEmpty, + FilterInButtonIcon, + FilterInCellAction, + FilterInContextMenu, +} from '.'; jest.mock('../../../indicators/hooks/use_indicators_filters_context'); @@ -49,6 +54,15 @@ describe(' ' expect(component).toMatchSnapshot(); }); + it('should render one EuiButtonEmpty', () => { + const component = render( + + ); + + expect(component.getByTestId(mockTestId)).toBeInTheDocument(); + expect(component).toMatchSnapshot(); + }); + it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { const component = render(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx index af6b608c1fb9e..0147e79285b0c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx @@ -8,17 +8,13 @@ import React, { VFC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiButtonIcon, EuiContextMenuItem, EuiToolTip } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; import { useFilterInOut } from '../../hooks/use_filter_in_out'; import { FilterIn } from '../../utils/filter'; import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; const ICON_TYPE = 'plusInCircle'; -const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterInButtonIcon', { - defaultMessage: 'Filter In', -}); -const CELL_ACTION_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterInCellAction', { +const TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterIn', { defaultMessage: 'Filter In', }); @@ -62,9 +58,9 @@ export const FilterInButtonIcon: VFC = ({ } return ( - + = ({ ); }; +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component renders an {@link EuiButtonEmpty}. + * + * @returns filter in button empty + */ +export const FilterInButtonEmpty: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterIn }); + if (!filterFn) { + return <>; + } + + return ( + + + {TITLE} + + + ); +}; + /** * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. * * This component is to be used in an EuiContextMenu. * - * @returns filter in item for a context menu + * @returns filter in {@link EuiContextMenuItem} for a context menu */ export const FilterInContextMenu: VFC = ({ data, @@ -101,10 +130,7 @@ export const FilterInContextMenu: VFC = ({ onClick={filterFn} data-test-subj={dataTestSub} > - + {TITLE} ); }; @@ -130,10 +156,11 @@ export const FilterInCellAction: VFC = ({ } return ( - +
- {/* @ts-ignore*/} - + + {TITLE} +
); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap index 686e543636d00..38ba10425238c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should render an empty component (wrong data input) 1`] = ` +exports[` should render an empty component (wrong data input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -61,7 +61,7 @@ Object { } `; -exports[` should render an empty component (wrong field input) 1`] = ` +exports[` should render an empty component (wrong field input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -122,7 +122,7 @@ Object { } `; -exports[` should render one Component (for EuiDataGrid use) 1`] = ` +exports[` should render one Component (for EuiDataGrid use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -223,7 +223,120 @@ Object { } `; -exports[` should render one EuiButtonIcon 1`] = ` +exports[` should render one EuiButtonEmpty 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should render one EuiButtonIcon 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -320,7 +433,7 @@ Object { } `; -exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` +exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -340,9 +453,7 @@ Object { - - Filter Out - + Filter Out @@ -364,9 +475,7 @@ Object { - - Filter Out - + Filter Out diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx index fa25a095191d4..68c08cbebd0fc 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx @@ -11,7 +11,12 @@ import { EuiButtonIcon } from '@elastic/eui'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; -import { FilterOutButtonIcon, FilterOutContextMenu, FilterOutCellAction } from '.'; +import { + FilterOutButtonEmpty, + FilterOutButtonIcon, + FilterOutCellAction, + FilterOutContextMenu, +} from '.'; jest.mock('../../../indicators/hooks/use_indicators_filters_context'); @@ -21,7 +26,7 @@ const mockField: string = 'threat.feed.name'; const mockTestId: string = 'abc'; -describe(' ', () => { +describe(' ', () => { beforeEach(() => { ( useIndicatorsFiltersContext as jest.MockedFunction @@ -49,6 +54,15 @@ describe(' expect(component).toMatchSnapshot(); }); + it('should render one EuiButtonEmpty', () => { + const component = render( + + ); + + expect(component.getByTestId(mockTestId)).toBeInTheDocument(); + expect(component).toMatchSnapshot(); + }); + it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { const component = render(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx index 3f77c14285f97..518850d9dc242 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx @@ -8,17 +8,13 @@ import React, { VFC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiButtonIcon, EuiContextMenuItem, EuiToolTip } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; import { useFilterInOut } from '../../hooks/use_filter_in_out'; import { FilterOut } from '../../utils/filter'; import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; const ICON_TYPE = 'minusInCircle'; -const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOutButtonIcon', { - defaultMessage: 'Filter Out', -}); -const CELL_ACTION_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOutCellAction', { +const TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOut', { defaultMessage: 'Filter Out', }); @@ -62,9 +58,9 @@ export const FilterOutButtonIcon: VFC = ({ } return ( - + = ({ ); }; +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component renders an {@link EuiButtonEmpty}. + * + * @returns filter out button empty + */ +export const FilterOutButtonEmpty: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterOut }); + if (!filterFn) { + return <>; + } + + return ( + + + {TITLE} + + + ); +}; + /** * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. * * This component is to be used in an EuiContextMenu. * - * @returns filter in item for a context menu + * @returns filter in {@link EuiContextMenuItem} for a context menu */ export const FilterOutContextMenu: VFC = ({ data, @@ -101,10 +130,7 @@ export const FilterOutContextMenu: VFC = ({ onClick={filterFn} data-test-subj={dataTestSub} > - + {TITLE} ); }; @@ -130,10 +156,11 @@ export const FilterOutCellAction: VFC = ({ } return ( - +
- {/* @ts-ignore*/} - + + {TITLE} +
); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx index 44f9df4225ba2..211d37dc6b4e4 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx @@ -8,10 +8,14 @@ import React, { useRef, VFC } from 'react'; import { DataProvider } from '@kbn/timelines-plugin/common'; import { AddToTimelineButtonProps } from '@kbn/timelines-plugin/public'; -import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui/src/components/button'; -import { EuiContextMenuItem, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiContextMenuItem, + EuiFlexItem, + EuiToolTip, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { generateDataProvider } from '../../utils/data_provider'; import { fieldAndValueValid, @@ -22,16 +26,10 @@ import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; -const BUTTON_ICON_TOOLTIP = i18n.translate( - 'xpack.threatIntelligence.timeline.addToTimelineButtonIcon', - { defaultMessage: 'Add to Timeline' } -); -const CELL_ACTION_TOOLTIP = i18n.translate( - 'xpack.threatIntelligence.timeline.addToTimelineCellAction', - { - defaultMessage: 'Add to Timeline', - } -); +const ICON_TYPE = 'timeline'; +const TITLE = i18n.translate('xpack.threatIntelligence.timeline.addToTimeline', { + defaultMessage: 'Add to Timeline', +}); export interface AddToTimelineProps { /** @@ -77,7 +75,7 @@ export const AddToTimelineButtonIcon: VFC = ({ } return ( - + {addToTimelineButton(addToTimelineProps)} @@ -85,13 +83,72 @@ export const AddToTimelineButtonIcon: VFC = ({ ); }; +/** + * Add to timeline feature, leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) + * Clicking on the button will add a key-value pair to an Untitled timeline. + * + * This component is renders an {@link EuiButtonEmpty}. + * + * @returns add to timeline button or an empty component + */ +export const AddToTimelineButtonEmpty: VFC = ({ + data, + field, + 'data-test-subj': dataTestSubj, +}) => { + const styles = useStyles(); + + const buttonRef = useRef(null); + + const addToTimelineButton = + useKibana().services.timelines.getHoverActions().getAddToTimelineButton; + + const { key, value } = + typeof data === 'string' ? { key: field, value: data } : getIndicatorFieldAndValue(data, field); + + if (!fieldAndValueValid(key, value)) { + return <>; + } + + const dataProvider: DataProvider[] = [generateDataProvider(key, value as string)]; + + const addToTimelineProps: AddToTimelineButtonProps = { + dataProvider, + field: key, + ownFocus: false, + }; + + // Use case is for the barchart legend (for example). + // We can't use the addToTimelineButton directly because the UI doesn't work in a EuiContextMenu. + // We hide it and use the defaultFocusedButtonRef props to programmatically click it. + addToTimelineProps.defaultFocusedButtonRef = buttonRef; + + return ( + <> +
{addToTimelineButton(addToTimelineProps)}
+ + buttonRef.current?.click()} + data-test-subj={dataTestSubj} + > + {TITLE} + + + + ); +}; + /** * Add to timeline feature, leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) * Clicking on the button will add a key-value pair to an Untitled timeline. * * This component is to be used in an EuiContextMenu. * - * @returns add to timeline item for a context menu + * @returns add to timeline {@link EuiContextMenuItem} for a context menu */ export const AddToTimelineContextMenu: VFC = ({ data, @@ -130,15 +187,12 @@ export const AddToTimelineContextMenu: VFC = ({
{addToTimelineButton(addToTimelineProps)}
contextMenuRef.current?.click()} data-test-subj={dataTestSubj} > - + {TITLE} ); @@ -168,7 +222,7 @@ export const AddToTimelineCellAction: VFC = ({ addToTimelineProps.Component = Component; return ( - + {addToTimelineButton(addToTimelineProps)} diff --git a/x-pack/plugins/triggers_actions_ui/common/data/index.ts b/x-pack/plugins/triggers_actions_ui/common/data/index.ts index e368c77fe5479..e0c0f240c3b2f 100644 --- a/x-pack/plugins/triggers_actions_ui/common/data/index.ts +++ b/x-pack/plugins/triggers_actions_ui/common/data/index.ts @@ -7,6 +7,7 @@ export interface TimeSeriesResult { results: TimeSeriesResultRow[]; + truncated: boolean; } export interface TimeSeriesResultRow { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx index 01ad50456b04b..82102402a982f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { renderHook } from '@testing-library/react-hooks'; +import { act, renderHook } from '@testing-library/react-hooks'; import { useKibana } from '../../common/lib/kibana'; import { useSubAction } from './use_sub_action'; @@ -19,10 +19,14 @@ describe('useSubAction', () => { subAction: 'test', subActionParams: {}, }; + useKibanaMock().services.http.post = jest + .fn() + .mockImplementation(() => Promise.resolve({ status: 'ok', data: {} })); + let abortSpy = jest.spyOn(window, 'AbortController'); beforeEach(() => { jest.clearAllMocks(); - useKibanaMock().services.http.post = jest.fn().mockResolvedValue({ status: 'ok', data: {} }); + abortSpy.mockRestore(); }); it('init', async () => { @@ -30,7 +34,6 @@ describe('useSubAction', () => { await waitForNextUpdate(); expect(result.current).toEqual({ - isError: false, isLoading: false, response: {}, error: null, @@ -43,30 +46,136 @@ describe('useSubAction', () => { expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( '/api/actions/connector/test-id/_execute', - { body: '{"params":{"subAction":"test","subActionParams":{}}}' } + { + body: '{"params":{"subAction":"test","subActionParams":{}}}', + signal: new AbortController().signal, + } ); }); + it('executes sub action if subAction parameter changes', async () => { + const { rerender, waitForNextUpdate } = renderHook(useSubAction, { initialProps: params }); + await waitForNextUpdate(); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(1); + + await act(async () => { + rerender({ ...params, subAction: 'test-2' }); + await waitForNextUpdate(); + }); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(2); + }); + + it('executes sub action if connectorId parameter changes', async () => { + const { rerender, waitForNextUpdate } = renderHook(useSubAction, { initialProps: params }); + await waitForNextUpdate(); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(1); + + await act(async () => { + rerender({ ...params, connectorId: 'test-id-2' }); + await waitForNextUpdate(); + }); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(2); + }); + + it('returns memoized response if subActionParams changes but values are equal', async () => { + const { result, rerender, waitForNextUpdate } = renderHook(useSubAction, { + initialProps: { ...params, subActionParams: { foo: 'bar' } }, + }); + await waitForNextUpdate(); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(1); + const previous = result.current; + + await act(async () => { + rerender({ ...params, subActionParams: { foo: 'bar' } }); + await waitForNextUpdate(); + }); + + expect(result.current.response).toBe(previous.response); + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(1); + }); + + it('executes sub action if subActionParams changes and values are not equal', async () => { + const { result, rerender, waitForNextUpdate } = renderHook(useSubAction, { + initialProps: { ...params, subActionParams: { foo: 'bar' } }, + }); + await waitForNextUpdate(); + + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(1); + const previous = result.current; + + await act(async () => { + rerender({ ...params, subActionParams: { foo: 'baz' } }); + await waitForNextUpdate(); + }); + + expect(result.current.response).not.toBe(previous.response); + expect(useKibanaMock().services.http.post).toHaveBeenCalledTimes(2); + }); + it('returns an error correctly', async () => { - useKibanaMock().services.http.post = jest.fn().mockRejectedValue(new Error('error executing')); + const error = new Error('error executing'); + useKibanaMock().services.http.post = jest.fn().mockRejectedValueOnce(error); const { result, waitForNextUpdate } = renderHook(() => useSubAction(params)); await waitForNextUpdate(); expect(result.current).toEqual({ - isError: true, isLoading: false, response: undefined, - error: expect.anything(), + error, }); }); - it('does not execute if params are null', async () => { - const { result } = renderHook(() => useSubAction(null)); + it('should not set error if aborted', async () => { + const firstAbortCtrl = new AbortController(); + firstAbortCtrl.abort(); + abortSpy = jest.spyOn(window, 'AbortController').mockReturnValueOnce(firstAbortCtrl); + + const error = new Error('error executing'); + useKibanaMock().services.http.post = jest.fn().mockRejectedValueOnce(error); + + const { result } = renderHook(() => useSubAction(params)); + + expect(result.current.error).toBe(null); + }); + + it('should abort on unmount', async () => { + const firstAbortCtrl = new AbortController(); + abortSpy = jest.spyOn(window, 'AbortController').mockReturnValueOnce(firstAbortCtrl); + + const { unmount } = renderHook(useSubAction, { initialProps: params }); + + unmount(); + + expect(firstAbortCtrl.signal.aborted).toEqual(true); + }); + + it('should abort on parameter change', async () => { + const firstAbortCtrl = new AbortController(); + abortSpy = jest.spyOn(window, 'AbortController').mockImplementation(() => { + abortSpy.mockRestore(); + return firstAbortCtrl; + }); + + const { rerender } = renderHook(useSubAction, { initialProps: params }); + + await act(async () => { + rerender({ ...params, connectorId: 'test-id-2' }); + }); + + expect(firstAbortCtrl.signal.aborted).toEqual(true); + }); + + it('does not execute if disabled', async () => { + const { result } = renderHook(() => useSubAction({ ...params, disabled: true })); expect(useKibanaMock().services.http.post).not.toHaveBeenCalled(); expect(result.current).toEqual({ - isError: false, isLoading: false, response: undefined, error: null, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx index a219caa9b4d23..fd727e7325814 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx @@ -5,140 +5,135 @@ * 2.0. */ -import { useCallback, useEffect, useReducer, useRef } from 'react'; -import { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; +import deepEqual from 'fast-deep-equal'; +import { Reducer, useEffect, useReducer, useRef } from 'react'; import { useKibana } from '../../common/lib/kibana'; import { executeAction } from '../lib/action_connector_api'; -interface UseSubActionParams { - connectorId: string; - subAction: string; - subActionParams: Record; +interface UseSubActionParams

{ + connectorId?: string; + subAction?: string; + subActionParams?: P; + disabled?: boolean; } -interface SubActionsState { +interface SubActionsState { isLoading: boolean; - isError: boolean; - response: unknown | undefined; + response: R | undefined; error: Error | null; } -enum SubActionsActionsList { - INIT, - LOADING, +const enum SubActionsActionsList { + START, SUCCESS, ERROR, } -type Action = - | { type: SubActionsActionsList.INIT } - | { type: SubActionsActionsList.LOADING } - | { type: SubActionsActionsList.SUCCESS; payload: T | undefined } +type Action = + | { type: SubActionsActionsList.START } + | { type: SubActionsActionsList.SUCCESS; payload: R | undefined } | { type: SubActionsActionsList.ERROR; payload: Error | null }; -const dataFetchReducer = (state: SubActionsState, action: Action): SubActionsState => { +const dataFetchReducer = (state: SubActionsState, action: Action): SubActionsState => { switch (action.type) { - case SubActionsActionsList.INIT: - return { - ...state, - isLoading: false, - isError: false, - }; - - case SubActionsActionsList.LOADING: + case SubActionsActionsList.START: return { ...state, isLoading: true, - isError: false, + error: null, }; - case SubActionsActionsList.SUCCESS: return { ...state, response: action.payload, isLoading: false, - isError: false, + error: null, }; - case SubActionsActionsList.ERROR: return { ...state, error: action.payload, isLoading: false, - isError: true, }; - default: return state; } }; -export const useSubAction = (params: UseSubActionParams | null) => { +const useMemoParams = (subActionsParams: P): P => { + const paramsRef = useRef

(subActionsParams); + if (!deepEqual(paramsRef.current, subActionsParams)) { + paramsRef.current = subActionsParams; + } + return paramsRef.current; +}; + +export const useSubAction = ({ + connectorId, + subAction, + subActionParams, + disabled = false, +}: UseSubActionParams

) => { const { http } = useKibana().services; - const [state, dispatch] = useReducer(dataFetchReducer, { - isError: false, + const [{ isLoading, response, error }, dispatch] = useReducer< + Reducer, Action> + >(dataFetchReducer, { isLoading: false, response: undefined, error: null, }); + const memoParams = useMemoParams(subActionParams); - const abortCtrl = useRef(new AbortController()); - const isMounted = useRef(false); - - const executeSubAction = useCallback(async () => { - if (params == null) { + useEffect(() => { + if (disabled || !connectorId || !subAction) { return; } - const { connectorId, subAction, subActionParams } = params; - dispatch({ type: SubActionsActionsList.INIT }); - - try { - abortCtrl.current.abort(); - abortCtrl.current = new AbortController(); - dispatch({ type: SubActionsActionsList.LOADING }); - - const res = (await executeAction({ - id: connectorId, - http, - params: { - subAction, - subActionParams, - }, - })) as ActionTypeExecutorResult; - - if (isMounted.current) { - if (res.status && res.status === 'error') { + const abortCtrl = new AbortController(); + let isMounted = true; + + const executeSubAction = async () => { + try { + dispatch({ type: SubActionsActionsList.START }); + + const res = await executeAction({ + id: connectorId, + params: { + subAction, + subActionParams: memoParams, + }, + http, + signal: abortCtrl.signal, + }); + + if (isMounted) { + if (res.status && res.status === 'ok') { + dispatch({ type: SubActionsActionsList.SUCCESS, payload: res.data }); + } else { + dispatch({ + type: SubActionsActionsList.ERROR, + payload: new Error(`${res.message}: ${res.serviceMessage}`), + }); + } + } + return res.data; + } catch (e) { + if (isMounted && !abortCtrl.signal.aborted) { dispatch({ type: SubActionsActionsList.ERROR, - payload: new Error(`${res.message}: ${res.serviceMessage}`), + payload: e, }); } - - dispatch({ type: SubActionsActionsList.SUCCESS, payload: res.data }); - } - - return res.data; - } catch (e) { - if (isMounted.current) { - dispatch({ - type: SubActionsActionsList.ERROR, - payload: e, - }); } - } - }, [http, params]); + }; - useEffect(() => { - isMounted.current = true; executeSubAction(); + return () => { - isMounted.current = false; - abortCtrl.current.abort(); + isMounted = false; + abortCtrl.abort(); }; - }, [executeSubAction]); + }, [memoParams, disabled, connectorId, subAction, http]); - return { - ...state, - }; + return { isLoading, response, error }; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts index 60fc763628bfb..2b6aa43a6da79 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts @@ -19,13 +19,14 @@ describe('executeAction', () => { stringParams: 'someString', numericParams: 123, }; + const signal = new AbortController().signal; http.post.mockResolvedValueOnce({ connector_id: id, status: 'ok', }); - const result = await executeAction({ id, http, params }); + const result = await executeAction({ id, http, params, signal }); expect(result).toEqual({ actionId: id, status: 'ok', @@ -35,6 +36,7 @@ describe('executeAction', () => { "/api/actions/connector/12%2F3/_execute", Object { "body": "{\\"params\\":{\\"stringParams\\":\\"someString\\",\\"numericParams\\":123}}", + "signal": AbortSignal {}, }, ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts index 74cc4d9484c9b..88871febee3e2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts @@ -6,33 +6,36 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { ActionTypeExecutorResult, RewriteRequestCase } from '@kbn/actions-plugin/common'; +import { ActionTypeExecutorResult, AsApiContract } from '@kbn/actions-plugin/common'; import { BASE_ACTION_API_PATH } from '../../constants'; -const rewriteBodyRes: RewriteRequestCase> = ({ +const rewriteBodyRes = ({ connector_id: actionId, service_message: serviceMessage, ...res -}) => ({ +}: AsApiContract>): ActionTypeExecutorResult => ({ ...res, actionId, serviceMessage, }); -export async function executeAction({ +export async function executeAction({ id, params, http, + signal, }: { id: string; http: HttpSetup; params: Record; -}): Promise> { - const res = await http.post[0]>( + signal?: AbortSignal; +}): Promise> { + const res = await http.post>>( `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}/_execute`, { body: JSON.stringify({ params }), + signal, } ); - return rewriteBodyRes(res); + return rewriteBodyRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx index ca5552c934be9..5c3a33d5b17c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx @@ -121,4 +121,25 @@ describe('FieldsBrowser', () => { const result = renderComponent({ isEventViewer, width: FIELD_BROWSER_WIDTH }); expect(result.getByTestId('show-field-browser')).toBeInTheDocument(); }); + + describe('options.preselectedCategoryIds', () => { + it("should render fields list narrowed to preselected category id's", async () => { + const agentFieldsCount = Object.keys(mockBrowserFields.agent?.fields || {}).length; + + // Narrowing the selection to 'agent' only + const result = renderComponent({ options: { preselectedCategoryIds: ['agent'] } }); + + result.getByTestId('show-field-browser').click(); + + // Wait for the modal to open + await waitFor(() => { + expect(result.getByTestId('fields-browser-container')).toBeInTheDocument(); + }); + + // Check if there are only 4 fields in the table + expect(result.queryByTestId('field-table')?.querySelectorAll('tbody tr')).toHaveLength( + agentFieldsCount + ); + }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx index 219a06713e7c3..32fe7a6f74df8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx @@ -31,6 +31,11 @@ export const FieldBrowserComponent: React.FC = ({ options, width, }) => { + const initialCategories = useMemo( + () => options?.preselectedCategoryIds ?? [], + [options?.preselectedCategoryIds] + ); + const customizeColumnsButtonRef = useRef(null); /** all field names shown in the field browser must contain this string (when specified) */ const [filterInput, setFilterInput] = useState(''); @@ -43,7 +48,7 @@ export const FieldBrowserComponent: React.FC = ({ /** when true, show a spinner in the input to indicate the field browser is searching for matching field names */ const [isSearching, setIsSearching] = useState(false); /** this category will be displayed in the right-hand pane of the field browser */ - const [selectedCategoryIds, setSelectedCategoryIds] = useState([]); + const [selectedCategoryIds, setSelectedCategoryIds] = useState(initialCategories); /** show the field browser */ const [show, setShow] = useState(false); @@ -93,9 +98,9 @@ export const FieldBrowserComponent: React.FC = ({ setFilteredBrowserFields(null); setFilterSelectedEnabled(false); setIsSearching(false); - setSelectedCategoryIds([]); + setSelectedCategoryIds(initialCategories); setShow(false); - }, []); + }, [initialCategories]); /** Invoked when the user types in the filter input */ const updateFilter = useCallback( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts index caed72a14b0c2..898fd67140837 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts @@ -34,6 +34,10 @@ export type GetFieldTableColumns = (params: { export interface FieldBrowserOptions { createFieldButton?: CreateFieldComponent; getFieldTableColumns?: GetFieldTableColumns; + /** + * Categories that should be selected initially + */ + preselectedCategoryIds?: string[]; } export interface FieldBrowserProps { diff --git a/x-pack/plugins/triggers_actions_ui/server/data/index.ts b/x-pack/plugins/triggers_actions_ui/server/data/index.ts index c65f44162ecd7..aae400a7469ea 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/index.ts @@ -11,6 +11,7 @@ import { registerRoutes } from './routes'; export type { TimeSeriesQuery, CoreQueryParams } from './lib'; export { + TIME_SERIES_BUCKET_SELECTOR_FIELD, CoreQueryParamsSchemaProperties, validateCoreQueryBody, validateTimeWindowUnits, diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts index c76eb1cafa867..b99f278837f26 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts @@ -6,6 +6,7 @@ */ export type { TimeSeriesQuery } from './time_series_query'; +export { TIME_SERIES_BUCKET_SELECTOR_FIELD } from './time_series_query'; export type { CoreQueryParams } from './core_query_types'; export { CoreQueryParamsSchemaProperties, diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts index dc037dde8f499..5e2550e15f35d 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts @@ -5,9 +5,6 @@ * 2.0. */ -// test error conditions of calling timeSeriesQuery - postive results tested in FT - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { Logger } from '@kbn/core/server'; import { TimeSeriesQuery, timeSeriesQuery, getResultFromEs } from './time_series_query'; @@ -20,9 +17,9 @@ const DefaultQueryParams: TimeSeriesQuery = { aggField: undefined, timeWindowSize: 5, timeWindowUnit: 'm', - dateStart: undefined, - dateEnd: undefined, - interval: undefined, + dateStart: '2021-04-22T15:19:31Z', + dateEnd: '2021-04-22T15:20:31Z', + interval: '1m', groupBy: 'all', termField: undefined, termSize: undefined, @@ -37,6 +34,10 @@ describe('timeSeriesQuery', () => { query: DefaultQueryParams, }; + beforeEach(() => { + jest.clearAllMocks(); + }); + it('fails as expected when the callCluster call fails', async () => { esClient.search.mockRejectedValue(new Error('woopsie')); await timeSeriesQuery(params); @@ -48,141 +49,1560 @@ describe('timeSeriesQuery', () => { }); it('fails as expected when the query params are invalid', async () => { - params.query = { ...params.query, dateStart: 'x' }; - expect(timeSeriesQuery(params)).rejects.toThrowErrorMatchingInlineSnapshot( - `"invalid date format for dateStart: \\"x\\""` + expect( + timeSeriesQuery({ ...params, query: { ...params.query, dateStart: 'x' } }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"invalid date format for dateStart: \\"x\\""`); + }); + + it('should create correct query when aggType=count and termField is undefined (count over all) and selector params are undefined', async () => { + await timeSeriesQuery(params); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType=count and termField is undefined (count over all) and selector params are defined', async () => { + await timeSeriesQuery({ + ...params, + condition: { + resultLimit: 1000, + conditionScript: `params.compareValue > 1`, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType=count and termField is specified (count over top N termField) and selector params are undefined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + termField: 'the-term', + termSize: 10, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + groupAgg: { + terms: { + field: 'the-term', + size: 10, + }, + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + }, + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType=count and termField is specified (count over top N termField) and selector params are defined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + termField: 'the-term', + termSize: 10, + }, + condition: { + resultLimit: 1000, + conditionScript: `params.compareValue > 1`, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + groupAgg: { + terms: { + field: 'the-term', + size: 10, + }, + aggs: { + conditionSelector: { + bucket_selector: { + buckets_path: { + compareValue: '_count', + }, + script: `params.compareValue > 1`, + }, + }, + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + }, + }, + }, + groupAggCount: { + stats_bucket: { + buckets_path: 'groupAgg._count', + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType!=count and termField is undefined (aggregate metric over all) and selector params are undefined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + aggType: 'avg', + aggField: 'avg-field', + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + aggs: { + metricAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + sortValueAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType!=count and termField is undefined (aggregate metric over all) and selector params are defined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + aggType: 'avg', + aggField: 'avg-field', + }, + condition: { + resultLimit: 1000, + conditionScript: `params.compareValue > 1`, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + aggs: { + metricAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + sortValueAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType!=count and termField is specified (aggregate metric over top N termField) and selector params are undefined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + aggType: 'avg', + aggField: 'avg-field', + termField: 'the-field', + termSize: 20, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + groupAgg: { + terms: { + field: 'the-field', + order: { + sortValueAgg: 'desc', + }, + size: 20, + }, + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + aggs: { + metricAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + sortValueAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should create correct query when aggType!=count and termField is specified (aggregate metric over top N termField) and selector params are defined', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + aggType: 'avg', + aggField: 'avg-field', + termField: 'the-field', + termSize: 20, + }, + condition: { + resultLimit: 1000, + conditionScript: `params.compareValue > 1`, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + groupAgg: { + terms: { + field: 'the-field', + order: { + sortValueAgg: 'desc', + }, + size: 20, + }, + aggs: { + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + aggs: { + metricAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + conditionSelector: { + bucket_selector: { + buckets_path: { + compareValue: 'sortValueAgg', + }, + script: 'params.compareValue > 1', + }, + }, + sortValueAgg: { + avg: { + field: 'avg-field', + }, + }, + }, + }, + groupAggCount: { + stats_bucket: { + buckets_path: 'groupAgg._count', + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } + ); + }); + + it('should correctly apply the resultLimit if specified', async () => { + await timeSeriesQuery({ + ...params, + query: { + ...params.query, + termField: 'the-term', + termSize: 100, + }, + condition: { + resultLimit: 5, + conditionScript: `params.compareValue > 1`, + }, + }); + expect(esClient.search).toHaveBeenCalledWith( + { + allow_no_indices: true, + body: { + aggs: { + groupAgg: { + terms: { + field: 'the-term', + size: 6, + }, + aggs: { + conditionSelector: { + bucket_selector: { + buckets_path: { + compareValue: '_count', + }, + script: `params.compareValue > 1`, + }, + }, + dateAgg: { + date_range: { + field: 'time-field', + format: 'strict_date_time', + ranges: [ + { + from: '2021-04-22T15:14:31.000Z', + to: '2021-04-22T15:19:31.000Z', + }, + { + from: '2021-04-22T15:15:31.000Z', + to: '2021-04-22T15:20:31.000Z', + }, + ], + }, + }, + }, + }, + groupAggCount: { + stats_bucket: { + buckets_path: 'groupAgg._count', + }, + }, + }, + query: { + bool: { + filter: { + range: { + 'time-field': { + format: 'strict_date_time', + gte: '2021-04-22T15:14:31.000Z', + lt: '2021-04-22T15:20:31.000Z', + }, + }, + }, + }, + }, + size: 0, + }, + ignore_unavailable: true, + index: 'index-name', + }, + { ignore: [404], meta: true } ); }); }); describe('getResultFromEs', () => { - it('correctly parses time series results for count aggregation', () => { + it('correctly parses time series results for count over all aggregation', () => { + // results should be same whether isConditionInQuery is true or false expect( - getResultFromEs(true, false, { - took: 0, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { total: { value: 0, relation: 'eq' }, hits: [] }, - aggregations: { - dateAgg: { - buckets: [ - { - key: '2021-04-22T15:14:31.075Z-2021-04-22T15:19:31.075Z', - from: 1619104471075, - from_as_string: '2021-04-22T15:14:31.075Z', - to: 1619104771075, - to_as_string: '2021-04-22T15:19:31.075Z', - doc_count: 0, - }, - ], + getResultFromEs({ + isCountAgg: true, + isGroupAgg: false, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + dateAgg: { + buckets: [ + { + key: '2022-09-20T00:14:31.000Z-2022-09-20T23:19:31.000Z', + from: 1663632871000, + from_as_string: '2022-09-20T00:14:31.000Z', + to: 1663715971000, + to_as_string: '2022-09-20T23:19:31.000Z', + doc_count: 481, + }, + ], + }, }, }, - } as estypes.SearchResponse) + }) ).toEqual({ results: [ { group: 'all documents', - metrics: [['2021-04-22T15:19:31.075Z', 0]], + metrics: [['2022-09-20T23:19:31.000Z', 481]], }, ], + truncated: false, + }); + + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: false, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + dateAgg: { + buckets: [ + { + key: '2022-09-20T00:14:31.000Z-2022-09-20T23:19:31.000Z', + from: 1663632871000, + from_as_string: '2022-09-20T00:14:31.000Z', + to: 1663715971000, + to_as_string: '2022-09-20T23:19:31.000Z', + doc_count: 481, + }, + ], + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'all documents', + metrics: [['2022-09-20T23:19:31.000Z', 481]], + }, + ], + truncated: false, }); }); - it('correctly parses time series results with no aggregation data for count aggregation', () => { + it('correctly parses time series results with no aggregation data for count over all aggregation', () => { // this could happen with cross cluster searches when cluster permissions are incorrect // the query completes but doesn't return any aggregations + + // results should be same whether isConditionInQuery is true or false expect( - getResultFromEs(true, false, { - took: 0, - timed_out: false, - _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, - _clusters: { total: 1, successful: 1, skipped: 0 }, - hits: { total: { value: 0, relation: 'eq' }, hits: [] }, - } as estypes.SearchResponse) + getResultFromEs({ + isCountAgg: true, + isGroupAgg: false, + isConditionInQuery: true, + esResult: { + took: 0, + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + _clusters: { total: 1, successful: 1, skipped: 0 }, + hits: { total: { value: 0, relation: 'eq' }, hits: [] }, + }, + }) ).toEqual({ results: [], + truncated: false, + }); + + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: false, + isConditionInQuery: false, + esResult: { + took: 0, + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + _clusters: { total: 1, successful: 1, skipped: 0 }, + hits: { total: { value: 0, relation: 'eq' }, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, }); }); - it('correctly parses time series results for group aggregation', () => { + it('correctly parses time series results for count over top N termField aggregation when isConditionInQuery = false', () => { expect( - getResultFromEs(false, true, { - took: 1, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { total: { value: 298, relation: 'eq' }, hits: [] }, - aggregations: { - groupAgg: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'host-2', - doc_count: 149, - sortValueAgg: { value: 0.5000000018251423 }, - dateAgg: { - buckets: [ - { - key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', - from: 1619104723191, - from_as_string: '2021-04-22T15:18:43.191Z', - to: 1619105023191, - to_as_string: '2021-04-22T15:23:43.191Z', - doc_count: 149, - metricAgg: { value: 0.5000000018251423 }, - }, - ], + getResultFromEs({ + isCountAgg: true, + isGroupAgg: true, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + }, + ], + }, }, - }, - { - key: 'host-1', - doc_count: 149, - sortValueAgg: { value: 0.5000000011000857 }, - dateAgg: { - buckets: [ - { - key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', - from: 1619104723191, - from_as_string: '2021-04-22T15:18:43.191Z', - to: 1619105023191, - to_as_string: '2021-04-22T15:23:43.191Z', - doc_count: 149, - metricAgg: { value: 0.5000000011000857 }, - }, - ], + { + key: 'host-1', + doc_count: 53, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + }, + ], + }, }, - }, - ], + ], + }, }, }, - } as estypes.SearchResponse) + }) ).toEqual({ results: [ { group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 149]], + }, + { + group: 'host-1', + metrics: [['2021-04-22T15:23:43.191Z', 53]], + }, + ], + truncated: false, + }); + }); + + it('correctly parses time series results for count over top N termField aggregation when isConditionInQuery = true', () => { + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: true, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + }, + ], + }, + }, + { + key: 'host-1', + doc_count: 53, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + }, + ], + }, + }, + ], + }, + groupAggCount: { + count: 2, + min: 90, + max: 90, + avg: 90, + sum: 180, + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 149]], + }, + { + group: 'host-1', + metrics: [['2021-04-22T15:23:43.191Z', 53]], + }, + ], + truncated: false, + }); + }); + + it('correctly returns truncated status for time series results for count over top N termField aggregation when isConditionInQuery = true', () => { + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: true, + isConditionInQuery: true, + resultLimit: 5, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + }, + ], + }, + }, + { + key: 'host-1', + doc_count: 53, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + }, + ], + }, + }, + { + key: 'host-3', + doc_count: 40, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 40, + }, + ], + }, + }, + { + key: 'host-6', + doc_count: 55, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 55, + }, + ], + }, + }, + { + key: 'host-9', + doc_count: 54, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 54, + }, + ], + }, + }, + { + key: 'host-11', + doc_count: 2, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 2, + }, + ], + }, + }, + ], + }, + groupAggCount: { + count: 6, + min: 90, + max: 90, + avg: 90, + sum: 180, + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 149]], + }, + { + group: 'host-1', + metrics: [['2021-04-22T15:23:43.191Z', 53]], + }, + { + group: 'host-3', + metrics: [['2021-04-22T15:23:43.191Z', 40]], + }, + { + group: 'host-6', + metrics: [['2021-04-22T15:23:43.191Z', 55]], + }, + { + group: 'host-9', + metrics: [['2021-04-22T15:23:43.191Z', 54]], + }, + ], + truncated: true, + }); + }); + + it('correctly parses time series results with no aggregation data for count over top N termField aggregation', () => { + // this could happen with cross cluster searches when cluster permissions are incorrect + // the query completes but doesn't return any aggregations + + // results should be same whether isConditionInQuery is true or false + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: true, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, + }); + + expect( + getResultFromEs({ + isCountAgg: true, + isGroupAgg: true, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, + }); + }); + + it('correctly parses time series results for aggregate metric over all aggregation', () => { + // results should be same whether isConditionInQuery is true or false + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: false, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + sortValueAgg: { value: 0.5000000018251423 }, + dateAgg: { + buckets: [ + { + key: '2022-09-20T00:14:31.000Z-2022-09-20T23:19:31.000Z', + from: 1663632871000, + from_as_string: '2022-09-20T00:14:31.000Z', + to: 1663715971000, + to_as_string: '2022-09-20T23:19:31.000Z', + doc_count: 481, + metricAgg: { value: 0.5000000018251423 }, + }, + ], + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'all documents', + metrics: [['2022-09-20T23:19:31.000Z', 0.5000000018251423]], + }, + ], + truncated: false, + }); + + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: false, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + sortValueAgg: { value: 0.5000000018251423 }, + dateAgg: { + buckets: [ + { + key: '2022-09-20T00:14:31.000Z-2022-09-20T23:19:31.000Z', + from: 1663632871000, + from_as_string: '2022-09-20T00:14:31.000Z', + to: 1663715971000, + to_as_string: '2022-09-20T23:19:31.000Z', + doc_count: 481, + metricAgg: { value: 0.5000000018251423 }, + }, + ], + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'all documents', + metrics: [['2022-09-20T23:19:31.000Z', 0.5000000018251423]], + }, + ], + truncated: false, + }); + }); + + it('correctly parses time series results with no aggregation data for aggregate metric over all aggregation', () => { + // this could happen with cross cluster searches when cluster permissions are incorrect + // the query completes but doesn't return any aggregations + + // results should be same whether isConditionInQuery is true or false + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: false, + isConditionInQuery: true, + esResult: { + took: 0, + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + _clusters: { total: 1, successful: 1, skipped: 0 }, + hits: { total: { value: 0, relation: 'eq' }, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, + }); + + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: false, + isConditionInQuery: false, + esResult: { + took: 0, + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + _clusters: { total: 1, successful: 1, skipped: 0 }, + hits: { total: { value: 0, relation: 'eq' }, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, + }); + }); + + it('correctly parses time series results for aggregate metric over top N termField aggregation when isConditionInQuery = false', () => { + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: true, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + sortValueAgg: { value: 0.7100000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + metricAgg: { value: 0.7100000018251423 }, + }, + ], + }, + }, + { + key: 'host-1', + doc_count: 53, + sortValueAgg: { value: 0.5000000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + metricAgg: { value: 0.5000000018251423 }, + }, + ], + }, + }, + ], + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 0.7100000018251423]], + }, + { + group: 'host-1', + metrics: [['2021-04-22T15:23:43.191Z', 0.5000000018251423]], + }, + ], + truncated: false, + }); + }); + + it('correctly parses time series results for aggregate metric over top N termField aggregation when isConditionInQuery = true', () => { + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: true, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + sortValueAgg: { value: 0.7100000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + metricAgg: { value: 0.7100000018251423 }, + }, + ], + }, + }, + { + key: 'host-1', + doc_count: 53, + sortValueAgg: { value: 0.5000000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + metricAgg: { value: 0.5000000018251423 }, + }, + ], + }, + }, + ], + }, + groupAggCount: { + count: 2, + min: 75, + max: 90, + avg: 82.5, + sum: 165, + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 0.7100000018251423]], + }, + { + group: 'host-1', metrics: [['2021-04-22T15:23:43.191Z', 0.5000000018251423]], }, + ], + truncated: false, + }); + }); + + it('correctly returns truncated status for time series results for aggregate metrics over top N termField aggregation when isConditionInQuery = true', () => { + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: true, + isConditionInQuery: true, + resultLimit: 5, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + groupAgg: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'host-2', + doc_count: 149, + sortValueAgg: { value: 0.7100000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 149, + metricAgg: { value: 0.7100000018251423 }, + }, + ], + }, + }, + { + key: 'host-1', + doc_count: 53, + sortValueAgg: { value: 0.5000000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 53, + metricAgg: { value: 0.5000000018251423 }, + }, + ], + }, + }, + { + key: 'host-3', + doc_count: 40, + sortValueAgg: { value: 0.4900000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 40, + metricAgg: { value: 0.4900000018251423 }, + }, + ], + }, + }, + { + key: 'host-6', + doc_count: 55, + sortValueAgg: { value: 0.4600000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 55, + metricAgg: { value: 0.4600000018251423 }, + }, + ], + }, + }, + { + key: 'host-9', + doc_count: 54, + sortValueAgg: { value: 0.3300000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 54, + metricAgg: { value: 0.3300000018251423 }, + }, + ], + }, + }, + { + key: 'host-11', + doc_count: 2, + sortValueAgg: { value: 0.1200000018251423 }, + dateAgg: { + buckets: [ + { + key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z', + from: 1619104723191, + from_as_string: '2021-04-22T15:18:43.191Z', + to: 1619105023191, + to_as_string: '2021-04-22T15:23:43.191Z', + doc_count: 2, + metricAgg: { value: 0.1200000018251423 }, + }, + ], + }, + }, + ], + }, + groupAggCount: { + count: 6, + min: 75, + max: 90, + avg: 82.5, + sum: 165, + }, + }, + }, + }) + ).toEqual({ + results: [ + { + group: 'host-2', + metrics: [['2021-04-22T15:23:43.191Z', 0.7100000018251423]], + }, { group: 'host-1', - metrics: [['2021-04-22T15:23:43.191Z', 0.5000000011000857]], + metrics: [['2021-04-22T15:23:43.191Z', 0.5000000018251423]], + }, + { + group: 'host-3', + metrics: [['2021-04-22T15:23:43.191Z', 0.4900000018251423]], + }, + { + group: 'host-6', + metrics: [['2021-04-22T15:23:43.191Z', 0.4600000018251423]], + }, + { + group: 'host-9', + metrics: [['2021-04-22T15:23:43.191Z', 0.3300000018251423]], }, ], + truncated: true, }); }); - it('correctly parses time series results with no aggregation data for group aggregation', () => { + it('correctly parses time series results with no aggregation data for aggregate metric over top N termField aggregation', () => { // this could happen with cross cluster searches when cluster permissions are incorrect // the query completes but doesn't return any aggregations + + // results should be same whether isConditionInQuery is true or false + expect( + getResultFromEs({ + isCountAgg: false, + isGroupAgg: true, + isConditionInQuery: true, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + }, + }) + ).toEqual({ + results: [], + truncated: false, + }); + expect( - getResultFromEs(false, true, { - took: 0, - timed_out: false, - _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, - _clusters: { total: 1, successful: 1, skipped: 0 }, - hits: { total: { value: 0, relation: 'eq' }, hits: [] }, - } as estypes.SearchResponse) + getResultFromEs({ + isCountAgg: false, + isGroupAgg: true, + isConditionInQuery: false, + esResult: { + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 481, relation: 'eq' }, max_score: null, hits: [] }, + }, + }) ).toEqual({ results: [], + truncated: false, }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts index 6f5ebbe34a891..885be0bf59f5b 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts @@ -12,19 +12,28 @@ import { getEsErrorMessage } from '@kbn/alerting-plugin/server'; import { DEFAULT_GROUPS } from '..'; import { getDateRangeInfo } from './date_range_info'; -import { TimeSeriesQuery, TimeSeriesResult, TimeSeriesResultRow } from './time_series_types'; +import { + TimeSeriesQuery, + TimeSeriesResult, + TimeSeriesResultRow, + TimeSeriesCondition, +} from './time_series_types'; export type { TimeSeriesQuery, TimeSeriesResult } from './time_series_types'; +export const TIME_SERIES_BUCKET_SELECTOR_PATH_NAME = 'compareValue'; +export const TIME_SERIES_BUCKET_SELECTOR_FIELD = `params.${TIME_SERIES_BUCKET_SELECTOR_PATH_NAME}`; + export interface TimeSeriesQueryParameters { logger: Logger; esClient: ElasticsearchClient; query: TimeSeriesQuery; + condition?: TimeSeriesCondition; } export async function timeSeriesQuery( params: TimeSeriesQueryParameters ): Promise { - const { logger, esClient, query: queryParams } = params; + const { logger, esClient, query: queryParams, condition: conditionParams } = params; const { index, timeWindowSize, timeWindowUnit, interval, timeField, dateStart, dateEnd } = queryParams; @@ -62,6 +71,22 @@ export async function timeSeriesQuery( const isCountAgg = aggType === 'count'; const isGroupAgg = !!termField; + const includeConditionInQuery = !!conditionParams; + + // Cap the maximum number of terms returned to the resultLimit if defined + // Use resultLimit + 1 because we're using the bucket selector aggregation + // to apply the threshold condition to the ES query. We don't seem to be + // able to get the true cardinality from the bucket selector (i.e., get + // the number of buckets that matched the selector condition without actually + // retrieving the bucket data). By using resultLimit + 1, we can count the number + // of buckets returned and if the value is greater than resultLimit, we know that + // there is additional alert data that we're not returning. + let terms = termSize || DEFAULT_GROUPS; + terms = includeConditionInQuery + ? terms > conditionParams.resultLimit + ? conditionParams.resultLimit + 1 + : terms + : terms; let aggParent = esQuery.body; @@ -71,9 +96,18 @@ export async function timeSeriesQuery( groupAgg: { terms: { field: termField, - size: termSize || DEFAULT_GROUPS, + size: terms, }, }, + ...(includeConditionInQuery + ? { + groupAggCount: { + stats_bucket: { + buckets_path: 'groupAgg._count', + }, + }, + } + : {}), }; // if not count add an order @@ -82,6 +116,17 @@ export async function timeSeriesQuery( aggParent.aggs.groupAgg.terms.order = { sortValueAgg: sortOrder, }; + } else if (includeConditionInQuery) { + aggParent.aggs.groupAgg.aggs = { + conditionSelector: { + bucket_selector: { + buckets_path: { + [TIME_SERIES_BUCKET_SELECTOR_PATH_NAME]: '_count', + }, + script: conditionParams.conditionScript, + }, + }, + }; } aggParent = aggParent.aggs.groupAgg; @@ -89,6 +134,7 @@ export async function timeSeriesQuery( // next, add the time window aggregation aggParent.aggs = { + ...aggParent.aggs, dateAgg: { date_range: { field: timeField, @@ -105,6 +151,17 @@ export async function timeSeriesQuery( field: aggField, }, }; + + if (isGroupAgg && includeConditionInQuery) { + aggParent.aggs.conditionSelector = { + bucket_selector: { + buckets_path: { + [TIME_SERIES_BUCKET_SELECTOR_PATH_NAME]: 'sortValueAgg', + }, + script: conditionParams.conditionScript, + }, + }; + } } aggParent = aggParent.aggs.dateAgg; @@ -133,19 +190,35 @@ export async function timeSeriesQuery( } catch (err) { // console.log('time_series_query.ts error\n', JSON.stringify(err, null, 4)); logger.warn(`${logPrefix} error: ${getEsErrorMessage(err)}`); - return { results: [] }; + return { results: [], truncated: false }; } // console.log('time_series_query.ts response\n', JSON.stringify(esResult, null, 4)); logger.debug(`${logPrefix} result: ${JSON.stringify(esResult)}`); - return getResultFromEs(isCountAgg, isGroupAgg, esResult); + return getResultFromEs({ + isCountAgg, + isGroupAgg, + isConditionInQuery: includeConditionInQuery, + esResult, + resultLimit: conditionParams?.resultLimit, + }); } -export function getResultFromEs( - isCountAgg: boolean, - isGroupAgg: boolean, - esResult: estypes.SearchResponse -): TimeSeriesResult { +interface GetResultFromEsParams { + isCountAgg: boolean; + isGroupAgg: boolean; + isConditionInQuery: boolean; + esResult: estypes.SearchResponse; + resultLimit?: number; +} + +export function getResultFromEs({ + isCountAgg, + isGroupAgg, + isConditionInQuery, + esResult, + resultLimit, +}: GetResultFromEsParams): TimeSeriesResult { const aggregations = esResult?.aggregations || {}; // add a fake 'all documents' group aggregation, if a group aggregation wasn't used @@ -161,11 +234,16 @@ export function getResultFromEs( // @ts-expect-error specify aggregations type explicitly const groupBuckets = aggregations.groupAgg?.buckets || []; + // @ts-expect-error specify aggregations type explicitly + const numGroupsTotal = aggregations.groupAggCount?.count ?? 0; const result: TimeSeriesResult = { results: [], + truncated: isConditionInQuery && resultLimit ? numGroupsTotal > resultLimit : false, }; for (const groupBucket of groupBuckets) { + if (resultLimit && result.results.length === resultLimit) break; + const groupName: string = `${groupBucket?.key}`; const dateBuckets = groupBucket?.dateAgg?.buckets || []; const groupResult: TimeSeriesResultRow = { diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts index d59d99c59419f..491bea6522dec 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts @@ -45,6 +45,13 @@ export const TimeSeriesQuerySchema = schema.object( } ); +export const TimeSeriesConditionSchema = schema.object({ + resultLimit: schema.number(), + conditionScript: schema.string({ minLength: 1 }), +}); + +export type TimeSeriesCondition = TypeOf; + // using direct type not allowed, circular reference, so body is typed to unknown function validateBody(anyParams: unknown): string | undefined { // validate core query parts, return if it fails validation (returning string) diff --git a/x-pack/plugins/triggers_actions_ui/server/index.ts b/x-pack/plugins/triggers_actions_ui/server/index.ts index 5b81caa235a29..e96ced7c7583c 100644 --- a/x-pack/plugins/triggers_actions_ui/server/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/index.ts @@ -17,6 +17,7 @@ export { MAX_INTERVALS, MAX_GROUPS, DEFAULT_GROUPS, + TIME_SERIES_BUCKET_SELECTOR_FIELD, } from './data'; export const config: PluginConfigDescriptor = { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/cases_webhook.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/cases_webhook.ts similarity index 95% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/cases_webhook.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/cases_webhook.ts index fed6273d28d52..60640e3121bd4 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/cases_webhook.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/cases_webhook.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ExternalServiceSimulator, getExternalServiceSimulatorPath, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function casesWebhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/jira.ts similarity index 92% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/jira.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/jira.ts index 94a6a0784feef..b00eb360f23dd 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/jira.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function jiraTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/resilient.ts similarity index 92% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/resilient.ts index 93688e96e8417..93bbbab5531ee 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/resilient.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function resilientTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/servicenow.ts similarity index 92% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/servicenow.ts index 4cd018a2647dc..6b767f44aefe3 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/servicenow.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function servicenowTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/swimlane.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/swimlane.ts similarity index 93% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/swimlane.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/swimlane.ts index 95e041bbeb03a..a588e890d653a 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/swimlane.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/cases/swimlane.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ExternalServiceSimulator, getExternalServiceSimulatorPath, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function swimlaneTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/email.ts similarity index 93% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/email.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/email.ts index 7b901810d8e70..18429bb0d1f52 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/email.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function emailTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/es_index.ts similarity index 91% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/es_index.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/es_index.ts index 8e55043b476f8..19eec58117e84 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/es_index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function indexTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/pagerduty.ts similarity index 93% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/pagerduty.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/pagerduty.ts index 7973e02cfe08a..818c75d921e05 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/pagerduty.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function pagerdutyTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/server_log.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/server_log.ts similarity index 90% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/server_log.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/server_log.ts index 3aafc942250f0..ac38ca991871c 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/server_log.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/server_log.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function serverLogTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/slack.ts similarity index 88% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/slack.ts index ee9ce7131b07b..45769d8e1995a 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/slack.ts @@ -7,9 +7,9 @@ import http from 'http'; import getPort from 'get-port'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getSlackServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getSlackServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/webhook.ts similarity index 89% rename from x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts rename to x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/webhook.ts index 0019c274774e6..b05cf51d91410 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/stack/webhook.ts @@ -7,8 +7,8 @@ import http from 'http'; import getPort from 'get-port'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getWebhookServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getWebhookServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/index.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/index.ts index 8251a1b6fb3f1..d3deb44dc19f9 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/index.ts @@ -8,17 +8,17 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function actionsTests({ loadTestFile }: FtrProviderContext) { - describe('Actions', () => { - loadTestFile(require.resolve('./builtin_action_types/cases_webhook')); - loadTestFile(require.resolve('./builtin_action_types/email')); - loadTestFile(require.resolve('./builtin_action_types/es_index')); - loadTestFile(require.resolve('./builtin_action_types/jira')); - loadTestFile(require.resolve('./builtin_action_types/pagerduty')); - loadTestFile(require.resolve('./builtin_action_types/swimlane')); - loadTestFile(require.resolve('./builtin_action_types/server_log')); - loadTestFile(require.resolve('./builtin_action_types/servicenow')); - loadTestFile(require.resolve('./builtin_action_types/slack')); - loadTestFile(require.resolve('./builtin_action_types/webhook')); +export default function connectorsTests({ loadTestFile }: FtrProviderContext) { + describe('Connectors', () => { + loadTestFile(require.resolve('./connector_types/cases/cases_webhook')); + loadTestFile(require.resolve('./connector_types/cases/jira')); + loadTestFile(require.resolve('./connector_types/cases/servicenow')); + loadTestFile(require.resolve('./connector_types/cases/swimlane')); + loadTestFile(require.resolve('./connector_types/stack/email')); + loadTestFile(require.resolve('./connector_types/stack/es_index')); + loadTestFile(require.resolve('./connector_types/stack/pagerduty')); + loadTestFile(require.resolve('./connector_types/stack/server_log')); + loadTestFile(require.resolve('./connector_types/stack/slack')); + loadTestFile(require.resolve('./connector_types/stack/webhook')); }); } diff --git a/x-pack/test/alerting_api_integration/common/lib/es_test_index_tool.ts b/x-pack/test/alerting_api_integration/common/lib/es_test_index_tool.ts index f66ad0bcd46e1..2af943785e612 100644 --- a/x-pack/test/alerting_api_integration/common/lib/es_test_index_tool.ts +++ b/x-pack/test/alerting_api_integration/common/lib/es_test_index_tool.ts @@ -50,6 +50,9 @@ export class ESTestIndexTool { testedValue: { type: 'long', }, + testedValueFloat: { + type: 'float', + }, testedValueUnsigned: { type: 'unsigned_long', }, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/cases_webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/cases_webhook.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/cases_webhook.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/cases_webhook.ts index d1e5f9a023043..d56da37907974 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/cases_webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/cases_webhook.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function casesWebhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/jira.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/jira.ts index 5a6e0967736a2..785c62f92c53a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/jira.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function jiraTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/resilient.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/resilient.ts index fec22ab72e1ef..2f2e0142a4a87 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/resilient.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function resilientTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itom.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itom.ts index 0771e4e293726..6f45de7859941 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itom.ts @@ -11,9 +11,9 @@ import { asyncForEach } from '@kbn/std'; import getPort from 'get-port'; import http from 'http'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function serviceNowITOMTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itsm.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itsm.ts index 368e1b104a87e..2bdda087af713 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_itsm.ts @@ -11,9 +11,9 @@ import { asyncForEach } from '@kbn/std'; import getPort from 'get-port'; import http from 'http'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function serviceNowITSMTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_sir.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_sir.ts index f08ca542e4617..c1a28cc9a6e63 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/servicenow_sir.ts @@ -11,9 +11,9 @@ import { asyncForEach } from '@kbn/std'; import getPort from 'get-port'; import http from 'http'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function serviceNowSIRTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/swimlane.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/swimlane.ts index 4119a409d7a4b..2df5d5d9e3362 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases/swimlane.ts @@ -10,9 +10,9 @@ import expect from '@kbn/expect'; import getPort from 'get-port'; import http from 'http'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getSwimlaneServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { getSwimlaneServer } from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function swimlaneTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/oauth_access_token.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/oauth_access_token.ts similarity index 100% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/oauth_access_token.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/oauth_access_token.ts diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/email.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/email.ts index 4d9282d4fdeea..f94655cca71fe 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/email.ts @@ -7,11 +7,11 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { ExternalServiceSimulator, getExternalServiceSimulatorPath, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function emailTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index.ts index fed3acba1147e..ed3e459acbb8a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index.ts @@ -7,7 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; const ES_TEST_INDEX_NAME = 'functional-test-actions-index'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index_preconfigured.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index_preconfigured.ts index a447921ac5041..4c155cb2c4eb3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/es_index_preconfigured.ts @@ -7,7 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; // from: x-pack/test/alerting_api_integration/common/config.ts const ACTION_ID = 'preconfigured-es-index-action'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/pagerduty.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/pagerduty.ts index 731eab7dad0f3..96cf87b7ff81f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/pagerduty.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function pagerdutyTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/server_log.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/server_log.ts index b8b12d6aac764..e41c729a50c30 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/server_log.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function serverLogTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/slack.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/slack.ts index a05c622d8a1cf..9bf2a1267a7bd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/slack.ts @@ -9,10 +9,10 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; import http from 'http'; import getPort from 'get-port'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; -import { getSlackServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getSlackServer } from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/webhook.ts similarity index 97% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/webhook.ts index c484dfad69539..ff988254a4d6e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/webhook.ts @@ -10,13 +10,13 @@ import http from 'http'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; import getPort from 'get-port'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, getWebhookServer, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; const defaultValues: Record = { headers: null, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/xmatters.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/xmatters.ts index e33597057cfe1..cbe27bbdc6bee 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/stack/xmatters.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function xmattersTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index 922b3500266ff..5207b0c711411 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -9,8 +9,8 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { setupSpacesAndUsers, tearDown } from '../../../setup'; // eslint-disable-next-line import/no-default-export -export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { - describe('Actions', () => { +export default function connectorsTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Connectors', () => { before(async () => { await setupSpacesAndUsers(getService); }); @@ -18,22 +18,22 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo after(async () => { await tearDown(getService); }); - loadTestFile(require.resolve('./builtin_action_types/cases_webhook')); - loadTestFile(require.resolve('./builtin_action_types/email')); - loadTestFile(require.resolve('./builtin_action_types/es_index')); - loadTestFile(require.resolve('./builtin_action_types/es_index_preconfigured')); - loadTestFile(require.resolve('./builtin_action_types/pagerduty')); - loadTestFile(require.resolve('./builtin_action_types/swimlane')); - loadTestFile(require.resolve('./builtin_action_types/server_log')); - loadTestFile(require.resolve('./builtin_action_types/oauth_access_token')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_itsm')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_sir')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_itom')); - loadTestFile(require.resolve('./builtin_action_types/jira')); - loadTestFile(require.resolve('./builtin_action_types/resilient')); - loadTestFile(require.resolve('./builtin_action_types/slack')); - loadTestFile(require.resolve('./builtin_action_types/webhook')); - loadTestFile(require.resolve('./builtin_action_types/xmatters')); + loadTestFile(require.resolve('./connector_types/oauth_access_token')); + loadTestFile(require.resolve('./connector_types/cases/cases_webhook')); + loadTestFile(require.resolve('./connector_types/cases/jira')); + loadTestFile(require.resolve('./connector_types/cases/resilient')); + loadTestFile(require.resolve('./connector_types/cases/servicenow_itsm')); + loadTestFile(require.resolve('./connector_types/cases/servicenow_sir')); + loadTestFile(require.resolve('./connector_types/cases/servicenow_itom')); + loadTestFile(require.resolve('./connector_types/cases/swimlane')); + loadTestFile(require.resolve('./connector_types/stack/email')); + loadTestFile(require.resolve('./connector_types/stack/es_index')); + loadTestFile(require.resolve('./connector_types/stack/es_index_preconfigured')); + loadTestFile(require.resolve('./connector_types/stack/pagerduty')); + loadTestFile(require.resolve('./connector_types/stack/server_log')); + loadTestFile(require.resolve('./connector_types/stack/slack')); + loadTestFile(require.resolve('./connector_types/stack/webhook')); + loadTestFile(require.resolve('./connector_types/stack/xmatters')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./execute')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/email.ts similarity index 97% rename from x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/email.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/email.ts index cf31d6111c349..eb2ddc33ce444 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/email.ts @@ -6,9 +6,9 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { ObjectRemover } from '../../../../common/lib'; -import { EmailDomainsAllowed } from '../../../config'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { ObjectRemover } from '../../../../../common/lib'; +import { EmailDomainsAllowed } from '../../../../config'; const EmailDomainAllowed = EmailDomainsAllowed[EmailDomainsAllowed.length - 1]; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts similarity index 98% rename from x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts index c8161c54569b5..aa3b2b6780dc8 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts @@ -7,7 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; const ES_TEST_INDEX_NAME = 'functional-test-connectors-index'; const ES_TEST_DATASTREAM_PREFIX = 'functional-test-connectors-ds'; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/preconfigured_alert_history_connector.ts similarity index 97% rename from x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/preconfigured_alert_history_connector.ts index 79e5928c61739..ad37f8220f395 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/preconfigured_alert_history_connector.ts @@ -7,8 +7,8 @@ import expect from '@kbn/expect'; import { AlertHistoryDefaultIndexName } from '@kbn/actions-plugin/common'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getTestRuleData, ObjectRemover } from '../../../../../common/lib'; const ALERT_HISTORY_OVERRIDE_INDEX = 'kibana-alert-history-not-the-default'; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts similarity index 96% rename from x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts index 9822254db444a..ef5912dc0f8f3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts @@ -10,12 +10,12 @@ import https from 'https'; import getPort from 'get-port'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getWebhookServer, getHttpsWebhookServer, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; -import { createTlsWebhookServer } from '../../../../common/lib/get_tls_webhook_servers'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { createTlsWebhookServer } from '../../../../../common/lib/get_tls_webhook_servers'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts index 5726023d9dc72..866f13ed5294c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts @@ -23,10 +23,10 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo loadTestFile(require.resolve('./monitoring_collection')); loadTestFile(require.resolve('./execute')); loadTestFile(require.resolve('./enqueue')); - loadTestFile(require.resolve('./builtin_action_types/email')); - loadTestFile(require.resolve('./builtin_action_types/es_index')); - loadTestFile(require.resolve('./builtin_action_types/webhook')); - loadTestFile(require.resolve('./builtin_action_types/preconfigured_alert_history_connector')); + loadTestFile(require.resolve('./connector_types/stack/email')); + loadTestFile(require.resolve('./connector_types/stack/es_index')); + loadTestFile(require.resolve('./connector_types/stack/webhook')); + loadTestFile(require.resolve('./connector_types/stack/preconfigured_alert_history_connector')); loadTestFile(require.resolve('./type_not_enabled')); // note that this test will destroy existing spaces diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/index.ts index 8f10635975ba0..98c0737ba670c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/index.ts @@ -11,5 +11,6 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; export default function alertingTests({ loadTestFile }: FtrProviderContext) { describe('es_query', () => { loadTestFile(require.resolve('./rule')); + loadTestFile(require.resolve('./query_dsl_only')); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/query_dsl_only.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/query_dsl_only.ts index 1f7930fc1bac4..9ba78b31ea5ec 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/query_dsl_only.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/query_dsl_only.ts @@ -159,7 +159,7 @@ export default function ruleTests({ getService }: FtrProviderContext) { "fields": [ { "field": "@timestamp", - "format": "epoch_millis" + "format": "epoch_millis" } ], "query": { @@ -190,6 +190,73 @@ export default function ruleTests({ getService }: FtrProviderContext) { } }); + it(`runs correctly: _source: false field for esQuery search type`, async () => { + // write documents from now to the future end date in groups + await createEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate); + await createRule({ + name: 'always fire', + esQuery: ` + { + "query": { + "match_all": { } + }, + "_source": false + }`.replace(`"`, `\"`), + size: 100, + thresholdComparator: '>', + threshold: [-1], + }); + + const docs = await waitForDocs(2); + for (let i = 0; i < docs.length; i++) { + const doc = docs[i]; + const { name, title } = doc._source.params; + expect(name).to.be('always fire'); + expect(title).to.be(`rule 'always fire' matched query`); + + const hits = JSON.parse(doc._source.hits); + expect(hits).not.to.be.empty(); + hits.forEach((hit: any) => { + expect(hit._source).to.be(undefined); + }); + } + }); + + it(`runs correctly: _source field for esQuery search type`, async () => { + // write documents from now to the future end date in groups + await createEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate); + await createRule({ + name: 'always fire', + esQuery: ` + { + "query": { + "match_all": { } + }, + "_source": "testedValue*" + }`.replace(`"`, `\"`), + size: 100, + thresholdComparator: '>', + threshold: [-1], + }); + + const docs = await waitForDocs(2); + for (let i = 0; i < docs.length; i++) { + const doc = docs[i]; + const { name, title } = doc._source.params; + expect(name).to.be('always fire'); + expect(title).to.be(`rule 'always fire' matched query`); + + const hits = JSON.parse(doc._source.hits); + expect(hits).not.to.be.empty(); + hits.forEach((hit: any) => { + expect(hit._source).not.to.be.empty(); + Object.keys(hit._source).forEach((key) => { + expect(key.startsWith('testedValue')).to.be(true); + }); + }); + } + }); + async function createRule(params: CreateRuleParams): Promise { const action = { id: connectorId, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index ab6a3023141a8..c06eb41759d63 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -265,6 +265,45 @@ export default function ruleTests({ getService }: FtrProviderContext) { expect(inGroup2).to.be.greaterThan(0); }); + it('runs correctly: max grouped on float', async () => { + await createRule({ + name: 'never fire', + aggType: 'max', + aggField: 'testedValueFloat', + groupBy: 'top', + termField: 'group', + termSize: 2, + thresholdComparator: '<', + threshold: [3.235423], + }); + + await createRule({ + name: 'always fire', + aggType: 'max', + aggField: 'testedValueFloat', + groupBy: 'top', + termField: 'group', + termSize: 2, // two actions will fire each interval + thresholdComparator: '>=', + threshold: [200.2354364], + }); + + // create some more documents in the first group + await createEsDocumentsInGroups(1); + + const docs = await waitForDocs(4); + + for (const doc of docs) { + const { name, message } = doc._source.params; + + expect(name).to.be('always fire'); + + const messagePattern = + /alert 'always fire' is active for group \'group-\d\':\n\n- Value: 234.2534637451172\n- Conditions Met: max\(testedValueFloat\) is greater than or equal to 200.2354364 over 15s\n- Timestamp: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; + expect(message).to.match(messagePattern); + } + }); + it('runs correctly: max grouped on unsigned long', async () => { await createRule({ name: 'never fire', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts index 5eee05a916da3..61fec5cf7e481 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts @@ -82,6 +82,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider const expected = { results: [{ group: 'all documents', metrics: [[START_DATE_PLUS_YEAR, 0]] }], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -95,6 +96,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider const expected = { results: [{ group: 'all documents', metrics: [[START_DATE_MINUS_YEAR, 0]] }], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -108,6 +110,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider const expected = { results: [{ group: 'all documents', metrics: [[START_DATE, 6]] }], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -130,6 +133,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider ], }, ], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -154,6 +158,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider ], }, ], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -185,6 +190,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider ], }, ], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -220,6 +226,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider ], }, ], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); @@ -289,6 +296,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider }); const expected = { results: [{ group: 'all documents', metrics: [[START_DATE, 6]] }], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); }); @@ -303,6 +311,7 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider }); const expected = { results: [], + truncated: false, }; expect(await runQueryExpect(query, 200)).eql(expected); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/lib/create_test_data.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/lib/create_test_data.ts index 8fbb4d0bc6805..58df573fa629e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/lib/create_test_data.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/lib/create_test_data.ts @@ -106,6 +106,7 @@ async function createEsDocument( date: new Date(epochMillis).toISOString(), date_epoch_millis: epochMillis, testedValue, + testedValueFloat: 234.2534643, testedValueUnsigned: '18446744073709551615', '@timestamp': new Date(epochMillis).toISOString(), ...(group ? { group } : {}), diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/connector_types/stack/webhook.ts similarity index 96% rename from x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/builtin_action_types/webhook.ts rename to x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/connector_types/stack/webhook.ts index 9822254db444a..ef5912dc0f8f3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/connector_types/stack/webhook.ts @@ -10,12 +10,12 @@ import https from 'https'; import getPort from 'get-port'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getWebhookServer, getHttpsWebhookServer, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; -import { createTlsWebhookServer } from '../../../../common/lib/get_tls_webhook_servers'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { createTlsWebhookServer } from '../../../../../common/lib/get_tls_webhook_servers'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts index dda1376a432a3..a8b55159f9192 100644 --- a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts @@ -11,7 +11,7 @@ import { Spaces } from '../scenarios'; // eslint-disable-next-line import/no-default-export export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('alerting api integration spaces only legacy configuration', function () { - loadTestFile(require.resolve('./actions/builtin_action_types/webhook')); + loadTestFile(require.resolve('./actions/connector_types/stack/webhook')); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_private_location.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_private_location.ts index 826190b73385f..12ef5ac975406 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_private_location.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_private_location.ts @@ -35,7 +35,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertestAPI - .post('/api/fleet/epm/packages/synthetics/0.10.2') + .post('/api/fleet/epm/packages/synthetics/0.10.3') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts index 75361d7ae213e..2be3509a61cf6 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts @@ -19,8 +19,7 @@ import { PrivateLocationTestService } from './services/private_location_test_ser import { comparePolicies, getTestProjectSyntheticsPolicy } from './sample_data/test_policy'; export default function ({ getService }: FtrProviderContext) { - // FLAKY: https://github.com/elastic/kibana/issues/142110 - describe.skip('AddProjectMonitors', function () { + describe('AddProjectMonitors', function () { this.tags('skipCloud'); const supertest = getService('supertest'); @@ -79,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.10.2') + .post('/api/fleet/epm/packages/synthetics/0.10.3') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); @@ -708,6 +707,7 @@ export default function ({ getService }: FtrProviderContext) { ...projectMonitors, keep_stale: false, monitors: testMonitors, + project: 'test-project-2', }); const messages = await parseStreamApiResponse( @@ -715,6 +715,7 @@ export default function ({ getService }: FtrProviderContext) { JSON.stringify({ ...projectMonitors, keep_stale: false, + project: 'test-project-2', }) ); @@ -1798,6 +1799,14 @@ export default function ({ getService }: FtrProviderContext) { config_id: { value: configId, type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { value: 'project', type: 'text' }, + 'monitor.project.id': { + type: 'text', + value: 'test-suite', + }, + 'monitor.project.name': { + type: 'text', + value: 'test-suite', + }, }, id: `synthetics/http-http-${id}-${testPolicyId}`, compiled_stream: { @@ -1828,6 +1837,8 @@ export default function ({ getService }: FtrProviderContext) { fields: { 'monitor.fleet_managed': true, config_id: configId, + 'monitor.project.id': 'test-suite', + 'monitor.project.name': 'test-suite', }, }, }, @@ -1859,7 +1870,7 @@ export default function ({ getService }: FtrProviderContext) { expect(packagePolicy2).eql(undefined); } finally { - await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project); + await deleteMonitor(httpProjectMonitors.monitors[1].id, httpProjectMonitors.project); const apiResponsePolicy2 = await supertest.get( '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics' diff --git a/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts index 37536c329cbb6..b9fd0fac8ac2a 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts @@ -41,7 +41,7 @@ export default function ({ getService }: FtrProviderContext) { _httpMonitorJson = getFixtureJson('http_monitor'); await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.10.2') + .post('/api/fleet/epm/packages/synthetics/0.10.3') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/uptime/rest/sample_data/test_policy.ts b/x-pack/test/api_integration/apis/uptime/rest/sample_data/test_policy.ts index ed9fa70ab4f59..05bba2286e004 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/sample_data/test_policy.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/sample_data/test_policy.ts @@ -19,7 +19,7 @@ export const getTestSyntheticsPolicy = ( version: 'WzE2MjYsMV0=', name: 'test-monitor-name-Test private location 0-default', namespace: namespace || 'testnamespace', - package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.10.2' }, + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.10.3' }, enabled: true, policy_id: '5347cd10-0368-11ed-8df7-a7424c6f5167', inputs: [ @@ -73,6 +73,8 @@ export const getTestSyntheticsPolicy = ( config_id: { value: id, type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { type: 'text', value: null }, + 'monitor.project.name': { type: 'text', value: null }, }, id: 'synthetics/http-http-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', compiled_stream: { @@ -158,6 +160,8 @@ export const getTestSyntheticsPolicy = ( config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, }, id: 'synthetics/tcp-tcp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', }, @@ -188,6 +192,8 @@ export const getTestSyntheticsPolicy = ( config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, }, id: 'synthetics/icmp-icmp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', }, @@ -312,7 +318,7 @@ export const getTestProjectSyntheticsPolicy = ( version: 'WzEzMDksMV0=', name: '4b6abc6c-118b-4d93-a489-1135500d09f1-test-suite-default-Test private location 0', namespace: 'default', - package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.10.2' }, + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.10.3' }, enabled: true, policy_id: '46034710-0ba6-11ed-ba04-5f123b9faa8b', inputs: [ @@ -359,6 +365,8 @@ export const getTestProjectSyntheticsPolicy = ( config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, }, id: 'synthetics/http-http-4b6abc6c-118b-4d93-a489-1135500d09f1-test-suite-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3', }, @@ -398,6 +406,8 @@ export const getTestProjectSyntheticsPolicy = ( config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, }, id: 'synthetics/tcp-tcp-4b6abc6c-118b-4d93-a489-1135500d09f1-test-suite-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3', }, @@ -428,6 +438,8 @@ export const getTestProjectSyntheticsPolicy = ( config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, }, id: 'synthetics/icmp-icmp-4b6abc6c-118b-4d93-a489-1135500d09f1-test-suite-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3', }, diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index 8400cccd64d4f..78fb376fa89b8 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -26,17 +26,6 @@ export interface ApmFtrConfig { kibanaConfig?: Record; } -function getLegacySupertestClient(kibanaServer: UrlObject, username: ApmUsername) { - return async (context: InheritedFtrProviderContext) => { - const url = format({ - ...kibanaServer, - auth: `${username}:${APM_TEST_PASSWORD}`, - }); - - return supertest(url); - }; -} - async function getApmApiClient({ kibanaServer, username, @@ -125,15 +114,6 @@ export function createTestConfig(config: ApmFtrConfig) { }; }, ml: MachineLearningAPIProvider, - // legacy clients - legacySupertestAsApmWriteUser: getLegacySupertestClient( - kibanaServer, - ApmUsername.editorUser - ), - legacySupertestAsApmReadUserWithoutMlAccess: getLegacySupertestClient( - kibanaServer, - ApmUsername.apmReadUserWithoutMlAccess - ), }, junit: { reportName: `APM API Integration tests (${name})`, diff --git a/x-pack/test/apm_api_integration/common/registry.ts b/x-pack/test/apm_api_integration/common/registry.ts index 177600208b73d..4213b7736a950 100644 --- a/x-pack/test/apm_api_integration/common/registry.ts +++ b/x-pack/test/apm_api_integration/common/registry.ts @@ -10,6 +10,10 @@ import { castArray, groupBy } from 'lodash'; import callsites from 'callsites'; import { maybe } from '@kbn/apm-plugin/common/utils/maybe'; import { joinByKey } from '@kbn/apm-plugin/common/utils/join_by_key'; +import { + ApmUsername, + APM_TEST_PASSWORD, +} from '@kbn/apm-plugin/server/test_helpers/create_apm_users/authentication'; import { APMFtrConfigName } from '../configs'; import { FtrProviderContext } from './ftr_provider_context'; @@ -104,8 +108,7 @@ export function RegistryProvider({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const logger = getService('log'); - - const supertest = getService('legacySupertestAsApmWriteUser'); + const supertest = getService('supertest'); const logWithTimer = () => { const start = process.hrtime(); @@ -148,7 +151,10 @@ export function RegistryProvider({ getService }: FtrProviderContext) { ); // sync jobs from .ml-config to .kibana SOs - await supertest.get('/api/ml/saved_objects/sync').set('kbn-xsrf', 'foo'); + await supertest + .get('/api/ml/saved_objects/sync') + .set('kbn-xsrf', 'foo') + .auth(ApmUsername.editorUser, APM_TEST_PASSWORD); } if (condition.archives.length) { log('Loaded all archives'); diff --git a/x-pack/test/apm_api_integration/tests/dependencies/top_operations.spec.ts b/x-pack/test/apm_api_integration/tests/dependencies/top_operations.spec.ts index 8d8de18eb6e01..41c18a8bf35b3 100644 --- a/x-pack/test/apm_api_integration/tests/dependencies/top_operations.spec.ts +++ b/x-pack/test/apm_api_integration/tests/dependencies/top_operations.spec.ts @@ -8,6 +8,8 @@ import expect from '@kbn/expect'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; import { ValuesType } from 'utility-types'; +import { DependencyOperation } from '@kbn/apm-plugin/server/routes/dependencies/get_top_dependency_operations'; +import { meanBy } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { roundNumber } from '../../utils'; import { generateOperationData, generateOperationDataConfig } from './generate_operation_data'; @@ -37,10 +39,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { dependencyName, environment = ENVIRONMENT_ALL.value, kuery = '', + searchServiceDestinationMetrics = false, }: { dependencyName: string; environment?: string; kuery?: string; + searchServiceDestinationMetrics?: boolean; }) { return await apmApiClient .readUser({ @@ -52,6 +56,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment, kuery, dependencyName, + searchServiceDestinationMetrics, }, }, }) @@ -210,5 +215,60 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(bulkOperation).to.be.ok(); }); }); + + describe('Compare span metrics and span events', () => { + let bulkOperationSpanEventsResponse: ValuesType; + let bulkOperationSpanMetricsResponse: ValuesType; + + before(async () => { + const [spanEventsResponse, spanMetricsResponse] = await Promise.all([ + callApi({ dependencyName: 'elasticsearch', searchServiceDestinationMetrics: false }), + callApi({ dependencyName: 'elasticsearch', searchServiceDestinationMetrics: true }), + ]); + function findBulkOperation(op: DependencyOperation) { + return op.spanName === '/_bulk'; + } + bulkOperationSpanEventsResponse = spanEventsResponse.find(findBulkOperation)!; + bulkOperationSpanMetricsResponse = spanMetricsResponse.find(findBulkOperation)!; + }); + + it('returns same latency', () => { + expect(bulkOperationSpanEventsResponse.latency).to.eql( + bulkOperationSpanMetricsResponse.latency + ); + + const meanSpanMetrics = meanBy( + bulkOperationSpanEventsResponse.timeseries.latency.filter(({ y }) => y !== null), + 'y' + ); + const meanSpanEvents = meanBy( + bulkOperationSpanMetricsResponse.timeseries.latency.filter(({ y }) => y !== null), + 'y' + ); + expect(meanSpanMetrics).to.eql(meanSpanEvents); + }); + + it('returns same throughput', () => { + expect(bulkOperationSpanEventsResponse.throughput).to.eql( + bulkOperationSpanMetricsResponse.throughput + ); + + const meanSpanMetrics = meanBy( + bulkOperationSpanEventsResponse.timeseries.throughput.filter(({ y }) => y !== 0), + 'y' + ); + const meanSpanEvents = meanBy( + bulkOperationSpanMetricsResponse.timeseries.throughput.filter(({ y }) => y !== 0), + 'y' + ); + expect(meanSpanMetrics).to.eql(meanSpanEvents); + }); + + it('returns same impact', () => { + expect(bulkOperationSpanEventsResponse.impact).to.eql( + bulkOperationSpanMetricsResponse.impact + ); + }); + }); }); } diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts index 21d2ad617f112..24267660397ea 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts @@ -6,11 +6,12 @@ */ import expect from '@kbn/expect'; +import { APIClientRequestParamsOf } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { FtrProviderContext } from '../common/ftr_provider_context'; export default function featureControlsTests({ getService }: FtrProviderContext) { const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmWriteUser'); + const apmApiClient = getService('apmApiClient'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); const spaces = getService('spaces'); @@ -40,6 +41,29 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expectResponse: (result: any) => void; onExpectationFail?: () => Promise; } + + function createAgent( + body: APIClientRequestParamsOf<'PUT /api/apm/settings/agent-configuration'>['params']['body'] + ) { + return apmApiClient.writeUser({ + endpoint: 'PUT /api/apm/settings/agent-configuration', + params: { + body, + }, + }); + } + + function deleteAgent( + body: APIClientRequestParamsOf<'DELETE /api/apm/settings/agent-configuration'>['params']['body'] + ) { + return apmApiClient.writeUser({ + endpoint: 'DELETE /api/apm/settings/agent-configuration', + params: { + body, + }, + }); + } + const endpoints: Endpoint[] = [ { // this doubles as a smoke test for the _inspect query parameter @@ -200,28 +224,6 @@ export default function featureControlsTests({ getService }: FtrProviderContext) .catch((error: any) => ({ error, response: undefined })); } - async function executeAsAdmin({ method = 'get', url, body }: Endpoint['req'], spaceId?: string) { - const basePath = spaceId ? `/s/${spaceId}` : ''; - const fullPath = `${basePath}${url}`; - let request = supertest[method](fullPath); - - // json body - if (body) { - request = request.send(body); - } - - const response = await request.set('kbn-xsrf', 'foo'); - - const { status } = response; - if (status !== 200) { - throw new Error(`Endpoint: ${method} ${fullPath} - Status code: ${status} - Response: ${response.body.message}`); - } - - return response; - } - async function executeRequests({ username, password, @@ -268,23 +270,14 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }; before(async () => { log.info(`Creating agent configuration`); - await executeAsAdmin({ - method: 'put', - url: '/api/apm/settings/agent-configuration', - body: config, - }); + await createAgent(config); log.info(`Agent configuration created`); }); after(async () => { log.info('deleting agent configuration'); - await executeAsAdmin({ - method: 'delete', - url: `/api/apm/settings/agent-configuration`, - body: { - service: config.service, - }, - }); + await deleteAgent({ service: config.service }); + log.info('Agent configuration deleted'); }); it(`APIs can't be accessed by logstash_read user`, async () => { diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.spec.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.spec.ts index 0bf4bec9ee8b1..bd86ec66a7aab 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.spec.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.spec.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); - const legacyWriteUserClient = getService('legacySupertestAsApmWriteUser'); + const ml = getService('ml'); function getJobs() { return apmApiClient.writeUser({ @@ -30,17 +30,14 @@ export default function apiTest({ getService }: FtrProviderContext) { } function deleteJobs(jobIds: string[]) { - return legacyWriteUserClient - .post(`/api/ml/jobs/delete_jobs`) - .send({ jobIds }) - .set('kbn-xsrf', 'foo'); + return Promise.allSettled(jobIds.map((jobId) => ml.deleteAnomalyDetectionJobES(jobId))); } registry.when('ML jobs', { config: 'trial', archives: [] }, () => { describe('when user has write access to ML', () => { after(async () => { const res = await getJobs(); - const jobIds = res.body.jobs.map((job: any) => job.job_id); + const jobIds = res.body.jobs.map((job: any) => job.jobId); await deleteJobs(jobIds); }); 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 index e75a35d88acc3..7619c6f3e359a 100644 --- 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 @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL } from '@kbn/security-solution-plugin/common/constants'; @@ -13,6 +15,7 @@ import { ExceptionListTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { RuleReferencesSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/response'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createRule, @@ -66,10 +69,46 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - expect(references).to.eql({ references: [{ i_exist: [] }] }); + const { + _version, + id, + created_at, + created_by, + tie_breaker_id, + updated_at, + updated_by, + ...referencesWithoutServerValues + } = references.references[0].i_exist; + + expect({ + references: [ + { + i_exist: { + ...referencesWithoutServerValues, + }, + }, + ], + }).to.eql({ + references: [ + { + i_exist: { + description: 'some description', + immutable: false, + list_id: 'i_exist', + name: 'some name', + namespace_type: 'single', + os_types: [], + tags: [], + type: 'detection', + version: 1, + referenced_rules: [], + }, + }, + ], + }); }); - it('returns empty array per list_id if list does not exist', async () => { + it('returns empty array per list_id if list does not exist', async () => { // create rule await createRule(supertest, log, getSimpleRule('rule-1')); @@ -83,7 +122,7 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - expect(references).to.eql({ references: [{ i_dont_exist: [] }] }); + expect(references).to.eql({ references: [] }); }); it('returns found references', async () => { @@ -101,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }); // create rule - const rule = await createRule(supertest, log, { + await createRule(supertest, log, { ...getSimpleRule('rule-2'), exceptions_list: [ { @@ -129,56 +168,54 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - expect(references).to.eql({ - references: [ + const refs = references.references.flatMap((ref: RuleReferencesSchema) => Object.keys(ref)); + + expect(refs.sort()).to.eql(['i_exist', 'i_exist_2'].sort()); + }); + + it('returns found references for all existing exception lists if no list id/list_id passed in', 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 + await createRule(supertest, log, { + ...getSimpleRule('rule-2'), + exceptions_list: [ { - 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', - }, - ], + id: `${exceptionList.id}`, + list_id: `${exceptionList.list_id}`, + namespace_type: `${exceptionList.namespace_type}`, + type: `${exceptionList.type}`, }, { - 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', - }, - ], + 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({ + namespace_types: `${exceptionList.namespace_type},${exceptionList2.namespace_type}`, + }) + .expect(200); + + const refs = references.references.flatMap((ref: RuleReferencesSchema) => Object.keys(ref)); + expect(refs.sort()).to.eql(['i_exist', 'i_exist_2', 'endpoint_list'].sort()); }); }); }; diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts index b279f0d8a93cd..66de13c67d94c 100644 --- a/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts @@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Agg based Vis to Lens', function () { loadTestFile(require.resolve('./pie')); + loadTestFile(require.resolve('./metric')); }); } diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/metric.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/metric.ts new file mode 100644 index 0000000000000..f81f5200e678a --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/metric.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualize, lens, timePicker } = getPageObjects(['visualize', 'lens', 'timePicker']); + + const testSubjects = getService('testSubjects'); + + describe('Metric', function describeIndexTests() { + const isNewChartsLibraryEnabled = true; + + before(async () => { + await visualize.initTests(isNewChartsLibraryEnabled); + }); + + beforeEach(async () => { + await visualize.navigateToNewAggBasedVisualization(); + await visualize.clickMetric(); + await visualize.clickNewSearch(); + await timePicker.setDefaultAbsoluteRange(); + }); + + it('should show the "Edit Visualization in Lens" menu item', async () => { + const button = await testSubjects.exists('visualizeEditInLensButton'); + expect(button).to.eql(true); + }); + + it('should convert to Lens', async () => { + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('mtrVis'); + expect((await lens.getMetricVisualizationData()).length).to.be.equal(1); + expect(await lens.getMetricVisualizationData()).to.eql([ + { + title: 'Count', + subtitle: undefined, + extraText: '', + value: '14.01K', + color: 'rgba(245, 247, 250, 1)', + showingBar: false, + }, + ]); + }); + }); +} diff --git a/x-pack/test/functional/services/cases/list.ts b/x-pack/test/functional/services/cases/list.ts index 15e2e40b0ca71..89b09ddde5879 100644 --- a/x-pack/test/functional/services/cases/list.ts +++ b/x-pack/test/functional/services/cases/list.ts @@ -159,7 +159,7 @@ export function CasesTableServiceProvider( }, async refreshTable() { - await testSubjects.click('all-cases-refresh'); + await testSubjects.click('all-cases-refresh-link-icon'); }, async openRowActions(index: number) { @@ -177,7 +177,8 @@ export function CasesTableServiceProvider( async selectAllCasesAndOpenBulkActions() { await testSubjects.setCheckbox('checkboxSelectAll', 'check'); - const button = await find.byCssSelector('[aria-label="Bulk actions"]'); + await testSubjects.existOrFail('case-table-bulk-actions-link-icon'); + const button = await testSubjects.find('case-table-bulk-actions-link-icon'); await button.click(); }, diff --git a/x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.ts b/x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.ts index 9f402c019d0a7..e1122125c8665 100644 --- a/x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.ts +++ b/x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.ts @@ -15,6 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('check heartbeat overview page', function () { it('Uptime app should show 1 UP monitor', async function () { await PageObjects.common.navigateToApp('uptime', { insertTimestamp: false }); + await PageObjects.uptime.dismissTour(); await PageObjects.timePicker.setCommonlyUsedTime('Last_1 year'); await retry.try(async function () { diff --git a/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js index 869d56fe90047..ca6ad964d617f 100644 --- a/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js +++ b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js @@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }) { const watch = { id }; const interval = 10; const emails = REPORTING_TEST_EMAILS.split(','); - + // http://localhost:5601/api/reporting/generate/printablePdfV2?jobParams=%28browserTimezone%3AAmerica%2FNew_York%2Clayout%3A%28dimensions%3A%28height%3A2024%2Cwidth%3A1920%29%2Cid%3Apreserve_layout%29%2ClocatorParams%3A%21%28%28id%3ADASHBOARD_APP_LOCATOR%2Cparams%3A%28dashboardId%3A%27722b74f0-b882-11e8-a6d9-e546fe2bba5f%27%2CpreserveSavedFilters%3A%21t%2CtimeRange%3A%28from%3Anow-7d%2Cto%3Anow%29%2CuseHash%3A%21f%2CviewMode%3Aview%29%29%29%2CobjectType%3Adashboard%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.6.0-SNAPSHOT%27%29 // https://localhost:5601/api/reporting/generate/printablePdf?jobParams=(objectType:dashboard,queryString:%27_g%3D(refreshInterval%3A(display%3AOff%2Cpause%3A!!f%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cmode%3Aquick%2Cto%3Anow))%26_a%3D(description%3A%2527%2527%2Cfilters%3A!!()%2CfullScreenMode%3A!!f%2Coptions%3A(darkTheme%3A!!f)%2Cpanels%3A!!((col%3A1%2Cid%3ASystem-Navigation%2CpanelIndex%3A9%2Crow%3A1%2Csize_x%3A8%2Csize_y%3A1%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3Ac6f2ffd0-4d17-11e7-a196-69b9a7a020a9%2CpanelIndex%3A11%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3Afe064790-1b1f-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A12%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%2527855899e0-1b1c-11e7-b09e-037021c4f8df%2527%2CpanelIndex%3A13%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%25277cdb1330-4d1a-11e7-a196-69b9a7a020a9%2527%2CpanelIndex%3A14%2Crow%3A9%2Csize_x%3A12%2Csize_y%3A6%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3A%2527522ee670-1b92-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A16%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A11%2Cid%3A%25271aae9140-1b93-11e7-8ada-3df93aab833e%2527%2CpanelIndex%3A17%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3A%2527825fdb80-4d1d-11e7-b5f2-2b7c1895bf32%2527%2CpanelIndex%3A18%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A5%2Cid%3Ad3166e80-1b91-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A19%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A3%2Cid%3A%252783e12df0-1b91-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A20%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3Ae9d22060-4d64-11e7-aa29-87a97a796de6%2CpanelIndex%3A21%2Crow%3A1%2Csize_x%3A4%2Csize_y%3A1%2Ctype%3Avisualization))%2Cquery%3A(language%3Alucene%2Cquery%3A(query_string%3A(analyze_wildcard%3A!!t%2Cquery%3A%2527*%2527)))%2CtimeRestore%3A!!f%2Ctitle%3A%2527Metricbeat%2Bsystem%2Boverview%2527%2CuiState%3A(P-11%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-12%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-14%3A(vis%3A(defaultColors%3A(%25270%2525%2B-%2B8.75%2525%2527%3A%2527rgb(247%2C252%2C245)%2527%2C%252717.5%2525%2B-%2B26.25%2525%2527%3A%2527rgb(116%2C196%2C118)%2527%2C%252726.25%2525%2B-%2B35%2525%2527%3A%2527rgb(35%2C139%2C69)%2527%2C%25278.75%2525%2B-%2B17.5%2525%2527%3A%2527rgb(199%2C233%2C192)%2527)))%2CP-16%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-2%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-3%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527))))%2CviewMode%3Aview)%27,savedObjectId:Metricbeat-system-overview) // https://localhost:5601/api/reporting/generate/printablePdf?jobParams=(objectType:dashboard,queryString:%27_g%3D()%26_a%3D(description%3A%2527%2527%2Cfilters%3A!!()%2CfullScreenMode%3A!!f%2Coptions%3A(darkTheme%3A!!f)%2Cpanels%3A!!((col%3A1%2Cid%3ASystem-Navigation%2CpanelIndex%3A9%2Crow%3A1%2Csize_x%3A12%2Csize_y%3A1%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3Ac6f2ffd0-4d17-11e7-a196-69b9a7a020a9%2CpanelIndex%3A11%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3Afe064790-1b1f-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A12%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%2527855899e0-1b1c-11e7-b09e-037021c4f8df%2527%2CpanelIndex%3A13%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%25277cdb1330-4d1a-11e7-a196-69b9a7a020a9%2527%2CpanelIndex%3A14%2Crow%3A9%2Csize_x%3A12%2Csize_y%3A6%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3A%2527522ee670-1b92-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A16%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A11%2Cid%3A%25271aae9140-1b93-11e7-8ada-3df93aab833e%2527%2CpanelIndex%3A17%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3A%2527825fdb80-4d1d-11e7-b5f2-2b7c1895bf32%2527%2CpanelIndex%3A18%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A5%2Cid%3Ad3166e80-1b91-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A19%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A3%2Cid%3A%252783e12df0-1b91-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A20%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization))%2Cquery%3A(language%3Alucene%2Cquery%3A(query_string%3A(analyze_wildcard%3A!!t%2Cdefault_field%3A%2527*%2527%2Cquery%3A%2527*%2527)))%2CtimeRestore%3A!!f%2Ctitle%3A%2527%255BMetricbeat%2BSystem%255D%2BOverview%2527%2CuiState%3A(P-11%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-12%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-14%3A(vis%3A(defaultColors%3A(%25270%2525%2B-%2B8.75%2525%2527%3A%2527rgb(247%2C252%2C245)%2527%2C%252717.5%2525%2B-%2B26.25%2525%2527%3A%2527rgb(116%2C196%2C118)%2527%2C%252726.25%2525%2B-%2B35%2525%2527%3A%2527rgb(35%2C139%2C69)%2527%2C%25278.75%2525%2B-%2B17.5%2525%2527%3A%2527rgb(199%2C233%2C192)%2527)))%2CP-16%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-2%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-3%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527))))%2CviewMode%3Aview)%27,savedObjectId:Metricbeat-system-overview) // https://localhost:5601 @@ -40,7 +40,7 @@ export default function ({ getService, getPageObjects }) { KIBANAIP + ':' + servers.kibana.port + - '/api/reporting/generate/printablePdf?jobParams=%28browserTimezone%3AEurope%2FParis%2Clayout%3A%28dimensions%3A%28height%3A2052%2Cwidth%3A2542.666748046875%29%2Cid%3Apreserve_layout%2Cselectors%3A%28itemsCountAttribute%3Adata-shared-items-count%2CrenderComplete%3A%5Bdata-shared-item%5D%2Cscreenshot%3A%5Bdata-shared-items-container%5D%2CtimefilterDurationAttribute%3Adata-shared-timefilter-duration%29%29%2CobjectType%3Adashboard%2CrelativeUrls%3A%21%28%27%2Fapp%2Fdashboards%23%2Fview%2F722b74f0-b882-11e8-a6d9-e546fe2bba5f%3F_g%3D%28filters%3A%21%21%28%29%29%26_a%3D%28description%3A%21%27Analyze%2520mock%2520eCommerce%2520orders%2520and%2520revenue%21%27%2Cfilters%3A%21%21%28%29%2CfullScreenMode%3A%21%21f%2Coptions%3A%28hidePanelTitles%3A%21%21f%2CuseMargins%3A%21%21t%29%2Cpanels%3A%21%21%28%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A10%2Ci%3A%21%275%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A22%29%2Cid%3A%21%2745e07720-b890-11e8-a6d9-e546fe2bba5f%21%27%2CpanelIndex%3A%21%275%21%27%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3A%21%277%21%27%2Cw%3A12%2Cx%3A36%2Cy%3A15%29%2Cid%3Ab80e6540-b891-11e8-a6d9-e546fe2bba5f%2CpanelIndex%3A%21%277%21%27%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A18%2Ci%3A%21%2710%21%27%2Cw%3A48%2Cx%3A0%2Cy%3A55%29%2Cid%3A%21%273ba638e0-b894-11e8-a6d9-e546fe2bba5f%21%27%2CpanelIndex%3A%21%2710%21%27%2Ctype%3Asearch%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChiddenLayers%3A%21%21%28%29%2CisLayerTOCOpen%3A%21%21f%2CmapBuffer%3A%28maxLat%3A66.51326%2CmaxLon%3A90%2CminLat%3A0%2CminLon%3A-135%29%2CmapCenter%3A%28lat%3A45.88578%2Clon%3A-15.07605%2Czoom%3A2.11%29%2CopenTOCDetails%3A%21%21%28%29%29%2CgridData%3A%28h%3A14%2Ci%3A%21%2711%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A32%29%2Cid%3A%21%272c9c1f60-1909-11e9-919b-ffe5949a18d2%21%27%2CpanelIndex%3A%21%2711%21%27%2Ctype%3Amap%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aa71cf076-6895-491c-8878-63592e429ed5%2Cw%3A18%2Cx%3A0%2Cy%3A0%29%2Cid%3Ac00d1f90-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aa71cf076-6895-491c-8878-63592e429ed5%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aadc0a2f4-481c-45eb-b422-0ea59a3e5163%2Cw%3A30%2Cx%3A18%2Cy%3A0%29%2Cid%3Ac3378480-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aadc0a2f4-481c-45eb-b422-0ea59a3e5163%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%29%2CgridData%3A%28h%3A8%2Ci%3A%21%277077b79f-2a99-4fcb-bbd4-456982843278%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A7%29%2Cid%3Ac762b7a0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%277077b79f-2a99-4fcb-bbd4-456982843278%21%27%2Ctitle%3A%21%27%2525%2520of%2520target%2520revenue%2520%28%2410k%29%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A8%2Ci%3A%21%2719a3c101-ad2e-4421-a71b-a4734ec1f03e%21%27%2Cw%3A12%2Cx%3A24%2Cy%3A7%29%2Cid%3Ace02e260-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%2719a3c101-ad2e-4421-a71b-a4734ec1f03e%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A8%2Ci%3A%21%27491469e7-7d24-4216-aeb3-bca00e5c8c1b%21%27%2Cw%3A12%2Cx%3A36%2Cy%3A7%29%2Cid%3Ad5f90030-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%27491469e7-7d24-4216-aeb3-bca00e5c8c1b%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aa1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef%2Cw%3A24%2Cx%3A0%2Cy%3A15%29%2Cid%3Adde978b0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aa1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Ada51079b-952f-43dc-96e6-6f9415a3708b%2Cw%3A12%2Cx%3A24%2Cy%3A15%29%2Cid%3Ae3902840-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Ada51079b-952f-43dc-96e6-6f9415a3708b%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A10%2Ci%3A%21%2764fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b%21%27%2Cw%3A24%2Cx%3A24%2Cy%3A22%29%2Cid%3Aeddf7850-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%2764fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A14%2Ci%3Abd330ede-2eef-4e2a-8100-22a21abf5038%2Cw%3A24%2Cx%3A24%2Cy%3A32%29%2Cid%3Aff6a21b0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Abd330ede-2eef-4e2a-8100-22a21abf5038%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%29%2CgridData%3A%28h%3A9%2Ci%3Ab897d4be-cf83-46fb-a111-c7fbec9ef403%2Cw%3A24%2Cx%3A0%2Cy%3A46%29%2Cid%3A%21%2703071e90-f5eb-11eb-a78e-83aac3c38a60%21%27%2CpanelIndex%3Ab897d4be-cf83-46fb-a111-c7fbec9ef403%2Ctitle%3A%21%27Top%2520products%2520this%2520week%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%2CtimeRange%3A%28from%3Anow-2w%2Cto%3Anow-1w%29%29%2CgridData%3A%28h%3A9%2Ci%3Ae0f68f93-30f2-4da7-889a-6cd128a68d3f%2Cw%3A24%2Cx%3A24%2Cy%3A46%29%2Cid%3A%21%2706379e00-f5eb-11eb-a78e-83aac3c38a60%21%27%2CpanelIndex%3Ae0f68f93-30f2-4da7-889a-6cd128a68d3f%2Ctitle%3A%21%27Top%2520products%2520last%2520week%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%29%2Cquery%3A%28language%3Akuery%2Cquery%3A%21%27%21%27%29%2Ctags%3A%21%21%28%29%2CtimeRestore%3A%21%21t%2Ctitle%3A%21%27%255BeCommerce%255D%2520Revenue%2520Dashboard%21%27%2CviewMode%3Aview%29%27%29%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.0.0%27%29'; + '/api/reporting/generate/printablePdfV2?jobParams=%28browserTimezone%3AAmerica%2FNew_York%2Clayout%3A%28dimensions%3A%28height%3A2024%2Cwidth%3A1920%29%2Cid%3Apreserve_layout%29%2ClocatorParams%3A%21%28%28id%3ADASHBOARD_APP_LOCATOR%2Cparams%3A%28dashboardId%3A%27722b74f0-b882-11e8-a6d9-e546fe2bba5f%27%2CpreserveSavedFilters%3A%21t%2CtimeRange%3A%28from%3Anow-7d%2Cto%3Anow%29%2CuseHash%3A%21f%2CviewMode%3Aview%29%29%29%2CobjectType%3Adashboard%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.6.0-SNAPSHOT%27%29'; const body = { trigger: { diff --git a/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js index d793390488596..9b8f17c422272 100644 --- a/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js +++ b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js @@ -26,6 +26,7 @@ export default ({ getService, getPageObjects }) => { describe('PNG Reporting watch', () => { let id = 'watcher_png_report-'; id = id + new Date().getTime(); // For debugging. + // http://localhost:5601/api/reporting/generate/pngV2?jobParams=%28browserTimezone%3AAmerica%2FNew_York%2Clayout%3A%28dimensions%3A%28height%3A2024%2Cwidth%3A1920%29%2Cid%3Apreserve_layout%29%2ClocatorParams%3A%28id%3ADASHBOARD_APP_LOCATOR%2Cparams%3A%28dashboardId%3A%27722b74f0-b882-11e8-a6d9-e546fe2bba5f%27%2CpreserveSavedFilters%3A%21t%2CtimeRange%3A%28from%3Anow-7d%2Cto%3Anow%29%2CuseHash%3A%21f%2CviewMode%3Aview%29%29%2CobjectType%3Adashboard%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.6.0-SNAPSHOT%27%29 const watch = { id }; const reportingUrl = servers.kibana.protocol + @@ -33,7 +34,7 @@ export default ({ getService, getPageObjects }) => { KIBANAIP + ':' + servers.kibana.port + - '/api/reporting/generate/png?jobParams=%28browserTimezone%3AEurope%2FParis%2Clayout%3A%28dimensions%3A%28height%3A2052%2Cwidth%3A2542.666748046875%29%2Cid%3Apreserve_layout%2Cselectors%3A%28itemsCountAttribute%3Adata-shared-items-count%2CrenderComplete%3A%5Bdata-shared-item%5D%2Cscreenshot%3A%5Bdata-shared-items-container%5D%2CtimefilterDurationAttribute%3Adata-shared-timefilter-duration%29%29%2CobjectType%3Adashboard%2CrelativeUrl%3A%27%2Fapp%2Fdashboards%23%2Fview%2F722b74f0-b882-11e8-a6d9-e546fe2bba5f%3F_g%3D%28filters%3A%21%21%28%29%29%26_a%3D%28description%3A%21%27Analyze%2520mock%2520eCommerce%2520orders%2520and%2520revenue%21%27%2Cfilters%3A%21%21%28%29%2CfullScreenMode%3A%21%21f%2Coptions%3A%28hidePanelTitles%3A%21%21f%2CuseMargins%3A%21%21t%29%2Cpanels%3A%21%21%28%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A10%2Ci%3A%21%275%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A22%29%2Cid%3A%21%2745e07720-b890-11e8-a6d9-e546fe2bba5f%21%27%2CpanelIndex%3A%21%275%21%27%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3A%21%277%21%27%2Cw%3A12%2Cx%3A36%2Cy%3A15%29%2Cid%3Ab80e6540-b891-11e8-a6d9-e546fe2bba5f%2CpanelIndex%3A%21%277%21%27%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A18%2Ci%3A%21%2710%21%27%2Cw%3A48%2Cx%3A0%2Cy%3A55%29%2Cid%3A%21%273ba638e0-b894-11e8-a6d9-e546fe2bba5f%21%27%2CpanelIndex%3A%21%2710%21%27%2Ctype%3Asearch%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChiddenLayers%3A%21%21%28%29%2CisLayerTOCOpen%3A%21%21f%2CmapBuffer%3A%28maxLat%3A66.51326%2CmaxLon%3A90%2CminLat%3A0%2CminLon%3A-135%29%2CmapCenter%3A%28lat%3A45.88578%2Clon%3A-15.07605%2Czoom%3A2.11%29%2CopenTOCDetails%3A%21%21%28%29%29%2CgridData%3A%28h%3A14%2Ci%3A%21%2711%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A32%29%2Cid%3A%21%272c9c1f60-1909-11e9-919b-ffe5949a18d2%21%27%2CpanelIndex%3A%21%2711%21%27%2Ctype%3Amap%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aa71cf076-6895-491c-8878-63592e429ed5%2Cw%3A18%2Cx%3A0%2Cy%3A0%29%2Cid%3Ac00d1f90-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aa71cf076-6895-491c-8878-63592e429ed5%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aadc0a2f4-481c-45eb-b422-0ea59a3e5163%2Cw%3A30%2Cx%3A18%2Cy%3A0%29%2Cid%3Ac3378480-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aadc0a2f4-481c-45eb-b422-0ea59a3e5163%2Ctype%3Avisualization%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%29%2CgridData%3A%28h%3A8%2Ci%3A%21%277077b79f-2a99-4fcb-bbd4-456982843278%21%27%2Cw%3A24%2Cx%3A0%2Cy%3A7%29%2Cid%3Ac762b7a0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%277077b79f-2a99-4fcb-bbd4-456982843278%21%27%2Ctitle%3A%21%27%2525%2520of%2520target%2520revenue%2520%28%2410k%29%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A8%2Ci%3A%21%2719a3c101-ad2e-4421-a71b-a4734ec1f03e%21%27%2Cw%3A12%2Cx%3A24%2Cy%3A7%29%2Cid%3Ace02e260-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%2719a3c101-ad2e-4421-a71b-a4734ec1f03e%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A8%2Ci%3A%21%27491469e7-7d24-4216-aeb3-bca00e5c8c1b%21%27%2Cw%3A12%2Cx%3A36%2Cy%3A7%29%2Cid%3Ad5f90030-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%27491469e7-7d24-4216-aeb3-bca00e5c8c1b%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Aa1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef%2Cw%3A24%2Cx%3A0%2Cy%3A15%29%2Cid%3Adde978b0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Aa1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A7%2Ci%3Ada51079b-952f-43dc-96e6-6f9415a3708b%2Cw%3A12%2Cx%3A24%2Cy%3A15%29%2Cid%3Ae3902840-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Ada51079b-952f-43dc-96e6-6f9415a3708b%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A10%2Ci%3A%21%2764fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b%21%27%2Cw%3A24%2Cx%3A24%2Cy%3A22%29%2Cid%3Aeddf7850-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3A%21%2764fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%29%2CgridData%3A%28h%3A14%2Ci%3Abd330ede-2eef-4e2a-8100-22a21abf5038%2Cw%3A24%2Cx%3A24%2Cy%3A32%29%2Cid%3Aff6a21b0-f5ea-11eb-a78e-83aac3c38a60%2CpanelIndex%3Abd330ede-2eef-4e2a-8100-22a21abf5038%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%29%2CgridData%3A%28h%3A9%2Ci%3Ab897d4be-cf83-46fb-a111-c7fbec9ef403%2Cw%3A24%2Cx%3A0%2Cy%3A46%29%2Cid%3A%21%2703071e90-f5eb-11eb-a78e-83aac3c38a60%21%27%2CpanelIndex%3Ab897d4be-cf83-46fb-a111-c7fbec9ef403%2Ctitle%3A%21%27Top%2520products%2520this%2520week%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%2C%28embeddableConfig%3A%28enhancements%3A%28%29%2ChidePanelTitles%3A%21%21f%2CtimeRange%3A%28from%3Anow-2w%2Cto%3Anow-1w%29%29%2CgridData%3A%28h%3A9%2Ci%3Ae0f68f93-30f2-4da7-889a-6cd128a68d3f%2Cw%3A24%2Cx%3A24%2Cy%3A46%29%2Cid%3A%21%2706379e00-f5eb-11eb-a78e-83aac3c38a60%21%27%2CpanelIndex%3Ae0f68f93-30f2-4da7-889a-6cd128a68d3f%2Ctitle%3A%21%27Top%2520products%2520last%2520week%21%27%2Ctype%3Alens%2Cversion%3A%21%278.0.0%21%27%29%29%2Cquery%3A%28language%3Akuery%2Cquery%3A%21%27%21%27%29%2Ctags%3A%21%21%28%29%2CtimeRestore%3A%21%21t%2Ctitle%3A%21%27%255BeCommerce%255D%2520Revenue%2520Dashboard%21%27%2CviewMode%3Aview%29%27%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.0.0%27%29'; + '/api/reporting/generate/pngV2?jobParams=%28browserTimezone%3AAmerica%2FNew_York%2Clayout%3A%28dimensions%3A%28height%3A2024%2Cwidth%3A1920%29%2Cid%3Apreserve_layout%29%2ClocatorParams%3A%28id%3ADASHBOARD_APP_LOCATOR%2Cparams%3A%28dashboardId%3A%27722b74f0-b882-11e8-a6d9-e546fe2bba5f%27%2CpreserveSavedFilters%3A%21t%2CtimeRange%3A%28from%3Anow-7d%2Cto%3Anow%29%2CuseHash%3A%21f%2CviewMode%3Aview%29%29%2CobjectType%3Adashboard%2Ctitle%3A%27%5BeCommerce%5D%20Revenue%20Dashboard%27%2Cversion%3A%278.6.0-SNAPSHOT%27%29'; const emails = REPORTING_TEST_EMAILS.split(','); const interval = 10; const body = { diff --git a/yarn.lock b/yarn.lock index 60a8900ecc859..8c11e9acf7d98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51,10 +51,10 @@ resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== -"@babel/cli@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.10.tgz#4211adfc45ffa7d4f3cee6b60bb92e9fe68fe56a" - integrity sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw== +"@babel/cli@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.19.3.tgz#55914ed388e658e0b924b3a95da1296267e278e2" + integrity sha512-643/TybmaCAe101m2tSVHi9UKpETXP9c/Ff4mD2tAwkdP6esKIfaauZFc67vGEM6r9fekbEGid+sZhbEnSe3dg== dependencies: "@jridgewell/trace-mapping" "^0.3.8" commander "^4.0.1" @@ -81,10 +81,10 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" - integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151" + integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw== "@babel/core@7.12.9": version "7.12.9" @@ -108,21 +108,21 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.19.1", "@babel/core@^7.7.5": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" - integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== +"@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.19.3", "@babel/core@^7.7.5": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c" + integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" - "@babel/helper-compilation-targets" "^7.19.1" + "@babel/generator" "^7.19.3" + "@babel/helper-compilation-targets" "^7.19.3" "@babel/helper-module-transforms" "^7.19.0" "@babel/helpers" "^7.19.0" - "@babel/parser" "^7.19.1" + "@babel/parser" "^7.19.3" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.19.3" + "@babel/types" "^7.19.3" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -145,12 +145,12 @@ dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" - integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.3.tgz#d7f4d1300485b4547cb6f94b27d10d237b42bf59" + integrity sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ== dependencies: - "@babel/types" "^7.19.0" + "@babel/types" "^7.19.3" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -169,12 +169,12 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" - integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca" + integrity sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg== dependencies: - "@babel/compat-data" "^7.19.1" + "@babel/compat-data" "^7.19.3" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.21.3" semver "^6.3.0" @@ -345,10 +345,10 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== "@babel/helper-validator-option@^7.18.6": version "7.18.6" @@ -383,10 +383,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" - integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== +"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a" + integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -1046,13 +1046,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-env@^7.12.11", "@babel/preset-env@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.1.tgz#9f04c916f9c0205a48ebe5cc1be7768eb1983f67" - integrity sha512-c8B2c6D16Lp+Nt6HcD+nHl0VbPKVnNPTpszahuxJJnurfMtKeZ80A+qUv48Y7wqvS+dTFuLuaM9oYxyNHbCLWA== +"@babel/preset-env@^7.12.11", "@babel/preset-env@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.3.tgz#52cd19abaecb3f176a4ff9cc5e15b7bf06bec754" + integrity sha512-ziye1OTc9dGFOAXSWKUqQblYHNlBOaDl8wzqf2iKXJAltYiR3hKHUKmkt+S9PppW7RQpq4fFCrwwpIDj/f5P4w== dependencies: - "@babel/compat-data" "^7.19.1" - "@babel/helper-compilation-targets" "^7.19.1" + "@babel/compat-data" "^7.19.3" + "@babel/helper-compilation-targets" "^7.19.3" "@babel/helper-plugin-utils" "^7.19.0" "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" @@ -1120,7 +1120,7 @@ "@babel/plugin-transform-unicode-escapes" "^7.18.10" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.19.0" + "@babel/types" "^7.19.3" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -1202,29 +1202,29 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.4.5": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" - integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.3", "@babel/traverse@^7.4.5": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4" + integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" + "@babel/generator" "^7.19.3" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/parser" "^7.19.3" + "@babel/types" "^7.19.3" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" - integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== +"@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624" + integrity sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw== dependencies: "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@base2/pretty-print-object@1.0.1": @@ -3388,6 +3388,10 @@ version "0.0.0" uid "" +"@kbn/core-test-helpers-so-type-serializer@link:bazel-bin/packages/core/test-helpers/core-test-helpers-so-type-serializer": + version "0.0.0" + uid "" + "@kbn/core-theme-browser-internal@link:bazel-bin/packages/core/theme/core-theme-browser-internal": version "0.0.0" uid "" @@ -7548,6 +7552,10 @@ version "0.0.0" uid "" +"@types/kbn__core-test-helpers-so-type-serializer@link:bazel-bin/packages/core/test-helpers/core-test-helpers-so-type-serializer/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-theme-browser-internal@link:bazel-bin/packages/core/theme/core-theme-browser-internal/npm_module_types": version "0.0.0" uid "" @@ -10844,6 +10852,11 @@ bluebird@3.7.2, bluebird@^3.3.5, bluebird@^3.5.5, bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blurhash@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.1.tgz#7f134ad0cf3cbb6bcceb81ea51b82e1423009dca" + integrity sha512-qAJW99ZIEVJqLKvR6EUtMavaalYiFgfHNvwO6eiqHE7RTBZYGQLPJvzs4WlnqSQPxZgqSPH/n4kRJIHzb/Y7dg== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9: version "4.11.9" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" @@ -28945,23 +28958,7 @@ winston-transport@^4.5.0: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@^3.3.3: - version "3.8.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.1.tgz#76f15b3478cde170b780234e0c4cf805c5a7fb57" - integrity sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w== - dependencies: - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" - -winston@^3.8.2: +winston@^3.3.3, winston@^3.8.2: version "3.8.2" resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== @@ -29253,12 +29250,7 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== - -yargs-parser@^21.1.1: +yargs-parser@^21.0.0, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==