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..503ce2ef06c91 100755 --- a/.buildkite/scripts/common/setup_bazel.sh +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -17,6 +17,12 @@ if [[ "$BAZEL_CACHE_MODE" == "gcs" ]]; then echo "[bazel] enabling caching with GCS buckets" BAZEL_REGION="${BUILDKITE_AGENT_GCP_REGION:-us-central1}" + + if ! [[ "$BAZEL_REGION" =~ ^(us-central1|northamerica-northeast2|europe-west2|southamerica-east1|asia-south2)$ ]]; then + echo "unsupported bazel cache region $BAZEL_REGION" + exit 1 + fi + BAZEL_BUCKET="kibana-ci-bazel_$BAZEL_REGION" echo "[bazel] using GCS bucket: $BAZEL_BUCKET" 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/cloud.sh b/.buildkite/scripts/steps/artifacts/cloud.sh index 4d2317ce0b6c7..16f280f68c539 100644 --- a/.buildkite/scripts/steps/artifacts/cloud.sh +++ b/.buildkite/scripts/steps/artifacts/cloud.sh @@ -7,31 +7,26 @@ set -euo pipefail source "$(dirname "$0")/../../common/util.sh" source .buildkite/scripts/steps/artifacts/env.sh -echo "--- Build and publish Cloud image" +echo "--- Push docker image" mkdir -p target -download_artifact "kibana-$FULL_VERSION-linux-x86_64.tar.gz" ./target --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" +download_artifact "kibana-cloud-$FULL_VERSION-docker-image.tar.gz" ./target --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" +docker load < "target/kibana-cloud-$FULL_VERSION-docker-image.tar.gz" TAG="$FULL_VERSION-$GIT_COMMIT" +KIBANA_BASE_IMAGE="docker.elastic.co/kibana-ci/kibana-cloud:$FULL_VERSION" KIBANA_TEST_IMAGE="docker.elastic.co/kibana-ci/kibana-cloud:$TAG" +# docker.elastic.co/kibana-ci/kibana-cloud:$FULL_VERSION -> :$FULL_VERSION-$GIT_COMMIT +docker tag "$KIBANA_BASE_IMAGE" "$KIBANA_TEST_IMAGE" + echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co trap 'docker logout docker.elastic.co' EXIT if docker manifest inspect $KIBANA_TEST_IMAGE &> /dev/null; then - echo "Distribution already exists, skipping build" + echo "Cloud image already exists, skipping docker push" else - node scripts/build \ - --skip-initialize \ - --skip-generic-folders \ - --skip-platform-folders \ - --skip-archives \ - --docker-images \ - --docker-tag-qualifier="$GIT_COMMIT" \ - --docker-push \ - --skip-docker-ubi \ - --skip-docker-ubuntu \ - --skip-docker-contexts + docker image push "$KIBANA_TEST_IMAGE" fi docker logout docker.elastic.co 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/functional/apm_cypress.sh b/.buildkite/scripts/steps/functional/apm_cypress.sh index 77b26fafee920..04f9aee280429 100755 --- a/.buildkite/scripts/steps/functional/apm_cypress.sh +++ b/.buildkite/scripts/steps/functional/apm_cypress.sh @@ -4,6 +4,8 @@ set -euo pipefail source .buildkite/scripts/common/util.sh +APM_CYPRESS_RECORD_KEY="$(retry 5 5 vault read -field=CYPRESS_RECORD_KEY secret/kibana-issues/dev/apm-cypress-dashboard-record-key)" + .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh @@ -15,4 +17,6 @@ cd "$XPACK_DIR" checks-reporter-with-killswitch "APM Cypress Tests" \ node plugins/apm/scripts/test/e2e.js \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --record \ + --key "$APM_CYPRESS_RECORD_KEY" 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/.buildkite/scripts/steps/storybooks/build_and_upload.ts b/.buildkite/scripts/steps/storybooks/build_and_upload.ts index dcceca7848910..945f85a820971 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.ts +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.ts @@ -15,7 +15,7 @@ const STORYBOOKS = [ 'apm', 'canvas', 'ci_composite', - 'cloud', + 'cloud_chat', 'coloring', 'chart_icons', 'controls', diff --git a/.eslintrc.js b/.eslintrc.js index 902643dbe5066..0e43c15cca13e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1406,6 +1406,30 @@ module.exports = { ], }, }, + /** + * Allows snake_case variables in the server, because that's how we return API properties + */ + { + files: ['x-pack/plugins/enterprise_search/server/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'variable', + modifiers: ['destructured'], + format: null, + leadingUnderscore: 'allow', + trailingUnderscore: 'allow', + }, + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE'], + leadingUnderscore: 'allow', + trailingUnderscore: 'allow', + }, + ], + }, + }, { // Source files only - allow `any` in test/mock files files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'], diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b7d588bc89269..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 @@ -504,6 +508,11 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/security_solution/server/lib/detection_engine/routes/fleet @elastic/security-detections-response-rules /x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules @elastic/security-detections-response-rules +/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rule_exceptions_route* @elastic/security-solution-platform +/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route* @elastic/security-solution-platform +/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route* @elastic/security-solution-platform +/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route* @elastic/security-detections-response-alerts +/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils @elastic/security-solution-platform /x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags @elastic/security-detections-response-rules /x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring @elastic/security-detections-response-rules /x-pack/plugins/security_solution/server/lib/detection_engine/rules @elastic/security-detections-response-rules @@ -745,6 +754,9 @@ packages/core/http/core-http-context-server-internal @elastic/kibana-core packages/core/http/core-http-context-server-mocks @elastic/kibana-core packages/core/http/core-http-request-handler-context-server @elastic/kibana-core packages/core/http/core-http-request-handler-context-server-internal @elastic/kibana-core +packages/core/http/core-http-resources-server @elastic/kibana-core +packages/core/http/core-http-resources-server-internal @elastic/kibana-core +packages/core/http/core-http-resources-server-mocks @elastic/kibana-core packages/core/http/core-http-router-server-internal @elastic/kibana-core packages/core/http/core-http-router-server-mocks @elastic/kibana-core packages/core/http/core-http-server @elastic/kibana-core @@ -821,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/.github/workflows/backport.yml b/.github/workflows/backport.yml index c39e6292c7e4b..6e5cf72bf44a3 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -3,6 +3,9 @@ on: branches: ['main'] types: ['labeled', 'closed'] +env: + NODE_ENV: kibana-github-action + jobs: backport: name: Backport PR @@ -16,7 +19,7 @@ jobs: ) steps: - name: Backport Action - uses: sqren/backport-github-action@v8.9.2 + uses: sqren/backport-github-action@v8.9.7 with: github_token: ${{secrets.KIBANAMACHINE_TOKEN}} diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index cefd0ffd4d6b1..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, @@ -858,6 +849,41 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-server.urlAllowListValidator", + "type": "Function", + "tags": [], + "label": "urlAllowListValidator", + "description": [], + "signature": [ + "(urlKey: string) => (obj: T, validatorServices: ", + "ValidatorServices", + ") => void" + ], + "path": "x-pack/plugins/actions/server/sub_action_framework/helpers/validators.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-server.urlAllowListValidator.$1", + "type": "string", + "tags": [], + "label": "urlKey", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/actions/server/sub_action_framework/helpers/validators.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -1191,7 +1217,13 @@ "label": "validate", "description": [], "signature": [ - "{ params?: ValidatorType | undefined; config?: ValidatorType | undefined; secrets?: ValidatorType | undefined; connector?: ((config: Config, secrets: Secrets) => string | null) | undefined; } | undefined" + "{ params?: ", + "ValidatorType", + " | undefined; config?: ", + "ValidatorType", + " | undefined; secrets?: ", + "ValidatorType", + " | undefined; connector?: ((config: Config, secrets: Secrets) => string | null) | undefined; } | undefined" ], "path": "x-pack/plugins/actions/server/types.ts", "deprecated": false, @@ -1205,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 8c70634191ac9..2bab1ac914cb6 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-03 +date: 2022-10-10 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 | |-------------------|-----------|------------------------|-----------------| -| 214 | 0 | 209 | 19 | +| 213 | 0 | 208 | 23 | ## Client diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 5f708b7bd5b22..10139c992c0d1 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-03 +date: 2022-10-10 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 affc8c08a679c..f9ea9de800a28 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-03 +date: 2022-10-10 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 9971fcee0a58a..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", @@ -2782,7 +2782,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - " = never>({ id, includeLegacyId, }: { id: string; includeLegacyId?: boolean | undefined; }) => Promise<", + " = never>({ id, includeLegacyId, includeSnoozeData, }: { id: string; includeLegacyId?: boolean | undefined; includeSnoozeData?: boolean | undefined; }) => Promise<", { "pluginId": "alerting", "scope": "common", @@ -5085,7 +5085,7 @@ "label": "status", "description": [], "signature": [ - "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"ok\" | \"active\"" + "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"active\" | \"ok\"" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -6055,7 +6055,7 @@ "label": "RuleExecutionStatuses", "description": [], "signature": [ - "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"ok\" | \"active\"" + "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"active\" | \"ok\"" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index dba2b5b8a03fa..01684374e77bf 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index e3201e3df01c0..ddbf9d8c9e22b 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -2001,7 +2001,9 @@ "TypeC", "<{ dependencyName: ", "StringC", - "; }>]>; }>, ", + "; searchServiceDestinationMetrics: ", + "Type", + "; }>]>; }>, ", { "pluginId": "apm", "scope": "server", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index e327d3d9ef266..c30a0992fd7f4 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-03 +date: 2022-10-10 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 8465fcc62bbbe..66bd7c7e641e1 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.devdocs.json b/api_docs/bfetch.devdocs.json index f060159bc0f3a..32dbe24517f93 100644 --- a/api_docs/bfetch.devdocs.json +++ b/api_docs/bfetch.devdocs.json @@ -370,7 +370,9 @@ "IRouter", "<", "RequestHandlerContext", - "> | undefined) => void" + "> | undefined, options?: ", + "RouteConfigOptions", + "<\"get\" | \"post\" | \"put\" | \"delete\"> | undefined) => void" ], "path": "src/plugins/bfetch/server/plugin.ts", "deprecated": false, @@ -450,6 +452,22 @@ "deprecated": false, "trackAdoption": false, "isRequired": false + }, + { + "parentPluginId": "bfetch", + "id": "def-server.BfetchServerSetup.addStreamingResponseRoute.$5", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "RouteConfigOptions", + "<\"get\" | \"post\" | \"put\" | \"delete\"> | undefined" + ], + "path": "src/plugins/bfetch/server/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index b4d39167b8afc..e8065146fd3d3 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.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 | |-------------------|-----------|------------------------|-----------------| -| 80 | 1 | 71 | 2 | +| 81 | 1 | 72 | 2 | ## Client diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 964d7c080f50e..a98cfd8e0fd77 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-03 +date: 2022-10-10 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 1b56bb9f2bb5a..99dd1bbe89355 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-03 +date: 2022-10-10 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 5971c6140b9d0..0d6e5deb4ebb4 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.devdocs.json b/api_docs/cloud.devdocs.json index 97aac894f3a0e..89134783e726f 100644 --- a/api_docs/cloud.devdocs.json +++ b/api_docs/cloud.devdocs.json @@ -2,27 +2,7 @@ "id": "cloud", "client": { "classes": [], - "functions": [ - { - "parentPluginId": "cloud", - "id": "def-public.Chat", - "type": "Function", - "tags": [], - "label": "Chat", - "description": [ - "\nA lazily-loaded component that will display a trigger that will allow the user to chat with a\nhuman operator when the service is enabled; otherwise, it renders nothing." - ], - "signature": [ - "() => JSX.Element" - ], - "path": "x-pack/plugins/cloud/public/components/index.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - } - ], + "functions": [], "interfaces": [ { "parentPluginId": "cloud", @@ -132,22 +112,6 @@ "path": "x-pack/plugins/cloud/public/plugin.tsx", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "cloud", - "id": "def-public.CloudConfigType.chat", - "type": "Object", - "tags": [], - "label": "chat", - "description": [ - "Configuration to enable live chat in Cloud-enabled instances of Kibana." - ], - "signature": [ - "{ enabled: boolean; chatURL: string; }" - ], - "path": "x-pack/plugins/cloud/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -209,6 +173,83 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.isCloudEnabled", + "type": "boolean", + "tags": [], + "label": "isCloudEnabled", + "description": [ + "\n`true` when Kibana is running on Elastic Cloud." + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.cloudId", + "type": "string", + "tags": [], + "label": "cloudId", + "description": [ + "\nCloud ID. Undefined if not running on Cloud." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.deploymentUrl", + "type": "string", + "tags": [], + "label": "deploymentUrl", + "description": [ + "\nThe full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.profileUrl", + "type": "string", + "tags": [], + "label": "profileUrl", + "description": [ + "\nThe full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.organizationUrl", + "type": "string", + "tags": [], + "label": "organizationUrl", + "description": [ + "\nThe full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -336,6 +377,38 @@ "path": "x-pack/plugins/cloud/public/plugin.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudSetup.registerCloudService", + "type": "Function", + "tags": [], + "label": "registerCloudService", + "description": [], + "signature": [ + "(contextProvider: React.FC<{}>) => void" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "cloud", + "id": "def-public.CloudSetup.registerCloudService.$1", + "type": "Function", + "tags": [], + "label": "contextProvider", + "description": [], + "signature": [ + "React.FC<{}>" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "lifecycle": "setup", diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 07a722e9fc63c..5ed1afbaec4d1 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; @@ -21,16 +21,13 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 29 | 0 | 24 | 0 | +| 34 | 0 | 26 | 0 | ## Client ### Setup -### Functions - - ### Interfaces diff --git a/api_docs/cloud_chat.devdocs.json b/api_docs/cloud_chat.devdocs.json new file mode 100644 index 0000000000000..a5c0e8eb90ccd --- /dev/null +++ b/api_docs/cloud_chat.devdocs.json @@ -0,0 +1,47 @@ +{ + "id": "cloudChat", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "cloudChat", + "id": "def-public.Chat", + "type": "Function", + "tags": [], + "label": "Chat", + "description": [ + "\nA lazily-loaded component that will display a trigger that will allow the user to chat with a\nhuman operator when the service is enabled; otherwise, it renders nothing." + ], + "signature": [ + "() => JSX.Element" + ], + "path": "x-pack/plugins/cloud_integrations/cloud_chat/public/components/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx new file mode 100644 index 0000000000000..154ea7db58498 --- /dev/null +++ b/api_docs/cloud_chat.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibCloudChatPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] +--- +import cloudChatObj from './cloud_chat.devdocs.json'; + +Chat available on Elastic Cloud deployments for quicker assistance. + +Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 1 | 0 | 0 | 0 | + +## Client + +### Functions + + diff --git a/api_docs/cloud_experiments.devdocs.json b/api_docs/cloud_experiments.devdocs.json index 0f827e35ec52a..d08957de16642 100644 --- a/api_docs/cloud_experiments.devdocs.json +++ b/api_docs/cloud_experiments.devdocs.json @@ -94,119 +94,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "cloudExperiments", - "id": "def-common.CloudExperimentsPluginSetup", - "type": "Interface", - "tags": [], - "label": "CloudExperimentsPluginSetup", - "description": [ - "\nThe contract of the setup lifecycle method.\n" - ], - "path": "x-pack/plugins/cloud_integrations/cloud_experiments/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "cloudExperiments", - "id": "def-common.CloudExperimentsPluginSetup.identifyUser", - "type": "Function", - "tags": [ - "deprecated" - ], - "label": "identifyUser", - "description": [ - "\nIdentifies the user in the A/B testing service.\nFor now, we only rely on the user ID. In the future, we may request further details for more targeted experiments." - ], - "signature": [ - "(userId: string, userMetadata?: Record | undefined) => void" - ], - "path": "x-pack/plugins/cloud_integrations/cloud_experiments/common/types.ts", - "deprecated": true, - "trackAdoption": false, - "references": [ - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.tsx" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/plugin.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/plugin.test.ts" - }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/plugin.test.ts" - } - ], - "children": [ - { - "parentPluginId": "cloudExperiments", - "id": "def-common.CloudExperimentsPluginSetup.identifyUser.$1", - "type": "string", - "tags": [], - "label": "userId", - "description": [ - "The unique identifier of the user in the experiment." - ], - "signature": [ - "string" - ], - "path": "x-pack/plugins/cloud_integrations/cloud_experiments/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "cloudExperiments", - "id": "def-common.CloudExperimentsPluginSetup.identifyUser.$2", - "type": "Object", - "tags": [], - "label": "userMetadata", - "description": [ - "Additional attributes to the user. Take care to ensure these values do not contain PII." - ], - "signature": [ - "Record | undefined" - ], - "path": "x-pack/plugins/cloud_integrations/cloud_experiments/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "cloudExperiments", "id": "def-common.CloudExperimentsPluginStart", diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 8cb19bfe8beb9..3f36edd65c5e7 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/@elastic/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 16 | 0 | 0 | 0 | +| 12 | 0 | 0 | 0 | ## Common diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 6c31131981413..299993c715eb8 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-03 +date: 2022-10-10 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 69717a2855de0..61acddd4e8811 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-03 +date: 2022-10-10 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 d120725ebf6c3..fd7bbc2f1dadf 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-03 +date: 2022-10-10 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 67fa46292601f..197b485695674 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -907,8 +907,8 @@ "path": "x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.tsx" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.ts" }, { "plugin": "telemetry", @@ -979,44 +979,44 @@ "path": "packages/core/status/core-status-server-internal/src/status_service.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { "plugin": "@kbn/core-analytics-browser-mocks", @@ -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" @@ -15374,7 +15374,11 @@ "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", - "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; preference?: string | undefined; }" + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; hasNoReference?: ", + "SavedObjectsFindOptionsReference", + " | ", + "SavedObjectsFindOptionsReference", + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; preference?: string | undefined; }" ], "path": "node_modules/@types/kbn__core-saved-objects-api-browser/index.d.ts", "deprecated": false, @@ -19658,8 +19662,8 @@ "path": "x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.tsx" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.ts" }, { "plugin": "telemetry", @@ -19730,44 +19734,44 @@ "path": "packages/core/status/core-status-server-internal/src/status_service.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.test.ts" + "plugin": "security", + "path": "x-pack/plugins/security/public/analytics/register_user_context.test.ts" }, { "plugin": "@kbn/core-analytics-browser-mocks", @@ -21813,13 +21817,7 @@ "<", "RequestHandlerContext", "> & { resources: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResources", - "text": "HttpResources" - }, + "HttpResources", "; }" ], "path": "src/core/server/index.ts", @@ -25938,7 +25936,10 @@ "description": [ "\nHttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP.\nProvides API allowing plug-ins to respond with:\n- a pre-configured HTML page bootstrapping Kibana client app\n- custom HTML page\n- custom JS script file." ], - "path": "src/core/server/http_resources/types.ts", + "signature": [ + "HttpResources" + ], + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -25959,16 +25960,10 @@ ">(route: ", "RouteConfig", ", handler: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRequestHandler", - "text": "HttpResourcesRequestHandler" - }, + "HttpResourcesRequestHandler", ") => void" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -25983,7 +25978,7 @@ "RouteConfig", "" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -25996,16 +25991,10 @@ "label": "handler", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRequestHandler", - "text": "HttpResourcesRequestHandler" - }, + "HttpResourcesRequestHandler", "" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -26025,7 +26014,10 @@ "description": [ "\nAllows to configure HTTP response parameters" ], - "path": "src/core/server/http_resources/types.ts", + "signature": [ + "HttpResourcesRenderOptions" + ], + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26042,7 +26034,7 @@ "ResponseHeaders", " | undefined" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false } @@ -26058,7 +26050,10 @@ "description": [ "\nExtended set of {@link KibanaResponseFactory} helpers used to respond with HTML or JS resource." ], - "path": "src/core/server/http_resources/types.ts", + "signature": [ + "HttpResourcesServiceToolkit" + ], + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26073,18 +26068,12 @@ ], "signature": [ "(options?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRenderOptions", - "text": "HttpResourcesRenderOptions" - }, + "HttpResourcesRenderOptions", " | undefined) => Promise<", "IKibanaResponse", ">" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26096,16 +26085,10 @@ "label": "options", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRenderOptions", - "text": "HttpResourcesRenderOptions" - }, + "HttpResourcesRenderOptions", " | undefined" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": false @@ -26124,18 +26107,12 @@ ], "signature": [ "(options?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRenderOptions", - "text": "HttpResourcesRenderOptions" - }, + "HttpResourcesRenderOptions", " | undefined) => Promise<", "IKibanaResponse", ">" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26147,16 +26124,10 @@ "label": "options", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesRenderOptions", - "text": "HttpResourcesRenderOptions" - }, + "HttpResourcesRenderOptions", " | undefined" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": false @@ -26180,7 +26151,7 @@ "IKibanaResponse", "" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26194,7 +26165,7 @@ "signature": [ "HttpResponseOptions" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -26218,7 +26189,7 @@ "IKibanaResponse", "" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -26232,7 +26203,7 @@ "signature": [ "HttpResponseOptions" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -29385,36 +29356,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "core", - "id": "def-server.IRenderOptions", - "type": "Interface", - "tags": [], - "label": "IRenderOptions", - "description": [], - "path": "src/core/server/rendering/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.IRenderOptions.isAnonymousPage", - "type": "CompoundType", - "tags": [], - "label": "isAnonymousPage", - "description": [ - "\nSet whether the page is anonymous, which determines what plugins are enabled and whether to output user settings in the page metadata.\n`false` by default." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/core/server/rendering/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "core", "id": "def-server.IRouter", @@ -36904,6 +36845,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "core", + "id": "def-server.OpsMetrics.elasticsearch_client", + "type": "Object", + "tags": [], + "label": "elasticsearch_client", + "description": [ + "\nMetrics related to the elasticsearch client" + ], + "signature": [ + "ElasticsearchClientsMetrics" + ], + "path": "node_modules/@types/kbn__core-metrics-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "core", "id": "def-server.OpsMetrics.process", @@ -40612,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" @@ -44338,6 +44287,41 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsFindOptions.hasNoReference", + "type": "CompoundType", + "tags": [], + "label": "hasNoReference", + "description": [ + "\nSearch for documents *not* having a reference to the specified objects.\nUse `hasNoReferenceOperator` to specify the operator to use when searching for multiple references." + ], + "signature": [ + "SavedObjectsFindOptionsReference", + " | ", + "SavedObjectsFindOptionsReference", + "[] | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-api-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsFindOptions.hasNoReferenceOperator", + "type": "CompoundType", + "tags": [], + "label": "hasNoReferenceOperator", + "description": [ + "\nThe operator to use when searching by multiple references using the `hasNoReference` option. Defaults to `OR`" + ], + "signature": [ + "\"AND\" | \"OR\" | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-api-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "core", "id": "def-server.SavedObjectsFindOptions.defaultSearchOperator", @@ -51918,20 +51902,14 @@ "): ", "IKibanaResponse", "; } & ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.HttpResourcesServiceToolkit", - "text": "HttpResourcesServiceToolkit" - }, + "HttpResourcesServiceToolkit", ") => ", "IKibanaResponse", " | Promise<", "IKibanaResponse", ">" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "returnComment": [], @@ -52000,7 +51978,7 @@ "signature": [ "HttpResponseOptions" ], - "path": "src/core/server/http_resources/types.ts", + "path": "node_modules/@types/kbn__core-http-resources-server/index.d.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -53721,7 +53699,11 @@ "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", - "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; }" + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; hasNoReference?: ", + "SavedObjectsFindOptionsReference", + " | ", + "SavedObjectsFindOptionsReference", + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; }" ], "path": "node_modules/@types/kbn__core-saved-objects-api-server/index.d.ts", "deprecated": false, @@ -53974,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 2ce73b73214aa..eaf05e52d4d5e 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2688 | 0 | 30 | 0 | +| 2689 | 0 | 23 | 0 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index c9bd9c16e7214..2638867b78d5f 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-03 +date: 2022-10-10 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 7fa93934def07..19197f8324fed 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-03 +date: 2022-10-10 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 4f0b134f643ca..d313c049a619f 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-03 +date: 2022-10-10 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 3cda1cdf63de6..705e1202dd7a7 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-03 +date: 2022-10-10 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 09c0ef0d696bf..4f27bd7c26453 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-03 +date: 2022-10-10 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 3be2133617864..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, @@ -2802,7 +2907,11 @@ "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", - "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; pit?: ", + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; hasNoReference?: ", + "SavedObjectsFindOptionsReference", + " | ", + "SavedObjectsFindOptionsReference", + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; pit?: ", "SavedObjectsPitParams", " | undefined; }" ], @@ -2864,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", @@ -2884,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, @@ -2999,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 @@ -26585,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", @@ -27044,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", @@ -28211,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, @@ -28222,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, @@ -28278,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, @@ -28295,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, @@ -28306,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, @@ -28345,7 +28451,7 @@ }, { "parentPluginId": "data", - "id": "def-common.SearchSessionRequestInfo.error", + "id": "def-common.SearchSessionRequestStatus.error", "type": "string", "tags": [], "label": "error", @@ -28429,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", @@ -28455,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", @@ -28567,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", @@ -28636,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 @@ -29632,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", @@ -32811,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 c3b582462fd7d..6652a2e32c05e 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-03 +date: 2022-10-10 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 34855c7100cec..49564408adf27 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-03 +date: 2022-10-10 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 5daa20fdddfef..7985b9c42f2ae 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-03 +date: 2022-10-10 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 fa43c84f3166a..d298a7341c27b 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-03 +date: 2022-10-10 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 817619ccf49c9..6c3fe9e7afdc9 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-03 +date: 2022-10-10 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 6df0430d04c27..62f252081b2cf 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-03 +date: 2022-10-10 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 3c34e78a89e7f..37d2e5139c65b 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -24,15 +24,15 @@ 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 | - | | | advancedSettings, discover | - | | | advancedSettings, discover | - | | | securitySolution | - | -| | encryptedSavedObjects, actions, data, cloud, ml, logstash, securitySolution | - | +| | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | dashboard, dataVisualizer, stackAlerts, expressionPartitionVis | - | | | dataViews, maps | - | | | dataViews, maps | - | @@ -62,7 +62,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | canvas | - | | | canvas | - | | | canvas | - | -| | cloud | - | | | spaces, savedObjectsManagement | - | | | spaces, savedObjectsManagement | - | | | actions, ml, savedObjectsTagging, enterpriseSearch | - | @@ -82,7 +81,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | | | management, fleet, security, kibanaOverview, @kbn/core-application-browser-internal, @kbn/core-application-browser-mocks | 8.8.0 | -| | cloud, apm | 8.8.0 | +| | apm | 8.8.0 | | | security, licenseManagement, ml, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | | | security | 8.8.0 | | | mapsEms | 8.8.0 | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index abe2810379ec2..4c125f75cfa83 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -173,13 +173,11 @@ so TS and code-reference navigation might not highlight them. | -## cloud +## cloudChat | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.tsx#:~:text=environment) | 8.8.0 | -| | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.tsx#:~:text=identifyUser), [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/plugin.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/plugin.test.ts#:~:text=identifyUser), [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/plugin.test.ts#:~:text=identifyUser) | - | -| | [chat.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/server/routes/chat.ts#:~:text=authc) | - | +| | [chat.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_integrations/cloud_chat/server/routes/chat.ts#:~:text=authc) | - | @@ -656,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 882abd4cb6150..58604d901ae72 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -66,7 +66,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| -| cloud | | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud/public/plugin.tsx#:~:text=environment) | 8.8.0 | | kibanaOverview | | [application.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/kibana_overview/public/application.tsx#:~:text=appBasePath), [app_container.tsx](https://github.com/elastic/kibana/tree/main/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx#:~:text=appBasePath), [application_service.mock.ts](https://github.com/elastic/kibana/tree/main/packages/core/application/core-application-browser-mocks/src/application_service.mock.ts#:~:text=appBasePath) | 8.8.0 | | savedObjectsTaggingOss | | [api.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_objects_tagging_oss/public/api.ts#:~:text=SavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_objects_tagging_oss/public/decorator/types.ts#:~:text=SavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_objects_tagging_oss/public/decorator/types.ts#:~:text=SavedObject), [api.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_objects_tagging_oss/public/api.ts#:~:text=SavedObject), [api.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_objects_tagging_oss/public/api.ts#:~:text=SavedObject) | 8.8.0 | | @kbn/core-application-browser-internal | | [app_container.tsx](https://github.com/elastic/kibana/tree/main/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx#:~:text=onAppLeave), [application_service.mock.ts](https://github.com/elastic/kibana/tree/main/packages/core/application/core-application-browser-mocks/src/application_service.mock.ts#:~:text=onAppLeave) | 8.8.0 | @@ -168,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 76be86527aaed..1f599397cba74 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-03 +date: 2022-10-10 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 9c9793bd2bcfb..f0b145b742a48 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-03 +date: 2022-10-10 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 bc90ebb67c09b..33551d38d8eb7 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-03 +date: 2022-10-10 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 4c4b222a02a7c..ac8234957b13f 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-03 +date: 2022-10-10 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 5ab0874911417..00b3b6c9f3008 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-03 +date: 2022-10-10 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 5aa1eae40de53..f9dc0ebec4adb 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-03 +date: 2022-10-10 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 ba021086e294a..2aef259660758 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-03 +date: 2022-10-10 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 bf991156be210..4283b8368b2ea 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-03 +date: 2022-10-10 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 2c09bd5d65d46..ff64843fa7edc 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index 8361059c91079..b4c0370096cc2 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1312,7 +1312,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" + "(Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ active?: string | number | undefined; recovered?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false, @@ -1332,7 +1332,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ active?: string | number | undefined; recovered?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1347,7 +1347,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" + "Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ active?: string | number | undefined; recovered?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 482e1d361a157..01f3f85e83ae3 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-03 +date: 2022-10-10 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 2ab9af9772d04..d6cf5c3d25754 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-03 +date: 2022-10-10 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 908d90469c751..1fe3db16572f0 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-03 +date: 2022-10-10 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 aa2ac31f046ec..dd0f849bf2e00 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-03 +date: 2022-10-10 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 fd4412ad3f9e9..73ebc3735408f 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.devdocs.json b/api_docs/expression_legacy_metric_vis.devdocs.json index 8921746a2df8c..e1dc4542035a7 100644 --- a/api_docs/expression_legacy_metric_vis.devdocs.json +++ b/api_docs/expression_legacy_metric_vis.devdocs.json @@ -629,6 +629,17 @@ "path": "src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "expressionLegacyMetricVis", + "id": "def-common.MetricVisRenderConfig.canNavigateToLens", + "type": "boolean", + "tags": [], + "label": "canNavigateToLens", + "description": [], + "path": "src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 4f1bd9723f36a..3a6e7dee6c2bf 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 48 | 0 | 48 | 1 | +| 49 | 0 | 49 | 1 | ## Common diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index d17081eed3cbf..f2a60f4396a7d 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-03 +date: 2022-10-10 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 daa61ecf37e7b..536c4ed7764e9 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-03 +date: 2022-10-10 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 aaf66fd40fa4e..0321ed48ea421 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-03 +date: 2022-10-10 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 8e5467dc9cff3..43832994473b1 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-03 +date: 2022-10-10 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 a2a032c0c0bde..8735e4a7b91b9 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-03 +date: 2022-10-10 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 e650bf57eac7f..f10823d12ac9d 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-03 +date: 2022-10-10 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 cf00311b239a8..ea1cfeadc6141 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-03 +date: 2022-10-10 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 0eed4b0b713d0..5c3926722ab59 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-03 +date: 2022-10-10 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 353dfa02a7830..825f043405431 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-03 +date: 2022-10-10 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 8b7f40f4a359b..416f97990d566 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-03 +date: 2022-10-10 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 040d820604007..9ead59b5563f6 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-03 +date: 2022-10-10 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 768f6188371fc..bf3db3e921d0c 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 7c94fcdac9025..aaf7a55eb4c7f 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -147,7 +147,7 @@ "section": "def-public.FilesClient", "text": "FilesClient" }, - " extends GlobalEndpoints" + " extends GlobalEndpoints" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -171,7 +171,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>" + "; }>" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -249,7 +249,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>" + "; }>" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -292,7 +292,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "[]; }>" + "[]; }>" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -335,7 +335,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>" + "; }>" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -675,14 +675,15 @@ "\nCreate a files client." ], "signature": [ - "() => ", + "() => ", { "pluginId": "files", "scope": "public", "docId": "kibFilesPluginApi", "section": "def-public.FilesClient", "text": "FilesClient" - } + }, + "" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -700,14 +701,15 @@ "\nCreate a {@link ScopedFileClient} for a given {@link FileKind}.\n" ], "signature": [ - "(fileKind: string) => ", + "(fileKind: string) => ", { "pluginId": "files", "scope": "public", "docId": "kibFilesPluginApi", "section": "def-public.ScopedFilesClient", "text": "ScopedFilesClient" - } + }, + "" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -779,6 +781,61 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "files", + "id": "def-public.Props.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [ + "\nImage metadata" + ], + "signature": [ + { + "pluginId": "files", + "scope": "common", + "docId": "kibFilesPluginApi", + "section": "def-common.FileImageMetadata", + "text": "FileImageMetadata" + }, + " | undefined" + ], + "path": "x-pack/plugins/files/public/components/image/image.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "files", + "id": "def-public.Props.size", + "type": "CompoundType", + "tags": [ + "default" + ], + "label": "size", + "description": [], + "signature": [ + "\"m\" | \"s\" | \"l\" | \"xl\" | \"original\" | \"fullWidth\" | undefined" + ], + "path": "x-pack/plugins/files/public/components/image/image.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "files", + "id": "def-public.Props.wrapperProps", + "type": "Object", + "tags": [], + "label": "wrapperProps", + "description": [ + "\nProps to pass to the wrapper element" + ], + "signature": [ + "React.HTMLAttributes | undefined" + ], + "path": "x-pack/plugins/files/public/components/image/image.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "files", "id": "def-public.Props.onFirstVisible", @@ -855,7 +912,8 @@ "docId": "kibFilesPluginApi", "section": "def-public.FilesClient", "text": "FilesClient" - } + }, + "" ], "path": "x-pack/plugins/files/public/components/upload_file/upload_file.tsx", "deprecated": false, @@ -1019,7 +1077,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }; delete: { ok: true; }; getById: { file: ", + "; }; delete: { ok: true; }; getById: { file: ", { "pluginId": "files", "scope": "common", @@ -1027,7 +1085,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }; list: { files: ", + "; }; list: { files: ", { "pluginId": "files", "scope": "common", @@ -1035,7 +1093,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "[]; }; update: { file: ", + "[]; }; update: { file: ", { "pluginId": "files", "scope": "common", @@ -1043,7 +1101,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }; upload: { ok: true; size: number; }; download: any; getDownloadHref: string; share: ", + "; }; upload: { ok: true; size: number; }; download: any; getDownloadHref: string; share: ", { "pluginId": "files", "scope": "common", @@ -1108,7 +1166,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>; delete: (arg: Omit & { kind: string; }, \"kind\">) => Promise<{ ok: true; }>; getById: (arg: Omit & { kind: string; }, \"kind\">) => Promise<{ file: ", + "; }>; delete: (arg: Omit & { kind: string; }, \"kind\">) => Promise<{ ok: true; }>; getById: (arg: Omit & { kind: string; }, \"kind\">) => Promise<{ file: ", { "pluginId": "files", "scope": "common", @@ -1116,7 +1174,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>; list: (arg?: Omit & { kind: string; }, \"kind\"> | undefined) => Promise<{ files: ", + "; }>; list: (arg?: Omit & { kind: string; }, \"kind\"> | undefined) => Promise<{ files: ", { "pluginId": "files", "scope": "common", @@ -1124,7 +1182,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "[]; }>; update: (arg: Omit | undefined; alt?: string | undefined; } & {}> & Readonly<{} & { id: string; }> & { kind: string; }, \"kind\">) => Promise<{ file: ", + "[]; }>; update: (arg: Omit | undefined; alt?: string | undefined; } & {}> & Readonly<{} & { id: string; }> & { kind: string; }, \"kind\">) => Promise<{ file: ", { "pluginId": "files", "scope": "common", @@ -1132,7 +1190,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>; upload: (arg: Omit & Readonly<{ selfDestructOnAbort?: boolean | undefined; } & {}> & { body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; }, \"kind\">) => Promise<{ ok: true; size: number; }>; download: (arg: Omit & { kind: string; }, \"kind\">) => Promise; getDownloadHref: (arg: Omit; }>; upload: (arg: Omit & Readonly<{ selfDestructOnAbort?: boolean | undefined; } & {}> & { body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; }, \"kind\">) => Promise<{ ok: true; size: number; }>; download: (arg: Omit & { kind: string; }, \"kind\">) => Promise; getDownloadHref: (arg: Omit>) | (", - { - "pluginId": "fleet", - "scope": "public", - "docId": "kibFleetPluginApi", - "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", - "text": "PackagePolicyCreateMultiStepExtensionComponentProps" - }, - " & { children?: React.ReactNode; })> & { readonly _result: ", + "React.ExoticComponent<{ children?: React.ReactNode; } | React.RefAttributes>> & { readonly _result: ", { "pluginId": "fleet", "scope": "public", @@ -1329,42 +1305,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "fleet", - "id": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", - "type": "Interface", - "tags": [], - "label": "PackagePolicyCreateMultiStepExtensionComponentProps", - "description": [], - "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "fleet", - "id": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps.newPolicy", - "type": "Object", - "tags": [], - "label": "newPolicy", - "description": [ - "The integration policy being created" - ], - "signature": [ - { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.NewPackagePolicy", - "text": "NewPackagePolicy" - } - ], - "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "fleet", "id": "def-public.PackagePolicyEditExtension", @@ -2287,23 +2227,7 @@ "\nUI Component Extension is used on the pages displaying the ability to Create a multi step\nIntegration Policy" ], "signature": [ - "React.ComponentClass<", - { - "pluginId": "fleet", - "scope": "public", - "docId": "kibFleetPluginApi", - "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", - "text": "PackagePolicyCreateMultiStepExtensionComponentProps" - }, - ", any> | React.FunctionComponent<", - { - "pluginId": "fleet", - "scope": "public", - "docId": "kibFleetPluginApi", - "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", - "text": "PackagePolicyCreateMultiStepExtensionComponentProps" - }, - ">" + "React.ComponentClass<{}, any> | React.FunctionComponent<{}>" ], "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", "deprecated": false, @@ -8262,6 +8186,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "fleet", + "id": "def-common.Agent.outputs", + "type": "Object", + "tags": [], + "label": "outputs", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/agent.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "fleet", "id": "def-common.Agent.status", @@ -8390,7 +8328,7 @@ "label": "status", "description": [], "signature": [ - "\"active\" | \"inactive\"" + "\"inactive\" | \"active\"" ], "path": "x-pack/plugins/fleet/common/types/models/agent_policy.ts", "deprecated": false, @@ -14105,7 +14043,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 b63495e177924..82149c338ffde 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 997 | 3 | 893 | 17 | +| 996 | 3 | 893 | 17 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 97d12a56906f0..bd9ccebf846d7 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-03 +date: 2022-10-10 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 27b0b8446a7d8..5b5809dbbe1e8 100644 --- a/api_docs/guided_onboarding.devdocs.json +++ b/api_docs/guided_onboarding.devdocs.json @@ -6,44 +6,111 @@ "interfaces": [ { "parentPluginId": "guidedOnboarding", - "id": "def-public.GuidedOnboardingState", + "id": "def-public.GuideState", "type": "Interface", "tags": [], - "label": "GuidedOnboardingState", + "label": "GuideState", "description": [], - "path": "src/plugins/guided_onboarding/public/types.ts", + "path": "src/plugins/guided_onboarding/common/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "guidedOnboarding", - "id": "def-public.GuidedOnboardingState.activeGuide", + "id": "def-public.GuideState.guideId", "type": "CompoundType", "tags": [], - "label": "activeGuide", + "label": "guideId", "description": [], "signature": [ - { - "pluginId": "guidedOnboarding", - "scope": "public", - "docId": "kibGuidedOnboardingPluginApi", - "section": "def-public.UseCase", - "text": "UseCase" - }, - " | \"unset\"" + "\"search\" | \"security\" | \"observability\"" ], - "path": "src/plugins/guided_onboarding/public/types.ts", + "path": "src/plugins/guided_onboarding/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "guidedOnboarding", - "id": "def-public.GuidedOnboardingState.activeStep", - "type": "string", + "id": "def-public.GuideState.status", + "type": "CompoundType", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "\"complete\" | \"in_progress\" | \"ready_to_complete\"" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideState.isActive", + "type": "CompoundType", + "tags": [], + "label": "isActive", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideState.steps", + "type": "Array", + "tags": [], + "label": "steps", + "description": [], + "signature": [ + "GuideStep", + "[]" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideStep", + "type": "Interface", + "tags": [], + "label": "GuideStep", + "description": [], + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideStep.id", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "\"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"rules\" | \"alertsCases\" | \"browse_docs\" | \"search_experience\"" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideStep.status", + "type": "CompoundType", "tags": [], - "label": "activeStep", + "label": "status", "description": [], - "path": "src/plugins/guided_onboarding/public/types.ts", + "signature": [ + "\"complete\" | \"in_progress\" | \"inactive\" | \"active\"" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", "deprecated": false, "trackAdoption": false } @@ -55,21 +122,93 @@ "misc": [ { "parentPluginId": "guidedOnboarding", - "id": "def-public.UseCase", + "id": "def-public.GuideId", "type": "Type", "tags": [], - "label": "UseCase", + "label": "GuideId", "description": [], "signature": [ "\"search\" | \"security\" | \"observability\"" ], - "path": "src/plugins/guided_onboarding/public/types.ts", + "path": "src/plugins/guided_onboarding/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuideStepIds", + "type": "Type", + "tags": [], + "label": "GuideStepIds", + "description": [], + "signature": [ + "\"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"rules\" | \"alertsCases\" | \"browse_docs\" | \"search_experience\"" + ], + "path": "src/plugins/guided_onboarding/common/types.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false } ], - "objects": [], + "objects": [ + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.guidesConfig", + "type": "Object", + "tags": [], + "label": "guidesConfig", + "description": [], + "path": "src/plugins/guided_onboarding/public/constants/guides_config/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.guidesConfig.security", + "type": "Object", + "tags": [], + "label": "security", + "description": [], + "signature": [ + "GuideConfig" + ], + "path": "src/plugins/guided_onboarding/public/constants/guides_config/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.guidesConfig.observability", + "type": "Object", + "tags": [], + "label": "observability", + "description": [], + "signature": [ + "GuideConfig" + ], + "path": "src/plugins/guided_onboarding/public/constants/guides_config/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.guidesConfig.search", + "type": "Object", + "tags": [], + "label": "search", + "description": [], + "signature": [ + "GuideConfig" + ], + "path": "src/plugins/guided_onboarding/public/constants/guides_config/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "setup": { "parentPluginId": "guidedOnboarding", "id": "def-public.GuidedOnboardingPluginSetup", @@ -103,7 +242,7 @@ "label": "guidedOnboardingApi", "description": [], "signature": [ - "ApiService", + "GuidedOnboardingApi", " | undefined" ], "path": "src/plugins/guided_onboarding/public/types.ts", @@ -156,53 +295,7 @@ "functions": [], "interfaces": [], "enums": [], - "misc": [ - { - "parentPluginId": "guidedOnboarding", - "id": "def-common.API_BASE_PATH", - "type": "string", - "tags": [], - "label": "API_BASE_PATH", - "description": [], - "signature": [ - "\"/api/guided_onboarding\"" - ], - "path": "src/plugins/guided_onboarding/common/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "guidedOnboarding", - "id": "def-common.PLUGIN_ID", - "type": "string", - "tags": [], - "label": "PLUGIN_ID", - "description": [], - "signature": [ - "\"guidedOnboarding\"" - ], - "path": "src/plugins/guided_onboarding/common/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "guidedOnboarding", - "id": "def-common.PLUGIN_NAME", - "type": "string", - "tags": [], - "label": "PLUGIN_NAME", - "description": [], - "signature": [ - "\"guidedOnboarding\"" - ], - "path": "src/plugins/guided_onboarding/common/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], + "misc": [], "objects": [] } } \ No newline at end of file diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 899b3205a819c..1021f353f39d5 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Journey Onboarding](https://github.com/orgs/elastic/teams/platform-onbo | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 12 | 1 | +| 19 | 0 | 19 | 3 | ## Client @@ -31,6 +31,9 @@ Contact [Journey Onboarding](https://github.com/orgs/elastic/teams/platform-onbo ### Start +### Objects + + ### Interfaces @@ -45,8 +48,3 @@ Contact [Journey Onboarding](https://github.com/orgs/elastic/teams/platform-onbo ### Start -## Common - -### Consts, variables and types - - diff --git a/api_docs/home.devdocs.json b/api_docs/home.devdocs.json index c8e980ff0a0ab..a18ea6d544131 100644 --- a/api_docs/home.devdocs.json +++ b/api_docs/home.devdocs.json @@ -1313,10 +1313,6 @@ "removeBy": "8.8.0", "trackAdoption": false, "references": [ - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/public/plugin.tsx" - }, { "plugin": "apm", "path": "x-pack/plugins/apm/public/plugin.ts" diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 30beb41d12fae..a1269fecd710d 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-03 +date: 2022-10-10 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 e3ee1798496d0..d64fb3896ad9d 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-03 +date: 2022-10-10 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 34f9d8fcde852..c0db624b8d38d 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-03 +date: 2022-10-10 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 7ab9b9d8573c5..4e9dd1b904ede 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-03 +date: 2022-10-10 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 4cb29a4632cab..c81edba15ec6b 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-03 +date: 2022-10-10 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 6f257bb854320..208c8049e38b4 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-03 +date: 2022-10-10 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 0dcf9a1b797bc..083f69d0939a1 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-03 +date: 2022-10-10 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 60ab8f506c3a5..61d044d8d2f42 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-03 +date: 2022-10-10 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 c459ac8369da3..3cd0f613a9e3d 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-03 +date: 2022-10-10 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 aaf236aab90f0..e177b66a85837 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-03 +date: 2022-10-10 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 35df4e16f17cd..b7fc346931109 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-03 +date: 2022-10-10 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 0d18980e0b1c0..28a6f6bafc1af 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-03 +date: 2022-10-10 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 c4b9427b1a333..070f03039547f 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-03 +date: 2022-10-10 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 f06cb724cc729..8c7ceac1d78f9 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-03 +date: 2022-10-10 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 cd5176593f6ad..7a21c30fd3843 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-03 +date: 2022-10-10 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 d6317c64bf48c..c1926cbf98c9a 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-03 +date: 2022-10-10 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 e916c19f16197..c9629dc573b1d 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-03 +date: 2022-10-10 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 9ba0d5aeeacf4..44d2e38a95375 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-03 +date: 2022-10-10 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 ca208a21625bd..e96342c7ce840 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-03 +date: 2022-10-10 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 7a1ad1863e07b..3abeee268d46e 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-03 +date: 2022-10-10 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 6e102733b051b..45276f22d3586 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-03 +date: 2022-10-10 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 cfd53f8e091ff..cd266cfa11bcd 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-03 +date: 2022-10-10 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 c28b07464ba41..a975d89c203cc 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-03 +date: 2022-10-10 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 d5c55b31b68c0..254af0805e3cf 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-03 +date: 2022-10-10 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 c2de5353062a3..2639fcb9a105a 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-03 +date: 2022-10-10 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 c5026a994bbff..ae700c05f3647 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-03 +date: 2022-10-10 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 a81d5f720c20a..ffebb3323e821 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-03 +date: 2022-10-10 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 3ca7cf87f8819..177cc1d2c88f5 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-03 +date: 2022-10-10 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 f4fd135eead55..668d543ce208e 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-03 +date: 2022-10-10 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 2cb822b645378..8ec280432b055 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-03 +date: 2022-10-10 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 2cf8df7f6fb13..18c7af5ca7546 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-03 +date: 2022-10-10 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 217056d756e6c..6a584f10cc266 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-03 +date: 2022-10-10 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 8674be0b88a22..56ae80db62968 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-03 +date: 2022-10-10 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 a3e9193110cf9..ae1bff84123b5 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-03 +date: 2022-10-10 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 87edbd5da1573..206cc86999bb5 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-03 +date: 2022-10-10 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 eba99f7740982..10f93de93e697 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-03 +date: 2022-10-10 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 6cd2f00a3f32a..2220baea064f8 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-03 +date: 2022-10-10 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 0e0674185951d..2451c9adfa293 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-03 +date: 2022-10-10 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 f58bebcf2f5d0..6dffd3fe3eab0 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-03 +date: 2022-10-10 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 3d4d7035f6c1d..374a205d6d7eb 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-03 +date: 2022-10-10 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 7681b7a4a3885..d77d53ffa1912 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-03 +date: 2022-10-10 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 02a409c5881de..a0adf2433ba90 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-03 +date: 2022-10-10 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 b60d8471e15dd..ff353e7206588 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-03 +date: 2022-10-10 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 39463461d9165..b1482ac80cfc8 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-03 +date: 2022-10-10 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 2a64da20012e8..3052f18231f88 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-03 +date: 2022-10-10 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 b2056befb2acb..a374fa4b72bfd 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-03 +date: 2022-10-10 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 6d28c54984174..1452d1eca68de 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-03 +date: 2022-10-10 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 c7628a5a59c63..2fa1a2398f3c7 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-03 +date: 2022-10-10 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 e01e96164ef47..58247b909688c 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-03 +date: 2022-10-10 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 9ef6e61852ed2..0c2303c8d7b49 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-03 +date: 2022-10-10 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 57e8483cb0f74..acbec545278f5 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-03 +date: 2022-10-10 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 c4fabecffea39..6b49aeb6ad2db 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-03 +date: 2022-10-10 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 1d7858167c974..758606f2c966f 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-03 +date: 2022-10-10 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 3914aa766a64d..8ef722c82ebb5 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-03 +date: 2022-10-10 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 fca144e245a76..75bee5daa99f5 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-03 +date: 2022-10-10 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 5be603ffa1f96..ff88c65e59c67 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-03 +date: 2022-10-10 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 afde35a745d30..a64b529d30a99 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-03 +date: 2022-10-10 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 92ba606e6044c..cf83c7e9bf2fa 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-03 +date: 2022-10-10 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 9b3c1070f6623..7786d5b668cf5 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-03 +date: 2022-10-10 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 e871d2dc0a941..403da6112f339 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-03 +date: 2022-10-10 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 d591288b81315..1f6219d03955c 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-03 +date: 2022-10-10 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 7cd809b6b97e1..b28f3f07c9703 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-03 +date: 2022-10-10 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 273051738c41f..2aae17b574f69 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-03 +date: 2022-10-10 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 0cddffa5aeab6..d646839a1a1cc 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_core_elasticsearch_client_server_internal.devdocs.json index 0491f42594311..aa5dd46285c2f 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.devdocs.json @@ -21,10 +21,10 @@ "signature": [ "(config: ", "ElasticsearchClientConfig", - ", { logger, type, scoped, getExecutionContext, agentManager, kibanaVersion, }: { logger: ", + ", { logger, type, scoped, getExecutionContext, agentFactoryProvider, kibanaVersion, }: { logger: ", "Logger", - "; type: string; scoped?: boolean | undefined; getExecutionContext?: (() => string | undefined) | undefined; agentManager: ", - "AgentManager", + "; type: string; scoped?: boolean | undefined; getExecutionContext?: (() => string | undefined) | undefined; agentFactoryProvider: ", + "AgentFactoryProvider", "; kibanaVersion: string; }) => ", "default" ], @@ -52,7 +52,7 @@ "id": "def-server.configureClient.$2", "type": "Object", "tags": [], - "label": "{\n logger,\n type,\n scoped = false,\n getExecutionContext = noop,\n agentManager,\n kibanaVersion,\n }", + "label": "{\n logger,\n type,\n scoped = false,\n getExecutionContext = noop,\n agentFactoryProvider,\n kibanaVersion,\n }", "description": [], "path": "packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts", "deprecated": false, @@ -115,13 +115,13 @@ }, { "parentPluginId": "@kbn/core-elasticsearch-client-server-internal", - "id": "def-server.configureClient.$2.agentManager", + "id": "def-server.configureClient.$2.agentFactoryProvider", "type": "Object", "tags": [], - "label": "agentManager", + "label": "agentFactoryProvider", "description": [], "signature": [ - "AgentManager" + "AgentFactoryProvider" ], "path": "packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts", "deprecated": false, @@ -220,7 +220,40 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-elasticsearch-client-server-internal", + "id": "def-server.AgentStore", + "type": "Interface", + "tags": [], + "label": "AgentStore", + "description": [], + "path": "packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-elasticsearch-client-server-internal", + "id": "def-server.AgentStore.getAgents", + "type": "Function", + "tags": [], + "label": "getAgents", + "description": [], + "signature": [ + "() => Set<", + "NetworkAgent", + ">" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [], "objects": [] diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 88eb042d98206..bbc274c925599 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; @@ -21,10 +21,13 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 11 | 1 | +| 15 | 0 | 13 | 2 | ## Server ### Functions +### Interfaces + + diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.devdocs.json b/api_docs/kbn_core_elasticsearch_client_server_mocks.devdocs.json index c682403d3d63d..dc17ba9d371c0 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.devdocs.json @@ -10,7 +10,46 @@ }, "server": { "classes": [], - "functions": [], + "functions": [ + { + "parentPluginId": "@kbn/core-elasticsearch-client-server-mocks", + "id": "def-server.createAgentStoreMock", + "type": "Function", + "tags": [], + "label": "createAgentStoreMock", + "description": [], + "signature": [ + "(agents?: Set<", + "NetworkAgent", + ">) => ", + "AgentStore" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-client-server-mocks/src/agent_manager.mocks.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-elasticsearch-client-server-mocks", + "id": "def-server.createAgentStoreMock.$1", + "type": "Object", + "tags": [], + "label": "agents", + "description": [], + "signature": [ + "Set<", + "NetworkAgent", + ">" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-client-server-mocks/src/agent_manager.mocks.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [ { "parentPluginId": "@kbn/core-elasticsearch-client-server-mocks", diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index bbf4d1f0a58e8..f13371eb32d33 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; @@ -21,13 +21,16 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 36 | 1 | 32 | 0 | +| 38 | 1 | 34 | 0 | ## Server ### Objects +### Functions + + ### Interfaces diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index c9f8fdbd9b45d..9a166f9484d64 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-03 +date: 2022-10-10 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 f4032d04f97ca..b7f7ae66ec615 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-03 +date: 2022-10-10 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 835c21903b6bf..7651de084b9a9 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-03 +date: 2022-10-10 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 47f64412fe99e..61c1ac3f1ac5f 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-03 +date: 2022-10-10 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 082f5dd46eced..49ddcada894e4 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-03 +date: 2022-10-10 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 492f874f833eb..03a2cd43f9145 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-03 +date: 2022-10-10 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 f8426b783c45a..411798fcb340e 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-03 +date: 2022-10-10 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 6efeffd1c91c3..380ab162b9c61 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-03 +date: 2022-10-10 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 ed8e5ae438b3d..fb62ecaef4a4c 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-03 +date: 2022-10-10 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 6441c8e326395..45029383a8e06 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-03 +date: 2022-10-10 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 4f0d3211fa39d..c8de0fc49cd9e 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-03 +date: 2022-10-10 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 02fa99d7cb6ce..41b4cdf396fab 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-03 +date: 2022-10-10 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 40263469ee782..b0aebe4d000fb 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-03 +date: 2022-10-10 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 a6af55aa79cbe..b09cce2f10598 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-03 +date: 2022-10-10 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 a1c258a426f33..2d5738cf8ed25 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-03 +date: 2022-10-10 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 a937dccab7fb8..a5986b8db688b 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-03 +date: 2022-10-10 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 a6274dd34b37d..ec1bafeee45e2 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-03 +date: 2022-10-10 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 6293ae9b58af2..7b60c4634ffa5 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-03 +date: 2022-10-10 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 f11227d039fcf..acbda3533b883 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-03 +date: 2022-10-10 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 d0ea0e82f7682..533d1af83814c 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_core_http_resources_server.devdocs.json new file mode 100644 index 0000000000000..4a58d5e1ad9ef --- /dev/null +++ b/api_docs/kbn_core_http_resources_server.devdocs.json @@ -0,0 +1,457 @@ +{ + "id": "@kbn/core-http-resources-server", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResources", + "type": "Interface", + "tags": [], + "label": "HttpResources", + "description": [ + "\nHttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP.\nProvides API allowing plug-ins to respond with:\n- a pre-configured HTML page bootstrapping Kibana client app\n- custom HTML page\n- custom JS script file." + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResources.register", + "type": "Function", + "tags": [], + "label": "register", + "description": [ + "To register a route handler executing passed function to form response." + ], + "signature": [ + "(route: ", + "RouteConfig", + ", handler: ", + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRequestHandler", + "text": "HttpResourcesRequestHandler" + }, + ") => void" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResources.register.$1", + "type": "Object", + "tags": [], + "label": "route", + "description": [], + "signature": [ + "RouteConfig", + "" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResources.register.$2", + "type": "Function", + "tags": [], + "label": "handler", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRequestHandler", + "text": "HttpResourcesRequestHandler" + }, + "" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRenderOptions", + "type": "Interface", + "tags": [], + "label": "HttpResourcesRenderOptions", + "description": [ + "\nAllows to configure HTTP response parameters" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRenderOptions.headers", + "type": "CompoundType", + "tags": [], + "label": "headers", + "description": [ + "\nHTTP Headers with additional information about response." + ], + "signature": [ + "ResponseHeaders", + " | undefined" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit", + "type": "Interface", + "tags": [], + "label": "HttpResourcesServiceToolkit", + "description": [ + "\nExtended set of {@link KibanaResponseFactory} helpers used to respond with HTML or JS resource." + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderCoreApp", + "type": "Function", + "tags": [], + "label": "renderCoreApp", + "description": [ + "To respond with HTML page bootstrapping Kibana application." + ], + "signature": [ + "(options?: ", + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRenderOptions", + "text": "HttpResourcesRenderOptions" + }, + " | undefined) => Promise<", + "IKibanaResponse", + ">" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderCoreApp.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRenderOptions", + "text": "HttpResourcesRenderOptions" + }, + " | undefined" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderAnonymousCoreApp", + "type": "Function", + "tags": [], + "label": "renderAnonymousCoreApp", + "description": [ + "To respond with HTML page bootstrapping Kibana application without retrieving user-specific information." + ], + "signature": [ + "(options?: ", + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRenderOptions", + "text": "HttpResourcesRenderOptions" + }, + " | undefined) => Promise<", + "IKibanaResponse", + ">" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderAnonymousCoreApp.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesRenderOptions", + "text": "HttpResourcesRenderOptions" + }, + " | undefined" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderHtml", + "type": "Function", + "tags": [], + "label": "renderHtml", + "description": [ + "To respond with a custom HTML page." + ], + "signature": [ + "(options: ", + "HttpResponseOptions", + ") => ", + "IKibanaResponse", + "" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderHtml.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "HttpResponseOptions" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderJs", + "type": "Function", + "tags": [], + "label": "renderJs", + "description": [ + "To respond with a custom JS script file." + ], + "signature": [ + "(options: ", + "HttpResponseOptions", + ") => ", + "IKibanaResponse", + "" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesServiceToolkit.renderJs.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "HttpResponseOptions" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRequestHandler", + "type": "Type", + "tags": [], + "label": "HttpResourcesRequestHandler", + "description": [ + "\nExtended version of {@link RequestHandler} having access to {@link HttpResourcesServiceToolkit}\nto respond with HTML or JS resources." + ], + "signature": [ + "(context: Context, request: ", + "KibanaRequest", + ", response: ", + "KibanaSuccessResponseFactory", + " & ", + "KibanaRedirectionResponseFactory", + " & ", + "KibanaErrorResponseFactory", + " & { custom | Error | ", + "Stream", + " | Buffer | { message: string | Error; attributes?: ", + "ResponseErrorAttributes", + " | undefined; } | undefined>(options: ", + "CustomHttpResponseOptions", + "): ", + "IKibanaResponse", + "; } & ", + { + "pluginId": "@kbn/core-http-resources-server", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerPluginApi", + "section": "def-server.HttpResourcesServiceToolkit", + "text": "HttpResourcesServiceToolkit" + }, + ") => ", + "IKibanaResponse", + " | Promise<", + "IKibanaResponse", + ">" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRequestHandler.$1", + "type": "Uncategorized", + "tags": [], + "label": "context", + "description": [ + "{@link RequestHandlerContext } - the core context exposed for this request." + ], + "signature": [ + "Context" + ], + "path": "node_modules/@types/kbn__core-http-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRequestHandler.$2", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "{@link KibanaRequest } - object containing information about requested resource,\nsuch as path, method, headers, parameters, query, body, etc." + ], + "signature": [ + "KibanaRequest", + "" + ], + "path": "node_modules/@types/kbn__core-http-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesRequestHandler.$3", + "type": "Uncategorized", + "tags": [], + "label": "response", + "description": [ + "{@link KibanaResponseFactory } {@libk HttpResourcesServiceToolkit} - a set of helper functions used to respond to a request." + ], + "signature": [ + "ResponseFactory" + ], + "path": "node_modules/@types/kbn__core-http-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-resources-server", + "id": "def-server.HttpResourcesResponseOptions", + "type": "Type", + "tags": [], + "label": "HttpResourcesResponseOptions", + "description": [ + "\nHTTP Resources response parameters" + ], + "signature": [ + "HttpResponseOptions" + ], + "path": "packages/core/http/core-http-resources-server/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx new file mode 100644 index 0000000000000..d7c3c5d569dd4 --- /dev/null +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreHttpResourcesServerPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] +--- +import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 20 | 0 | 6 | 0 | + +## Server + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_http_resources_server_internal.devdocs.json b/api_docs/kbn_core_http_resources_server_internal.devdocs.json new file mode 100644 index 0000000000000..fae7163d58b82 --- /dev/null +++ b/api_docs/kbn_core_http_resources_server_internal.devdocs.json @@ -0,0 +1,200 @@ +{ + "id": "@kbn/core-http-resources-server-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService", + "type": "Class", + "tags": [], + "label": "HttpResourcesService", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-resources-server-internal", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerInternalPluginApi", + "section": "def-server.HttpResourcesService", + "text": "HttpResourcesService" + }, + " implements ", + "CoreService", + "<", + "InternalHttpResourcesPreboot", + ", void>" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + "CoreContext" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.preboot", + "type": "Function", + "tags": [], + "label": "preboot", + "description": [], + "signature": [ + "(deps: ", + "PrebootDeps", + ") => { createRegistrar: (router: ", + "IRouter", + "<", + "RequestHandlerContext", + ">) => ", + "HttpResources", + "; }" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.preboot.$1", + "type": "Object", + "tags": [], + "label": "deps", + "description": [], + "signature": [ + "PrebootDeps" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "(deps: ", + "SetupDeps", + ") => { createRegistrar: (router: ", + "IRouter", + "<", + "RequestHandlerContext", + ">) => ", + "HttpResources", + "; }" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.setup.$1", + "type": "Object", + "tags": [], + "label": "deps", + "description": [], + "signature": [ + "SetupDeps" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-internal", + "id": "def-server.HttpResourcesService.stop", + "type": "Function", + "tags": [], + "label": "stop", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx new file mode 100644 index 0000000000000..100517bf79a77 --- /dev/null +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreHttpResourcesServerInternalPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] +--- +import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 9 | 0 | 9 | 3 | + +## Server + +### Classes + + diff --git a/api_docs/kbn_core_http_resources_server_mocks.devdocs.json b/api_docs/kbn_core_http_resources_server_mocks.devdocs.json new file mode 100644 index 0000000000000..619ba47e29df4 --- /dev/null +++ b/api_docs/kbn_core_http_resources_server_mocks.devdocs.json @@ -0,0 +1,307 @@ +{ + "id": "@kbn/core-http-resources-server-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.HttpResourcesMock", + "type": "Type", + "tags": [], + "label": "HttpResourcesMock", + "description": [], + "signature": [ + "{ setup: jest.MockInstance<{ createRegistrar: (router: ", + "IRouter", + "<", + "RequestHandlerContext", + ">) => ", + "HttpResources", + "; }, [deps: ", + "SetupDeps", + "]>; start: jest.MockInstance; stop: jest.MockInstance; preboot: jest.MockInstance<{ createRegistrar: (router: ", + "IRouter", + "<", + "RequestHandlerContext", + ">) => ", + "HttpResources", + "; }, [deps: ", + "PrebootDeps", + "]>; } & ", + "PublicMethodsOf", + "<", + "HttpResourcesService", + ">" + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock", + "type": "Object", + "tags": [], + "label": "httpResourcesMock", + "description": [], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/core-http-resources-server-mocks", + "scope": "server", + "docId": "kibKbnCoreHttpResourcesServerMocksPluginApi", + "section": "def-server.HttpResourcesMock", + "text": "HttpResourcesMock" + } + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock.createRegistrar", + "type": "Function", + "tags": [], + "label": "createRegistrar", + "description": [], + "signature": [ + "() => jest.Mocked<", + "HttpResources", + ">" + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock.createPrebootContract", + "type": "Function", + "tags": [], + "label": "createPrebootContract", + "description": [], + "signature": [ + "() => { createRegistrar: jest.Mock, []>; }" + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock.createSetupContract", + "type": "Function", + "tags": [], + "label": "createSetupContract", + "description": [], + "signature": [ + "() => { createRegistrar: jest.Mock, []>; }" + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-http-resources-server-mocks", + "id": "def-server.httpResourcesMock.createResponseFactory", + "type": "Function", + "tags": [], + "label": "createResponseFactory", + "description": [], + "signature": [ + "() => { renderCoreApp: jest.MockInstance>, [options?: ", + "HttpResourcesRenderOptions", + " | undefined]> & ((options?: ", + "HttpResourcesRenderOptions", + " | undefined) => Promise<", + "IKibanaResponse", + ">); renderAnonymousCoreApp: jest.MockInstance>, [options?: ", + "HttpResourcesRenderOptions", + " | undefined]> & ((options?: ", + "HttpResourcesRenderOptions", + " | undefined) => Promise<", + "IKibanaResponse", + ">); renderHtml: jest.MockInstance<", + "IKibanaResponse", + ", [options: ", + "HttpResponseOptions", + "]> & ((options: ", + "HttpResponseOptions", + ") => ", + "IKibanaResponse", + "); renderJs: jest.MockInstance<", + "IKibanaResponse", + ", [options: ", + "HttpResponseOptions", + "]> & ((options: ", + "HttpResponseOptions", + ") => ", + "IKibanaResponse", + "); ok: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "HttpResponseOptions", + " | undefined]> & ((options?: ", + "HttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); accepted: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "HttpResponseOptions", + " | undefined]> & ((options?: ", + "HttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); noContent: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "HttpResponseOptions", + " | undefined]> & ((options?: ", + "HttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); redirected: jest.MockInstance<", + "IKibanaResponse", + ", [options: ", + "RedirectResponseOptions", + "]> & ((options: ", + "RedirectResponseOptions", + ") => ", + "IKibanaResponse", + "); badRequest: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "ErrorHttpResponseOptions", + " | undefined]> & ((options?: ", + "ErrorHttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); unauthorized: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "ErrorHttpResponseOptions", + " | undefined]> & ((options?: ", + "ErrorHttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); forbidden: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "ErrorHttpResponseOptions", + " | undefined]> & ((options?: ", + "ErrorHttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); notFound: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "ErrorHttpResponseOptions", + " | undefined]> & ((options?: ", + "ErrorHttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); conflict: jest.MockInstance<", + "IKibanaResponse", + ", [options?: ", + "ErrorHttpResponseOptions", + " | undefined]> & ((options?: ", + "ErrorHttpResponseOptions", + " | undefined) => ", + "IKibanaResponse", + "); customError: jest.MockInstance<", + "IKibanaResponse", + ", [options: ", + "CustomHttpResponseOptions", + "<", + "Stream", + " | Buffer | ", + "ResponseError", + ">]> & ((options: ", + "CustomHttpResponseOptions", + "<", + "Stream", + " | Buffer | ", + "ResponseError", + ">) => ", + "IKibanaResponse", + "); custom: jest.MockInstance<", + "IKibanaResponse", + ", [options: ", + "CustomHttpResponseOptions", + " | Error | ", + "Stream", + " | Buffer | { message: string | Error; attributes?: ", + "ResponseErrorAttributes", + " | undefined; } | undefined>]> & ( | Error | ", + "Stream", + " | Buffer | { message: string | Error; attributes?: ", + "ResponseErrorAttributes", + " | undefined; } | undefined>(options: ", + "CustomHttpResponseOptions", + ") => ", + "IKibanaResponse", + "); }" + ], + "path": "packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx new file mode 100644 index 0000000000000..a7ccc3b652e73 --- /dev/null +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreHttpResourcesServerMocksPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] +--- +import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 7 | 0 | 7 | 0 | + +## Server + +### Objects + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 7654b3f70cc87..19c25e8b0395b 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-03 +date: 2022-10-10 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 8a2d3a3de3333..414aad8ed5b84 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-03 +date: 2022-10-10 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 62b834449c23f..8b9c843e68fcd 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-03 +date: 2022-10-10 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 bbda88d5c238c..abe1586bedf14 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-03 +date: 2022-10-10 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 e99fba0127cc2..05a8f02808490 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-03 +date: 2022-10-10 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 c6da1dc82bc90..4db46422eeffe 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-03 +date: 2022-10-10 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 140e346128004..77771a9c4e01f 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-03 +date: 2022-10-10 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 ddd02e61d7ee4..102e9d27d27dc 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-03 +date: 2022-10-10 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 56636905f7afa..ce3c5558f9386 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-03 +date: 2022-10-10 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 f8ed8c6f0bec6..346b9f5a1a97e 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-03 +date: 2022-10-10 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 9a02125114dc7..dc28aa0d05f95 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-03 +date: 2022-10-10 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 d6a078aef0df0..f17c613a29524 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-03 +date: 2022-10-10 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 38c283eaaf97e..e9103d35d715e 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-03 +date: 2022-10-10 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 5cc58ad5a914c..86bc7cd276cb0 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-03 +date: 2022-10-10 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 1ab8822af2252..e8d18ffa85999 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-03 +date: 2022-10-10 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 99d9d3f32d3e8..2da058044c31d 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-03 +date: 2022-10-10 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 5065fff3211ff..0fd6658b66ccf 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-03 +date: 2022-10-10 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 2e6959cc87594..5ea9405ce0bdf 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-03 +date: 2022-10-10 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 81a53652b4ea2..b33d09367059e 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_core_metrics_collectors_server_internal.devdocs.json index 0de9634445ccd..8713e12e2a39f 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.devdocs.json +++ b/api_docs/kbn_core_metrics_collectors_server_internal.devdocs.json @@ -10,6 +10,100 @@ }, "server": { "classes": [ + { + "parentPluginId": "@kbn/core-metrics-collectors-server-internal", + "id": "def-server.ElasticsearchClientsMetricsCollector", + "type": "Class", + "tags": [], + "label": "ElasticsearchClientsMetricsCollector", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-metrics-collectors-server-internal", + "scope": "server", + "docId": "kibKbnCoreMetricsCollectorsServerInternalPluginApi", + "section": "def-server.ElasticsearchClientsMetricsCollector", + "text": "ElasticsearchClientsMetricsCollector" + }, + " implements ", + "MetricsCollector", + "<", + "ElasticsearchClientsMetrics", + ">" + ], + "path": "packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-metrics-collectors-server-internal", + "id": "def-server.ElasticsearchClientsMetricsCollector.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-metrics-collectors-server-internal", + "id": "def-server.ElasticsearchClientsMetricsCollector.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "agentStore", + "description": [], + "signature": [ + "AgentStore" + ], + "path": "packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-metrics-collectors-server-internal", + "id": "def-server.ElasticsearchClientsMetricsCollector.collect", + "type": "Function", + "tags": [], + "label": "collect", + "description": [], + "signature": [ + "() => Promise<", + "ElasticsearchClientsMetrics", + ">" + ], + "path": "packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-metrics-collectors-server-internal", + "id": "def-server.ElasticsearchClientsMetricsCollector.reset", + "type": "Function", + "tags": [], + "label": "reset", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-metrics-collectors-server-internal", "id": "def-server.EventLoopDelaysMonitor", diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 3d5b6fffdd513..b6588e68f0ad7 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 20 | 0 | +| 29 | 0 | 25 | 0 | ## Server diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 14756f136bd3a..9f08d7e3befda 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_core_metrics_server.devdocs.json index 77ae844e93992..6413560fea457 100644 --- a/api_docs/kbn_core_metrics_server.devdocs.json +++ b/api_docs/kbn_core_metrics_server.devdocs.json @@ -12,6 +12,168 @@ "classes": [], "functions": [], "interfaces": [ + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics", + "type": "Interface", + "tags": [], + "label": "ElasticsearchClientsMetrics", + "description": [ + "\nMetrics related to the elasticsearch clients" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.protocol", + "type": "CompoundType", + "tags": [], + "label": "protocol", + "description": [ + "The protocol (or protocols) that these Agents are using" + ], + "signature": [ + "\"http\" | \"none\" | \"mixed\" | \"https\"" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.connectedNodes", + "type": "number", + "tags": [], + "label": "connectedNodes", + "description": [ + "Number of ES nodes that ES-js client is connecting to" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.nodesWithActiveSockets", + "type": "number", + "tags": [], + "label": "nodesWithActiveSockets", + "description": [ + "Number of nodes with active connections" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.nodesWithIdleSockets", + "type": "number", + "tags": [], + "label": "nodesWithIdleSockets", + "description": [ + "Number of nodes with available connections (alive but idle).\nNote that a node can have both active and idle connections at the same time" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.totalActiveSockets", + "type": "number", + "tags": [], + "label": "totalActiveSockets", + "description": [ + "Total number of active sockets (all nodes, all connections)" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.totalIdleSockets", + "type": "number", + "tags": [], + "label": "totalIdleSockets", + "description": [ + "Total number of available sockets (alive but idle, all nodes, all connections)" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.totalQueuedRequests", + "type": "number", + "tags": [], + "label": "totalQueuedRequests", + "description": [ + "Total number of queued requests (all nodes, all connections)" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.mostActiveNodeSockets", + "type": "number", + "tags": [], + "label": "mostActiveNodeSockets", + "description": [ + "Number of active connections of the node with most active connections" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.averageActiveSocketsPerNode", + "type": "number", + "tags": [], + "label": "averageActiveSocketsPerNode", + "description": [ + "Average of active sockets per node (all connections)" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.mostIdleNodeSockets", + "type": "number", + "tags": [], + "label": "mostIdleNodeSockets", + "description": [ + "Number of idle connections of the node with most idle connections" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientsMetrics.averageIdleSocketsPerNode", + "type": "number", + "tags": [], + "label": "averageIdleSocketsPerNode", + "description": [ + "Average of available (idle) sockets per node (all connections)" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-metrics-server", "id": "def-server.IEventLoopDelaysMonitor", @@ -349,6 +511,28 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.OpsMetrics.elasticsearch_client", + "type": "Object", + "tags": [], + "label": "elasticsearch_client", + "description": [ + "\nMetrics related to the elasticsearch client" + ], + "signature": [ + { + "pluginId": "@kbn/core-metrics-server", + "scope": "server", + "docId": "kibKbnCoreMetricsServerPluginApi", + "section": "def-server.ElasticsearchClientsMetrics", + "text": "ElasticsearchClientsMetrics" + } + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-metrics-server", "id": "def-server.OpsMetrics.process", @@ -779,6 +963,23 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/core-metrics-server", + "id": "def-server.ElasticsearchClientProtocol", + "type": "Type", + "tags": [], + "label": "ElasticsearchClientProtocol", + "description": [ + "\nProtocol(s) used by the Elasticsearch Client" + ], + "signature": [ + "\"http\" | \"none\" | \"mixed\" | \"https\"" + ], + "path": "packages/core/metrics/core-metrics-server/src/metrics.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-metrics-server", "id": "def-server.MetricsServiceStart", diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 087dd2dd357ca..e8eb390e07292 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 48 | 0 | 8 | 0 | +| 62 | 0 | 8 | 0 | ## Server diff --git a/api_docs/kbn_core_metrics_server_internal.devdocs.json b/api_docs/kbn_core_metrics_server_internal.devdocs.json index dfdb1f7c95ef5..0920f42e9bf88 100644 --- a/api_docs/kbn_core_metrics_server_internal.devdocs.json +++ b/api_docs/kbn_core_metrics_server_internal.devdocs.json @@ -36,6 +36,20 @@ "path": "packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-internal", + "id": "def-server.MetricsServiceSetupDeps.elasticsearchService", + "type": "Object", + "tags": [], + "label": "elasticsearchService", + "description": [], + "signature": [ + "InternalElasticsearchServiceSetup" + ], + "path": "packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 5f5ea85f80056..16403bb05f5a0 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 5 | 0 | +| 6 | 0 | 6 | 0 | ## Server diff --git a/api_docs/kbn_core_metrics_server_mocks.devdocs.json b/api_docs/kbn_core_metrics_server_mocks.devdocs.json index 1ca231c151d2f..9e06f6bcaef02 100644 --- a/api_docs/kbn_core_metrics_server_mocks.devdocs.json +++ b/api_docs/kbn_core_metrics_server_mocks.devdocs.json @@ -133,6 +133,144 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics", + "type": "Object", + "tags": [], + "label": "sampleEsClientMetrics", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.protocol", + "type": "string", + "tags": [], + "label": "protocol", + "description": [], + "signature": [ + "\"https\"" + ], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.connectedNodes", + "type": "number", + "tags": [], + "label": "connectedNodes", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.nodesWithActiveSockets", + "type": "number", + "tags": [], + "label": "nodesWithActiveSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.nodesWithIdleSockets", + "type": "number", + "tags": [], + "label": "nodesWithIdleSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.totalActiveSockets", + "type": "number", + "tags": [], + "label": "totalActiveSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.totalIdleSockets", + "type": "number", + "tags": [], + "label": "totalIdleSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.totalQueuedRequests", + "type": "number", + "tags": [], + "label": "totalQueuedRequests", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.mostActiveNodeSockets", + "type": "number", + "tags": [], + "label": "mostActiveNodeSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.averageActiveSocketsPerNode", + "type": "number", + "tags": [], + "label": "averageActiveSocketsPerNode", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.mostIdleNodeSockets", + "type": "number", + "tags": [], + "label": "mostIdleNodeSockets", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-metrics-server-mocks", + "id": "def-server.sampleEsClientMetrics.averageIdleSocketsPerNode", + "type": "number", + "tags": [], + "label": "averageIdleSocketsPerNode", + "description": [], + "path": "packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ] }, diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 6216a6d589ce5..3da8129abb0ab 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; @@ -21,7 +21,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 7 | 0 | 7 | 0 | +| 19 | 0 | 19 | 0 | ## Server diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 82c2c06e08457..cee2a79f8fac6 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-03 +date: 2022-10-10 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 b4eeaf823c522..ae1b4654b8e87 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-03 +date: 2022-10-10 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 04c3664f49b67..83c7283e11de1 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-03 +date: 2022-10-10 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 edcf4af2b62ad..9ae964ddb4e34 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-03 +date: 2022-10-10 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 d120b6ccece74..8e7ad4d161b99 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-03 +date: 2022-10-10 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 e4152c45f1f60..474f6ab09bdac 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-03 +date: 2022-10-10 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 a571fb85c2ef4..e1bd6b5ef59e3 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-03 +date: 2022-10-10 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 8f4457512da88..d07f78ffb9d56 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-03 +date: 2022-10-10 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 6e25c69de32ee..656ab7187bbaa 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-03 +date: 2022-10-10 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 ce41f3aac1f43..dec4f69c482e2 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-03 +date: 2022-10-10 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 0b413f962b275..a2418521db9c7 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-03 +date: 2022-10-10 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 b31af30c16030..676f282449efe 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-03 +date: 2022-10-10 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 ec6b64842af42..9aad3e0b93103 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-03 +date: 2022-10-10 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 583b5bc973add..39c0978cc6d07 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-03 +date: 2022-10-10 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 383e9c6511efa..41988c7f87ccf 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_core_rendering_server_internal.devdocs.json new file mode 100644 index 0000000000000..d765832debee1 --- /dev/null +++ b/api_docs/kbn_core_rendering_server_internal.devdocs.json @@ -0,0 +1,61 @@ +{ + "id": "@kbn/core-rendering-server-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/core-rendering-server-internal", + "id": "def-server.Fonts", + "type": "Function", + "tags": [], + "label": "Fonts", + "description": [], + "signature": [ + "({ url }: React.PropsWithChildren) => JSX.Element" + ], + "path": "packages/core/rendering/core-rendering-server-internal/src/views/fonts.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-rendering-server-internal", + "id": "def-server.Fonts.$1", + "type": "CompoundType", + "tags": [], + "label": "{ url }", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "packages/core/rendering/core-rendering-server-internal/src/views/fonts.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx new file mode 100644 index 0000000000000..b666f07c63691 --- /dev/null +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreRenderingServerInternalPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] +--- +import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_core_rendering_server_mocks.devdocs.json b/api_docs/kbn_core_rendering_server_mocks.devdocs.json new file mode 100644 index 0000000000000..fc43829e9518e --- /dev/null +++ b/api_docs/kbn_core_rendering_server_mocks.devdocs.json @@ -0,0 +1,95 @@ +{ + "id": "@kbn/core-rendering-server-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/core-rendering-server-mocks", + "id": "def-server.renderingServiceMock", + "type": "Object", + "tags": [], + "label": "renderingServiceMock", + "description": [], + "path": "packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-rendering-server-mocks", + "id": "def-server.renderingServiceMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => ", + "RenderingServiceMock" + ], + "path": "packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-rendering-server-mocks", + "id": "def-server.renderingServiceMock.createPrebootContract", + "type": "Function", + "tags": [], + "label": "createPrebootContract", + "description": [], + "signature": [ + "() => jest.Mocked<", + "InternalRenderingServiceSetup", + ">" + ], + "path": "packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-rendering-server-mocks", + "id": "def-server.renderingServiceMock.createSetupContract", + "type": "Function", + "tags": [], + "label": "createSetupContract", + "description": [], + "signature": [ + "() => jest.Mocked<", + "InternalRenderingServiceSetup", + ">" + ], + "path": "packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx new file mode 100644 index 0000000000000..9d385203d5418 --- /dev/null +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreRenderingServerMocksPluginApi +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-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] +--- +import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 1 | + +## Server + +### Objects + + diff --git a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json index bc6024dea788e..19434ae87b701 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json @@ -1980,7 +1980,11 @@ "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", - "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; preference?: string | undefined; }" + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; hasNoReference?: ", + "SavedObjectsFindOptionsReference", + " | ", + "SavedObjectsFindOptionsReference", + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; preference?: string | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/apis/find.ts", "deprecated": false, diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index e07f5797c7f72..c373b57c1273b 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 1bff25681bdbf..cae43e5b22b0d 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -5207,6 +5207,53 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-server.SavedObjectsFindOptions.hasNoReference", + "type": "CompoundType", + "tags": [], + "label": "hasNoReference", + "description": [ + "\nSearch for documents *not* having a reference to the specified objects.\nUse `hasNoReferenceOperator` to specify the operator to use when searching for multiple references." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + " | ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-server.SavedObjectsFindOptions.hasNoReferenceOperator", + "type": "CompoundType", + "tags": [], + "label": "hasNoReferenceOperator", + "description": [ + "\nThe operator to use when searching by multiple references using the `hasNoReference` option. Defaults to `OR`" + ], + "signature": [ + "\"AND\" | \"OR\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-server.SavedObjectsFindOptions.defaultSearchOperator", @@ -6447,7 +6494,23 @@ "section": "def-server.SavedObjectsFindOptionsReference", "text": "SavedObjectsFindOptionsReference" }, - "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; }" + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; hasNoReference?: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + " | ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create_point_in_time_finder.ts", "deprecated": false, diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 8a3662b8b470c..cff107daa7111 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 308 | 1 | 137 | 0 | +| 310 | 1 | 137 | 0 | ## Server 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 7244cfe8beb60..4890a7483790b 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-03 +date: 2022-10-10 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 9a62ec00bb37c..e76ae1fd05bb1 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-03 +date: 2022-10-10 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 4068fcf5e73b5..2f47670abdfec 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-03 +date: 2022-10-10 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 7af80f7527944..7415fc8f24013 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-03 +date: 2022-10-10 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 83b6f08887286..e72c29db35935 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-03 +date: 2022-10-10 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 3d8e3d03be29b..16a8f9bb1f80c 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-03 +date: 2022-10-10 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 ad03d6488f2a0..58f98e15e9203 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-03 +date: 2022-10-10 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 b06d26ed9bea4..fcf15987f1274 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-03 +date: 2022-10-10 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 6532dfcaeaf81..326c1611c791a 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-03 +date: 2022-10-10 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 53092b90fc151..07756a85c7175 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-03 +date: 2022-10-10 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 3d1544f4f110f..10230a6232201 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-03 +date: 2022-10-10 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 c662be1f8fb09..536a73abac4a7 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-03 +date: 2022-10-10 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 8a27161ad18de..0b8e044f0b574 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-03 +date: 2022-10-10 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 c73c73d974180..74fbe52d0069c 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-03 +date: 2022-10-10 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 e8fe3856fa57f..d266a38b89a25 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-03 +date: 2022-10-10 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 5eb6ed5fabf23..ebd8bbf47dc93 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-03 +date: 2022-10-10 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 3f9ec5be08ad2..223043281e54a 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-03 +date: 2022-10-10 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 fd7956ee7ab43..639c80dd16184 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-03 +date: 2022-10-10 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 71ceb6a7bede4..4abe99291214a 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-03 +date: 2022-10-10 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 3916cb3c3119c..88d04e4eeea0b 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-03 +date: 2022-10-10 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 966876de9b0c2..5a3230d00fcbc 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-03 +date: 2022-10-10 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 f3ad0c01cc377..d42b15ff5b04b 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-03 +date: 2022-10-10 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 995497dffa505..60fd56639a01b 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.devdocs.json b/api_docs/kbn_core_test_helpers_so_type_serializer.devdocs.json new file mode 100644 index 0000000000000..10df0d4e5d2de --- /dev/null +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.devdocs.json @@ -0,0 +1,230 @@ +{ + "id": "@kbn/core-test-helpers-so-type-serializer", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.extractMigrationInfo", + "type": "Function", + "tags": [], + "label": "extractMigrationInfo", + "description": [ + "\nExtract all migration-relevant informations bound to given type in a serializable format.\n" + ], + "signature": [ + "(soType: ", + "SavedObjectsType", + ") => ", + { + "pluginId": "@kbn/core-test-helpers-so-type-serializer", + "scope": "server", + "docId": "kibKbnCoreTestHelpersSoTypeSerializerPluginApi", + "section": "def-server.SavedObjectTypeMigrationInfo", + "text": "SavedObjectTypeMigrationInfo" + } + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.extractMigrationInfo.$1", + "type": "Object", + "tags": [], + "label": "soType", + "description": [], + "signature": [ + "SavedObjectsType", + "" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.getMigrationHash", + "type": "Function", + "tags": [], + "label": "getMigrationHash", + "description": [], + "signature": [ + "(soType: ", + "SavedObjectsType", + ") => string" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.getMigrationHash.$1", + "type": "Object", + "tags": [], + "label": "soType", + "description": [], + "signature": [ + "SavedObjectsType", + "" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/get_migration_hash.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo", + "type": "Interface", + "tags": [], + "label": "SavedObjectTypeMigrationInfo", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.namespaceType", + "type": "CompoundType", + "tags": [], + "label": "namespaceType", + "description": [], + "signature": [ + "\"single\" | \"multiple\" | \"multiple-isolated\" | \"agnostic\"" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.convertToAliasScript", + "type": "string", + "tags": [], + "label": "convertToAliasScript", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.convertToMultiNamespaceTypeVersion", + "type": "string", + "tags": [], + "label": "convertToMultiNamespaceTypeVersion", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.migrationVersions", + "type": "Array", + "tags": [], + "label": "migrationVersions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.schemaVersions", + "type": "Array", + "tags": [], + "label": "schemaVersions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.mappings", + "type": "Object", + "tags": [], + "label": "mappings", + "description": [], + "signature": [ + "{ [x: string]: unknown; }" + ], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-so-type-serializer", + "id": "def-server.SavedObjectTypeMigrationInfo.hasExcludeOnUpgrade", + "type": "boolean", + "tags": [], + "label": "hasExcludeOnUpgrade", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-so-type-serializer/src/extract_migration_info.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx new file mode 100644 index 0000000000000..1d26649f21f59 --- /dev/null +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreTestHelpersSoTypeSerializerPluginApi +slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer +title: "@kbn/core-test-helpers-so-type-serializer" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin +date: 2022-10-10 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] +--- +import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 13 | 0 | 12 | 0 | + +## Server + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 2db8ecd68b0dc..1b42667d99bb0 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-03 +date: 2022-10-10 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 38778f82b4a73..e69c72b770b7a 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-03 +date: 2022-10-10 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 d561c846ba528..f3b5cbe8e0712 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-03 +date: 2022-10-10 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 f4b08646423d6..f7428dc798396 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-03 +date: 2022-10-10 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 607eaa47b682d..3489c2c081f45 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-03 +date: 2022-10-10 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 15ecdc9fee508..8f8093426e22e 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-03 +date: 2022-10-10 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 6644389a6513b..c91d9beb9d48f 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-03 +date: 2022-10-10 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 d486425f2067d..634a41ec1fe52 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-03 +date: 2022-10-10 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 98b9aa8e6584a..04d3804c3387a 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-03 +date: 2022-10-10 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 c287c7d3717f4..e1878987d59e7 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-03 +date: 2022-10-10 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 8d5b6922438e8..8bac77409ac02 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-03 +date: 2022-10-10 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 e433b18463057..3761c0054f0df 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-03 +date: 2022-10-10 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 ad3dac588eb63..c75090a9b6142 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-03 +date: 2022-10-10 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 0e41431342876..2b4f692045595 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-03 +date: 2022-10-10 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 7f67262cf91c9..7c8b276e834cc 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-03 +date: 2022-10-10 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 86cc145ec159a..e0fd07d2de51a 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-03 +date: 2022-10-10 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 e46730cf719c3..18d746039c14b 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-03 +date: 2022-10-10 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 54d0f8713d0fb..bcbe3e6f0708a 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-03 +date: 2022-10-10 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 b646fc86f43f5..582d465be5a63 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-03 +date: 2022-10-10 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 83091d9879dc4..743a82b68b2e2 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index d74d520ff8430..fd01dbf8e5503 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -300,7 +300,7 @@ "label": "enterpriseSearch", "description": [], "signature": [ - "{ readonly apiKeys: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsWorkplaceSearch: string; readonly contentExtraction: string; readonly crawlerGettingStarted: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly documentLevelSecurity: string; readonly ingestPipelines: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly mailService: string; readonly start: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" + "{ readonly apiKeys: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsWorkplaceSearch: string; readonly contentExtraction: string; readonly crawlerGettingStarted: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly ingestPipelines: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly mailService: string; readonly start: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -546,7 +546,7 @@ "label": "securitySolution", "description": [], "signature": [ - "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; }" + "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; readonly configureEndpointIntegrationPolicy: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -658,7 +658,7 @@ "label": "observability", "description": [], "signature": [ - "{ readonly guide: string; readonly infrastructureThreshold: string; readonly logsThreshold: string; readonly metricsThreshold: string; readonly monitorStatus: string; readonly monitorUptime: string; readonly tlsCertificate: string; readonly uptimeDurationAnomaly: string; readonly monitorLogs: string; readonly analyzeMetrics: string; readonly monitorUptimeSynthetics: string; readonly userExperience: string; readonly createAlerts: string; readonly syntheticsCommandReference: string; }" + "{ readonly guide: string; readonly infrastructureThreshold: string; readonly logsThreshold: string; readonly metricsThreshold: string; readonly monitorStatus: string; readonly monitorUptime: string; readonly tlsCertificate: string; readonly uptimeDurationAnomaly: string; readonly monitorLogs: string; readonly analyzeMetrics: string; readonly monitorUptimeSynthetics: string; readonly userExperience: string; readonly createAlerts: string; readonly syntheticsCommandReference: string; readonly syntheticsProjectMonitors: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 8913a1a03ab85..5989572689e52 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-03 +date: 2022-10-10 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 a5d4079c284b0..c95fc1e111f64 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-03 +date: 2022-10-10 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 8370c22a01f19..177ceb72f72dc 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-03 +date: 2022-10-10 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 4ad77bfff6559..5dca8c1133830 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-03 +date: 2022-10-10 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 bf9c27ac92b27..53ef00e7813c8 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-03 +date: 2022-10-10 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 632303fa82c97..903a72f04b9bb 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-03 +date: 2022-10-10 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 6a6ac63f69acd..de03102edaaec 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-03 +date: 2022-10-10 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 e0525000b5f9f..c328060468254 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-03 +date: 2022-10-10 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 f9ad3846f19fa..3b1d69004f2d9 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-03 +date: 2022-10-10 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 9bc0b92168240..99c56f93be934 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-03 +date: 2022-10-10 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 359334478d03d..1374a4aec1d96 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-03 +date: 2022-10-10 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 151e0da4dd3b1..51789356f12dd 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-03 +date: 2022-10-10 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 161354bbbcb14..aef3268c9bfba 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-03 +date: 2022-10-10 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 1e054c0706c6f..66aba654ff775 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-03 +date: 2022-10-10 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 0852dda61f29a..4b7223d26561f 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-03 +date: 2022-10-10 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 d4cca06818606..240a64c2264ac 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-03 +date: 2022-10-10 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 54d4d15fc741e..cccab042144a3 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-03 +date: 2022-10-10 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 c7ff5b8ab5056..c9db829a85be3 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-03 +date: 2022-10-10 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 262e79df335d1..fd6582ff8a7c5 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-03 +date: 2022-10-10 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 df20b5d894c5e..c6b3c5a349b3b 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-03 +date: 2022-10-10 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 4994d60766a45..929f3442b07fd 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-03 +date: 2022-10-10 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 e5e069f91c957..ea265ce6a91d3 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-03 +date: 2022-10-10 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 21477f1c435c7..620cc2a26b48c 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-03 +date: 2022-10-10 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 6d9b4a9bb317c..a407fb3d00675 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-03 +date: 2022-10-10 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 712dd79f55359..f8f1c0cbbf18e 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-03 +date: 2022-10-10 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 6dcc57446aaad..d9ca0522727a6 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-03 +date: 2022-10-10 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 788e96c22f5c3..598854d6f14f6 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-03 +date: 2022-10-10 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 8e2ce8b3d9fdc..7dd21a446f77f 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-03 +date: 2022-10-10 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 25b2b17e3997b..02b13bbb589b9 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-03 +date: 2022-10-10 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 5f24dc1806b03..f7394b06d8d9e 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-03 +date: 2022-10-10 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 2fa9ac8d84b7a..0687bfe1ceb56 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-03 +date: 2022-10-10 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 c5b48fbeff006..7eeb0479f984e 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-03 +date: 2022-10-10 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 2a5d7bc10afb8..8d45d987a956e 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-03 +date: 2022-10-10 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 3f87a2b8b79d8..10368217e8d44 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-03 +date: 2022-10-10 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 76d9089526cec..dc1bc2010d831 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-03 +date: 2022-10-10 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 3e83e64f435dc..3fa082077bf04 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-03 +date: 2022-10-10 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 dc887c9ce3056..fd1c4a622607c 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-03 +date: 2022-10-10 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 30cf3560edd3a..91f1073961202 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-03 +date: 2022-10-10 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 ae485f79249a3..a2dc7b6bb5fb0 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-03 +date: 2022-10-10 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 8249b1eae5a39..b4a5ffe67fbb5 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_rule_data_utils.devdocs.json index 2d6403bd41dfe..513060301f142 100644 --- a/api_docs/kbn_rule_data_utils.devdocs.json +++ b/api_docs/kbn_rule_data_utils.devdocs.json @@ -844,6 +844,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/rule-data-utils", + "id": "def-common.ALERT_TIME_RANGE", + "type": "string", + "tags": [], + "label": "ALERT_TIME_RANGE", + "description": [], + "signature": [ + "\"kibana.alert.time_range\"" + ], + "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/rule-data-utils", "id": "def-common.ALERT_UUID", @@ -942,7 +957,7 @@ "label": "AlertStatus", "description": [], "signature": [ - "\"recovered\" | \"active\"" + "\"active\" | \"recovered\"" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_status.ts", "deprecated": false, @@ -1077,7 +1092,7 @@ "label": "TechnicalRuleDataFieldName", "description": [], "signature": [ - "\"tags\" | \"kibana\" | \"@timestamp\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"event.action\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.instance.id\" | \"kibana.alert.start\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.severity\" | \"kibana.alert.status\" | \"kibana.version\" | \"ecs.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.workflow_status\" | \"kibana.alert.workflow_user\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.system_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"event.kind\" | \"event.module\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert\" | \"kibana.alert.rule\"" + "\"tags\" | \"kibana\" | \"@timestamp\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"event.action\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.instance.id\" | \"kibana.alert.start\" | \"kibana.alert.time_range\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.severity\" | \"kibana.alert.status\" | \"kibana.version\" | \"ecs.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.workflow_status\" | \"kibana.alert.workflow_user\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.system_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"event.kind\" | \"event.module\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert\" | \"kibana.alert.rule\"" ], "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", "deprecated": false, diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index a4ef149f8033b..8842f16f95521 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_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 | |-------------------|-----------|------------------------|-----------------| -| 74 | 0 | 71 | 0 | +| 75 | 0 | 72 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index ea7380185e08a..c1162de9f27a8 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-03 +date: 2022-10-10 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 4f2b01b578636..55450f4d392ab 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-03 +date: 2022-10-10 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 35651464e503c..75ed0a758d7b4 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-03 +date: 2022-10-10 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 74a60ea3a6e0d..99d48e0e7c4c4 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-03 +date: 2022-10-10 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 a2e5cb79e7ec0..47e94eafc034e 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-03 +date: 2022-10-10 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 878d21884f5ab..0efde1f2fc5e5 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-03 +date: 2022-10-10 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 7009b4610d376..879dab8d19863 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-03 +date: 2022-10-10 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 1a8635741da38..d5f0dd0f088f4 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-03 +date: 2022-10-10 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 ba0f3639d8e00..eb7870fb27812 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-03 +date: 2022-10-10 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 cc6cd2e0e30ab..a72acb71f8343 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-03 +date: 2022-10-10 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 c160feacf522f..818f3cedcf431 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-03 +date: 2022-10-10 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 1a0fb17e5ced7..d3b238f064f04 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-03 +date: 2022-10-10 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 7aa3b1237d1bd..9819937553c2c 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-03 +date: 2022-10-10 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 7b831d8213377..3fe4182737cb1 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-03 +date: 2022-10-10 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 e82e1785f2490..cffd24280bd80 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-03 +date: 2022-10-10 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 60f6a7802671b..63492dc74b4a0 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-03 +date: 2022-10-10 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 d5b974e9060a3..f129163c988e9 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-03 +date: 2022-10-10 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 434f6ebf7360f..44d456487ad02 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-03 +date: 2022-10-10 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 aca5844ce1aa5..01725450e1a3f 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-03 +date: 2022-10-10 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 64ff483b257b8..1ca4cca275861 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-03 +date: 2022-10-10 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 98810e33e0a49..54896916668a9 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-03 +date: 2022-10-10 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 f7ca5b1619118..0c858f6846b3c 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-03 +date: 2022-10-10 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 bae9391ff07c9..78c646f343f8e 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-03 +date: 2022-10-10 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 aeabec4b1421b..fe98104746134 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-03 +date: 2022-10-10 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 7dc8d4c973138..2d756c72ea7a1 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-03 +date: 2022-10-10 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 5386d337930ef..a8ac95588218f 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-03 +date: 2022-10-10 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 77248704888b4..7926794780d49 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-03 +date: 2022-10-10 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 100b84a823c93..5c8075601d9a8 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-03 +date: 2022-10-10 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 fc9392a85f8ee..df0736b573f17 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-03 +date: 2022-10-10 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 75c7b818d2b99..39ee413fc7676 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-03 +date: 2022-10-10 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 46c1575ac211e..edab8ba9cdeda 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-03 +date: 2022-10-10 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 ab3b48e848639..3238e35f522ab 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-03 +date: 2022-10-10 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 be35872625c46..d91e3c960d34c 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-03 +date: 2022-10-10 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 b5afba9ff421d..c908ad8566edb 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-03 +date: 2022-10-10 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 da1478041d724..19fa427b65ad9 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-03 +date: 2022-10-10 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 1b1327a86bd0a..039d9ad79e772 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-03 +date: 2022-10-10 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 0e7e8889a58c6..691755b31bb3a 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-03 +date: 2022-10-10 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 f9c07c50257c8..838f60da629eb 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-03 +date: 2022-10-10 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 a0aca2fcd5aaf..753b087e6f375 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-03 +date: 2022-10-10 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 aeae1c356a861..c2935ffb7d512 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-03 +date: 2022-10-10 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 d5712323de82d..0d2b31cecba0e 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-03 +date: 2022-10-10 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 3466072027056..e73b81401efa6 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-03 +date: 2022-10-10 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 d6c26edac107d..d5317c0029e9c 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-03 +date: 2022-10-10 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 13db8c8155fd0..c93ed16d6f152 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-03 +date: 2022-10-10 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 e96bc3e61652b..07dcb1a3659d0 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-03 +date: 2022-10-10 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 b4aac204fc2fc..2e32807ed0a2e 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-03 +date: 2022-10-10 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 13b0046b3c5f5..04a276c4eb964 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-03 +date: 2022-10-10 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 5dc57b83e3caf..bfa6f898f4309 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-03 +date: 2022-10-10 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 924a8f1d2d36d..bb0dc9dcf8166 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-03 +date: 2022-10-10 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 cc4a486b6dc8e..a1e8dbf164f44 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-03 +date: 2022-10-10 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 38fd9ee192959..393f75434d833 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-03 +date: 2022-10-10 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 efc952ecde5a0..3e90ec8fb19e9 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-03 +date: 2022-10-10 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 1c63f3fb99305..1b1c168133c53 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-03 +date: 2022-10-10 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 346fc07253bf5..8d1abc35c23dc 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-03 +date: 2022-10-10 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 cc16ed53a0ad1..d108fa2cf7a0a 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-03 +date: 2022-10-10 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 768d7656e3a83..c0ec429464c4e 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-03 +date: 2022-10-10 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.devdocs.json b/api_docs/kbn_user_profile_components.devdocs.json index d8cd0cfd94f9e..55ddd03b7d6d4 100644 --- a/api_docs/kbn_user_profile_components.devdocs.json +++ b/api_docs/kbn_user_profile_components.devdocs.json @@ -121,6 +121,57 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatarTip", + "type": "Function", + "tags": [], + "label": "UserAvatarTip", + "description": [ + "\nRenders a user avatar with tooltip" + ], + "signature": [ + "({ user, avatar, ...rest }: React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserAvatarProps", + "text": "UserAvatarProps" + }, + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/user_avatar_tip.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatarTip.$1", + "type": "CompoundType", + "tags": [], + "label": "{ user, avatar, ...rest }", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserAvatarProps", + "text": "UserAvatarProps" + }, + ">" + ], + "path": "packages/kbn-user-profile-components/src/user_avatar_tip.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/user-profile-components", "id": "def-common.UserProfilesPopover", @@ -236,6 +287,57 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserToolTip", + "type": "Function", + "tags": [], + "label": "UserToolTip", + "description": [ + "\nRenders a tooltip with user information" + ], + "signature": [ + "({ user, avatar, ...rest }: React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserToolTipProps", + "text": "UserToolTipProps" + }, + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/user_tooltip.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserToolTip.$1", + "type": "CompoundType", + "tags": [], + "label": "{ user, avatar, ...rest }", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserToolTipProps", + "text": "UserToolTipProps" + }, + ">" + ], + "path": "packages/kbn-user-profile-components/src/user_tooltip.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -938,6 +1040,79 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserToolTipProps", + "type": "Interface", + "tags": [], + "label": "UserToolTipProps", + "description": [ + "\nProps of {@link UserToolTip} component" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserToolTipProps", + "text": "UserToolTipProps" + }, + " extends Omit<", + "EuiToolTipProps", + ", \"title\" | \"content\">" + ], + "path": "packages/kbn-user-profile-components/src/user_tooltip.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserToolTipProps.user", + "type": "Object", + "tags": [], + "label": "user", + "description": [ + "\nUser to be rendered" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileUserInfo", + "text": "UserProfileUserInfo" + } + ], + "path": "packages/kbn-user-profile-components/src/user_tooltip.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserToolTipProps.avatar", + "type": "Object", + "tags": [], + "label": "avatar", + "description": [ + "\nAvatar data of user to be rendered" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileAvatarData", + "text": "UserProfileAvatarData" + }, + " | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_tooltip.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "enums": [], diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 321db07786d48..b0967bc0c569a 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 48 | 0 | 3 | 0 | +| 55 | 0 | 5 | 0 | ## Common diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 59733af696053..418262e39279d 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-03 +date: 2022-10-10 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 d34f5eb451f66..662803563e248 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-03 +date: 2022-10-10 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 3301abb7449f7..0d1e452936370 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-03 +date: 2022-10-10 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 7aba97a043a20..83b89a98d54b2 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-03 +date: 2022-10-10 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 99437dfa2d79f..6f9e09103095d 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-03 +date: 2022-10-10 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 b563f5461da0f..8ea2cb868f741 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-03 +date: 2022-10-10 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 5bb528b470212..c35811e02734d 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-03 +date: 2022-10-10 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 b08fc593fdcc7..fa1459a1b6c14 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index d29c21bc24d31..6749b467d5b11 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -10805,39 +10805,6 @@ ], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "lens", - "id": "def-common.isPartitionShape", - "type": "Function", - "tags": [], - "label": "isPartitionShape", - "description": [], - "signature": [ - "(shape: string) => boolean" - ], - "path": "x-pack/plugins/lens/common/visualizations/partition/utils.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-common.isPartitionShape.$1", - "type": "string", - "tags": [], - "label": "shape", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/plugins/lens/common/visualizations/partition/utils.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [ diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index ee26d7bf7b870..93d38cc9b09a5 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 658 | 0 | 567 | 45 | +| 656 | 0 | 565 | 45 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index f3039f89e8db3..44640af1df067 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-03 +date: 2022-10-10 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 a2bbb989390e5..c3a53ddfcb762 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-03 +date: 2022-10-10 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 a1ea39b243d1e..05f7e0d8fd343 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-03 +date: 2022-10-10 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 167f95ca81b40..fb83ae6f81a32 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-03 +date: 2022-10-10 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 7b13b081164ff..0393eb71f3632 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.devdocs.json b/api_docs/maps.devdocs.json index 83836732b0bfc..d40b99e5b5795 100644 --- a/api_docs/maps.devdocs.json +++ b/api_docs/maps.devdocs.json @@ -2182,6 +2182,149 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource", + "type": "Interface", + "tags": [], + "label": "IRasterSource", + "description": [], + "signature": [ + { + "pluginId": "maps", + "scope": "public", + "docId": "kibMapsPluginApi", + "section": "def-public.IRasterSource", + "text": "IRasterSource" + }, + " extends ", + { + "pluginId": "maps", + "scope": "public", + "docId": "kibMapsPluginApi", + "section": "def-public.ITMSSource", + "text": "ITMSSource" + } + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.canSkipSourceUpdate", + "type": "Function", + "tags": [], + "label": "canSkipSourceUpdate", + "description": [], + "signature": [ + "(dataRequest: ", + { + "pluginId": "maps", + "scope": "public", + "docId": "kibMapsPluginApi", + "section": "def-public.DataRequest", + "text": "DataRequest" + }, + ", nextRequestMeta: ", + "DataRequestMeta", + ") => Promise" + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.canSkipSourceUpdate.$1", + "type": "Object", + "tags": [], + "label": "dataRequest", + "description": [], + "signature": [ + { + "pluginId": "maps", + "scope": "public", + "docId": "kibMapsPluginApi", + "section": "def-public.DataRequest", + "text": "DataRequest" + } + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.canSkipSourceUpdate.$2", + "type": "CompoundType", + "tags": [], + "label": "nextRequestMeta", + "description": [], + "signature": [ + "DataRequestMeta" + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.isSourceStale", + "type": "Function", + "tags": [], + "label": "isSourceStale", + "description": [], + "signature": [ + "(mbSource: maplibregl.RasterTileSource, sourceData: ", + "RasterTileSourceData", + ") => boolean" + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.isSourceStale.$1", + "type": "Object", + "tags": [], + "label": "mbSource", + "description": [], + "signature": [ + "maplibregl.RasterTileSource" + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "maps", + "id": "def-public.IRasterSource.isSourceStale.$2", + "type": "Object", + "tags": [], + "label": "sourceData", + "description": [], + "signature": [ + "RasterTileSourceData" + ], + "path": "x-pack/plugins/maps/public/classes/sources/raster_source/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "maps", "id": "def-public.ITMSSource", diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 84567432aed62..9816101695ae5 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; @@ -21,7 +21,7 @@ Contact [GIS](https://github.com/orgs/elastic/teams/kibana-gis) for questions re | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 256 | 0 | 255 | 25 | +| 263 | 0 | 262 | 26 | ## Client diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 7e0c0528fef4a..fe95c1e9304be 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-03 +date: 2022-10-10 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 a7a7a0517b9b8..55d7ef71b1a20 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-03 +date: 2022-10-10 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 e884c213dddbc..da7874274db48 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-03 +date: 2022-10-10 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 37856b288370a..2f60b591347cc 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-03 +date: 2022-10-10 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 283d52b0a4c3a..28aac147b57ed 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-03 +date: 2022-10-10 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 278b15e23d47f..fecad3b643019 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index fd03a2f285ab5..2c08034995076 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -3793,7 +3793,7 @@ "label": "format", "description": [], "signature": [ - "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -3812,7 +3812,7 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -5320,7 +5320,7 @@ "label": "ObservabilityRuleTypeFormatter", "description": [], "signature": [ - "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -5339,7 +5339,7 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -7759,7 +7759,129 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; } & { name: string; description: string; indicator: { type: \"slo.apm.transaction_duration\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: \"slo.apm.transaction_error_rate\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: true; }; budgeting_method: \"occurrences\"; objective: { target: number; }; }, ", + ", { id: string; name: string; description: string; indicator: { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: boolean; }; budgeting_method: \"occurrences\"; objective: { target: number; }; revision: number; created_at: Date; updated_at: Date; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"PUT /api/observability/slos/{id}\"?: ", + "ServerRoute", + "<\"PUT /api/observability/slos/{id}\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; body: ", + "PartialC", + "<{ name: ", + "StringC", + "; description: ", + "StringC", + "; indicator: ", + "UnionC", + "<[", + "TypeC", + "<{ type: ", + "LiteralC", + "; params: ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_type: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_name: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; 'threshold.us': ", + "NumberC", + "; }>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_type: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_name: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; }>, ", + "PartialC", + "<{ good_status_codes: ", + "ArrayC", + "<", + "UnionC", + "<[", + "LiteralC", + "<\"2xx\">, ", + "LiteralC", + "<\"3xx\">, ", + "LiteralC", + "<\"4xx\">, ", + "LiteralC", + "<\"5xx\">]>>; }>]>; }>]>; time_window: ", + "TypeC", + "<{ duration: ", + "StringC", + "; is_rolling: ", + "LiteralC", + "; }>; budgeting_method: ", + "LiteralC", + "<\"occurrences\">; objective: ", + "TypeC", + "<{ target: ", + "NumberC", + "; }>; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { id: string; name: string; description: string; indicator: { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: boolean; }; budgeting_method: \"occurrences\"; objective: { target: number; }; created_at: Date; updated_at: Date; }, ", { "pluginId": "observability", "scope": "server", @@ -7783,7 +7905,7 @@ "TypeC", "<{ type: ", "LiteralC", - "<\"slo.apm.transaction_duration\">; params: ", + "; params: ", "TypeC", "<{ environment: ", "UnionC", @@ -7815,7 +7937,7 @@ "TypeC", "<{ type: ", "LiteralC", - "<\"slo.apm.transaction_error_rate\">; params: ", + "; params: ", "IntersectionC", "<[", "TypeC", @@ -7863,7 +7985,7 @@ "StringC", "; is_rolling: ", "LiteralC", - "; }>; budgeting_method: ", + "; }>; budgeting_method: ", "LiteralC", "<\"occurrences\">; objective: ", "TypeC", @@ -7987,7 +8109,129 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; } & { name: string; description: string; indicator: { type: \"slo.apm.transaction_duration\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: \"slo.apm.transaction_error_rate\"; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: true; }; budgeting_method: \"occurrences\"; objective: { target: number; }; }, ", + ", { id: string; name: string; description: string; indicator: { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: boolean; }; budgeting_method: \"occurrences\"; objective: { target: number; }; revision: number; created_at: Date; updated_at: Date; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"PUT /api/observability/slos/{id}\"?: ", + "ServerRoute", + "<\"PUT /api/observability/slos/{id}\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; body: ", + "PartialC", + "<{ name: ", + "StringC", + "; description: ", + "StringC", + "; indicator: ", + "UnionC", + "<[", + "TypeC", + "<{ type: ", + "LiteralC", + "; params: ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_type: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_name: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; 'threshold.us': ", + "NumberC", + "; }>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_type: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transaction_name: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; }>, ", + "PartialC", + "<{ good_status_codes: ", + "ArrayC", + "<", + "UnionC", + "<[", + "LiteralC", + "<\"2xx\">, ", + "LiteralC", + "<\"3xx\">, ", + "LiteralC", + "<\"4xx\">, ", + "LiteralC", + "<\"5xx\">]>>; }>]>; }>]>; time_window: ", + "TypeC", + "<{ duration: ", + "StringC", + "; is_rolling: ", + "LiteralC", + "; }>; budgeting_method: ", + "LiteralC", + "<\"occurrences\">; objective: ", + "TypeC", + "<{ target: ", + "NumberC", + "; }>; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { id: string; name: string; description: string; indicator: { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; 'threshold.us': number; }; } | { type: string; params: { environment: string; service: string; transaction_type: string; transaction_name: string; } & { good_status_codes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; }; }; time_window: { duration: string; is_rolling: boolean; }; budgeting_method: \"occurrences\"; objective: { target: number; }; created_at: Date; updated_at: Date; }, ", { "pluginId": "observability", "scope": "server", @@ -8011,7 +8255,7 @@ "TypeC", "<{ type: ", "LiteralC", - "<\"slo.apm.transaction_duration\">; params: ", + "; params: ", "TypeC", "<{ environment: ", "UnionC", @@ -8043,7 +8287,7 @@ "TypeC", "<{ type: ", "LiteralC", - "<\"slo.apm.transaction_error_rate\">; params: ", + "; params: ", "IntersectionC", "<[", "TypeC", @@ -8091,7 +8335,7 @@ "StringC", "; is_rolling: ", "LiteralC", - "; }>; budgeting_method: ", + "; }>; budgeting_method: ", "LiteralC", "<\"occurrences\">; objective: ", "TypeC", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 2f29ba44a8c06..a3c878a3cf2bf 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-03 +date: 2022-10-10 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 5b05f78a15a04..e6cbfa7500c92 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-03 +date: 2022-10-10 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 b6b1d856686da..020ecc70d4218 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,40 +15,43 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 477 | 397 | 38 | +| 487 | 404 | 38 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 32016 | 179 | 21562 | 1007 | +| 32173 | 179 | 21657 | 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) | - | 214 | 0 | 209 | 19 | +| | [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 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 38 | 0 | 38 | 52 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 80 | 1 | 71 | 2 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 81 | 1 | 72 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 87 | 0 | 71 | 28 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 264 | 2 | 249 | 9 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 24 | 0 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/@elastic/kibana-core) | Provides the necessary APIs to implement A/B testing scenarios, fetching the variations in configuration and reporting back metrics to track conversion rates of the experiments. | 16 | 0 | 0 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 34 | 0 | 26 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | Chat available on Elastic Cloud deployments for quicker assistance. | 1 | 0 | 0 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/@elastic/kibana-core) | Provides the necessary APIs to implement A/B testing scenarios, fetching the variations in configuration and reporting back metrics to track conversion rates of the experiments. | 12 | 0 | 0 | 0 | +| cloudFullStory | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | When Kibana runs on Elastic Cloud, this plugin registers FullStory as a shipper for telemetry. | 0 | 0 | 0 | 0 | +| cloudLinks | [Kibana Core](https://github.com/orgs/elastic/teams/@kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 18 | 0 | 2 | 3 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 212 | 0 | 204 | 7 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2688 | 0 | 30 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2689 | 0 | 23 | 0 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 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 | @@ -67,7 +70,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 57 | 0 | 57 | 2 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 105 | 0 | 101 | 3 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'image' function and renderer to expressions | 26 | 0 | 26 | 0 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `legacy metric` chart. | 48 | 0 | 48 | 1 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `legacy metric` chart. | 49 | 0 | 49 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'metric' function and renderer to expressions | 32 | 0 | 27 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. | 56 | 0 | 56 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Partition Visualization plugin adds a `partitionVis` renderer and `pieVis`, `mosaicVis`, `treemapVis`, `waffleVis` functions to the expression plugin. The renderer will display the `pie`, `waffle`, `treemap` and `mosaic` charts. | 70 | 0 | 70 | 2 | @@ -80,14 +83,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 222 | 0 | 95 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 5 | 249 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 62 | 0 | 62 | 2 | -| | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 263 | 0 | 14 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 997 | 3 | 893 | 17 | +| | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 270 | 0 | 15 | 2 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 996 | 3 | 893 | 17 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | graph | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 0 | 0 | 0 | 0 | | grokdebugger | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | -| | [Journey Onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 12 | 0 | 12 | 1 | +| | [Journey Onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 19 | 0 | 19 | 3 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 143 | 0 | 104 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 4 | 0 | 4 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 177 | 0 | 172 | 3 | @@ -101,14 +104,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 418 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 658 | 0 | 567 | 45 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 656 | 0 | 565 | 45 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [Security detections response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 204 | 0 | 92 | 50 | | logstash | [Logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 41 | 0 | 41 | 6 | -| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 256 | 0 | 255 | 25 | +| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 263 | 0 | 262 | 26 | | | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 251 | 9 | 78 | 39 | | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 11 | 0 | 9 | 1 | @@ -134,7 +137,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 32 | 0 | 13 | 0 | | | [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 4 | | searchprofiler | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | -| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 247 | 0 | 90 | 0 | +| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 249 | 0 | 90 | 0 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 55 | 0 | 54 | 23 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds URL Service and sharing capabilities to Kibana | 114 | 0 | 55 | 10 | @@ -148,15 +151,15 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 31 | 0 | 26 | 6 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 11 | 0 | 10 | 0 | -| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 20 | 0 | 4 | 3 | +| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 26 | 0 | 8 | 3 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 452 | 1 | 346 | 33 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 512 | 1 | 485 | 48 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 517 | 1 | 489 | 49 | | | [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 | @@ -175,7 +178,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 53 | 0 | 50 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 725 | 12 | 695 | 18 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 729 | 12 | 699 | 18 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -240,8 +243,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 5 | 0 | 2 | 0 | | | Kibana Core | - | 4 | 0 | 4 | 0 | -| | Kibana Core | - | 13 | 0 | 11 | 1 | -| | Kibana Core | - | 36 | 1 | 32 | 0 | +| | Kibana Core | - | 15 | 0 | 13 | 2 | +| | Kibana Core | - | 38 | 1 | 34 | 0 | | | Kibana Core | - | 99 | 0 | 51 | 0 | | | Kibana Core | - | 33 | 0 | 29 | 0 | | | Kibana Core | - | 15 | 1 | 15 | 0 | @@ -262,6 +265,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 4 | 0 | 0 | 0 | | | Kibana Core | - | 10 | 1 | 10 | 0 | | | Kibana Core | - | 14 | 0 | 11 | 0 | +| | [Owner missing] | - | 20 | 0 | 6 | 0 | +| | [Owner missing] | - | 9 | 0 | 9 | 3 | +| | Kibana Core | - | 7 | 0 | 7 | 0 | | | Kibana Core | - | 25 | 5 | 25 | 1 | | | Kibana Core | - | 7 | 0 | 7 | 1 | | | Kibana Core | - | 392 | 1 | 154 | 0 | @@ -281,11 +287,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 56 | 0 | 30 | 0 | | | Kibana Core | - | 9 | 0 | 5 | 1 | | | Kibana Core | - | 13 | 0 | 12 | 0 | -| | Kibana Core | - | 24 | 0 | 20 | 0 | +| | Kibana Core | - | 29 | 0 | 25 | 0 | | | Kibana Core | - | 11 | 1 | 11 | 0 | -| | Kibana Core | - | 48 | 0 | 8 | 0 | -| | Kibana Core | - | 5 | 0 | 5 | 0 | -| | Kibana Core | - | 7 | 0 | 7 | 0 | +| | Kibana Core | - | 62 | 0 | 8 | 0 | +| | Kibana Core | - | 6 | 0 | 6 | 0 | +| | Kibana Core | - | 19 | 0 | 19 | 0 | | | Kibana Core | - | 6 | 0 | 0 | 0 | | | Kibana Core | - | 5 | 0 | 0 | 0 | | | Kibana Core | - | 3 | 0 | 3 | 0 | @@ -301,8 +307,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 5 | 0 | 0 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 2 | 0 | 2 | 0 | +| | Kibana Core | - | 2 | 0 | 2 | 0 | +| | Kibana Core | - | 4 | 0 | 4 | 1 | | | Kibana Core | - | 106 | 1 | 75 | 0 | -| | Kibana Core | - | 308 | 1 | 137 | 0 | +| | Kibana Core | - | 310 | 1 | 137 | 0 | | | Kibana Core | - | 71 | 0 | 51 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 37 | 0 | 31 | 1 | @@ -326,6 +334,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 11 | 0 | 9 | 0 | | | Kibana Core | - | 5 | 0 | 5 | 0 | +| | Kibana Core | - | 13 | 0 | 12 | 0 | | | Kibana Core | - | 6 | 0 | 4 | 0 | | | Kibana Core | - | 2 | 0 | 1 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | @@ -351,7 +360,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 | @@ -386,7 +395,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | Just some helpers for kibana plugin devs. | 1 | 0 | 1 | 0 | | | [Owner missing] | - | 21 | 0 | 10 | 0 | | | [Owner missing] | - | 6 | 0 | 6 | 1 | -| | [Owner missing] | - | 74 | 0 | 71 | 0 | +| | [Owner missing] | - | 75 | 0 | 72 | 0 | | | [Owner missing] | Security Solution auto complete | 56 | 1 | 41 | 1 | | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | | | [Owner missing] | - | 76 | 0 | 67 | 1 | @@ -398,7 +407,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 | @@ -443,7 +452,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 113 | 1 | 65 | 0 | | | [Owner missing] | - | 83 | 0 | 83 | 1 | | | [Owner missing] | - | 7 | 0 | 6 | 0 | -| | [Owner missing] | - | 48 | 0 | 3 | 0 | +| | [Owner missing] | - | 55 | 0 | 5 | 0 | | | [Owner missing] | - | 34 | 0 | 14 | 1 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | | | [Owner missing] | - | 30 | 0 | 20 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index f9b349676713b..72ab2b6b9b0c1 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-03 +date: 2022-10-10 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 489f7c32b600e..c374d8ff9bb23 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-03 +date: 2022-10-10 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 6a8252bfebe78..2cd2b8b545029 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-03 +date: 2022-10-10 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 fbdfd3cb5bd01..25fde92c9c027 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-03 +date: 2022-10-10 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 11313a6dc5f24..a7ce02fa445ae 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 089f973bc0bf0..37e78ced1236a 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -63,7 +63,7 @@ "label": "get", "description": [], "signature": [ - "({ id, index }: GetAlertParams) => Promise> | undefined>" + "({ id, index }: GetAlertParams) => Promise> | undefined>" ], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", "deprecated": false, @@ -203,7 +203,7 @@ "SortOptions", "[] | undefined; search_after?: (string | number)[] | undefined; }) => Promise<", "SearchResponse", - ">, unknown>>" + ">, unknown>>" ], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", "deprecated": false, @@ -2319,7 +2319,7 @@ "SearchRequest", ">(request: TSearchRequest) => Promise<", "ESSearchResponse", - "> & OutputOf>>, TSearchRequest, { restTotalHitsAsInt: false; }>>" + "> & OutputOf>>, TSearchRequest, { restTotalHitsAsInt: false; }>>" ], "path": "x-pack/plugins/rule_registry/server/rule_data_client/types.ts", "deprecated": false, @@ -3949,7 +3949,7 @@ "label": "parseTechnicalFields", "description": [], "signature": [ - "(input: unknown, partial?: boolean) => OutputOf>" + "(input: unknown, partial?: boolean) => OutputOf>" ], "path": "x-pack/plugins/rule_registry/common/parse_technical_fields.ts", "deprecated": false, @@ -4270,7 +4270,7 @@ "label": "ParsedTechnicalFields", "description": [], "signature": [ - "{ readonly '@timestamp': string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.space_ids\": string[]; readonly \"kibana.alert.uuid\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.rule.name\": string; readonly tags?: string[] | undefined; readonly 'event.action'?: string | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly 'event.kind'?: string | undefined; }" + "{ readonly '@timestamp': string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.space_ids\": string[]; readonly \"kibana.alert.uuid\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.rule.name\": string; readonly tags?: string[] | undefined; readonly 'event.action'?: string | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.time_range\"?: unknown; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly 'event.kind'?: string | undefined; }" ], "path": "x-pack/plugins/rule_registry/common/parse_technical_fields.ts", "deprecated": false, diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 3ac4843ea4176..36968e9f6d84c 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-03 +date: 2022-10-10 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 17f5b0c7f230d..cfd864e5e1e7c 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-03 +date: 2022-10-10 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 5063707c3abd9..3ddfe47eb5d24 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-03 +date: 2022-10-10 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 53186cbce4362..f947e389666e7 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-03 +date: 2022-10-10 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 0032f71ba6484..092940749757f 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-03 +date: 2022-10-10 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 b3c954d358cb1..6e3788871ed75 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-03 +date: 2022-10-10 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 280c60872da7d..b0006411b10ce 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-03 +date: 2022-10-10 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 eac6e8187a83f..4694eea70cade 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-03 +date: 2022-10-10 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 50deed0a47fdd..800a8386708e0 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-03 +date: 2022-10-10 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 64b2f1e872dd9..e3e29cd303353 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index d400f91576d7d..b913d81e4e55a 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -125,6 +125,22 @@ "path": "x-pack/plugins/security/common/model/authenticated_user.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-public.AuthenticatedUser.profile_uid", + "type": "string", + "tags": [], + "label": "profile_uid", + "description": [ + "\nUser profile ID of this user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1522,6 +1538,22 @@ "path": "x-pack/plugins/security/common/model/authenticated_user.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.profile_uid", + "type": "string", + "tags": [], + "label": "profile_uid", + "description": [ + "\nUser profile ID of this user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2517,10 +2549,6 @@ "plugin": "data", "path": "src/plugins/data/server/search/session/session_service.ts" }, - { - "plugin": "cloud", - "path": "x-pack/plugins/cloud/server/routes/chat.ts" - }, { "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/annotations.ts" @@ -2557,6 +2585,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts" }, + { + "plugin": "cloudChat", + "path": "x-pack/plugins/cloud_integrations/cloud_chat/server/routes/chat.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts" @@ -2688,24 +2720,6 @@ "path": "x-pack/plugins/security/server/plugin.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.SecurityPluginSetup.setIsElasticCloudDeployment", - "type": "Function", - "tags": [], - "label": "setIsElasticCloudDeployment", - "description": [ - "\nSets the flag to indicate that Kibana is running inside an Elastic Cloud deployment. This flag is supposed to be\nset by the Cloud plugin and can be only once." - ], - "signature": [ - "() => void" - ], - "path": "x-pack/plugins/security/server/plugin.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] } ], "lifecycle": "setup", @@ -3077,6 +3091,22 @@ "path": "x-pack/plugins/security/common/model/authenticated_user.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.AuthenticatedUser.profile_uid", + "type": "string", + "tags": [], + "label": "profile_uid", + "description": [ + "\nUser profile ID of this user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/security.mdx b/api_docs/security.mdx index b62b787d9d0e4..5fe2fe5894253 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Platform Security](https://github.com/orgs/elastic/teams/kibana-securit | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 247 | 0 | 90 | 0 | +| 249 | 0 | 90 | 0 | ## Client diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 088b9d7a12e2b..33d50cb8babe2 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-03 +date: 2022-10-10 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 eaddd45f9b778..77ba39d41f48e 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-03 +date: 2022-10-10 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 c6ba8145d4c64..df0619d9b5290 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-03 +date: 2022-10-10 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 daaaceba668b4..1c86c9c84811f 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-03 +date: 2022-10-10 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 1196b40d5c3bf..04356137a0932 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.devdocs.json b/api_docs/stack_alerts.devdocs.json index 3850659e8a4d7..9614f1ebea57d 100644 --- a/api_docs/stack_alerts.devdocs.json +++ b/api_docs/stack_alerts.devdocs.json @@ -24,7 +24,7 @@ "signature": [ "\".index-threshold\"" ], - "path": "x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts", + "path": "x-pack/plugins/stack_alerts/server/alert_types/index_threshold/rule_type.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index fdd5dfbc025b1..909167b8d55ce 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-03 +date: 2022-10-10 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 89056c9ebd088..6c49650732577 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-03 +date: 2022-10-10 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 1b7e13dd4d650..1217311901de7 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-03 +date: 2022-10-10 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 8e865875b83fe..bcedaaf0d81ff 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-03 +date: 2022-10-10 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 e5cbc94c567a3..5d0c3054de25a 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-03 +date: 2022-10-10 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 7b775d1ce3bce..e0bc1e797caaa 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-03 +date: 2022-10-10 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 7312616ea8a6e..b352bd4e0dca3 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.devdocs.json b/api_docs/threat_intelligence.devdocs.json index a29de53bf77a5..d5d35c60068c4 100644 --- a/api_docs/threat_intelligence.devdocs.json +++ b/api_docs/threat_intelligence.devdocs.json @@ -274,6 +274,103 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.useQuery", + "type": "Function", + "tags": [], + "label": "useQuery", + "description": [], + "signature": [ + "() => ", + "Query" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.useFilters", + "type": "Function", + "tags": [], + "label": "useFilters", + "description": [], + "signature": [ + "() => ", + "Filter", + "[]" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.useGlobalTime", + "type": "Function", + "tags": [], + "label": "useGlobalTime", + "description": [], + "signature": [ + "() => ", + "TimeRange" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.SiemSearchBar", + "type": "Function", + "tags": [], + "label": "SiemSearchBar", + "description": [], + "signature": [ + "React.VoidFunctionComponent" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.SiemSearchBar.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.SiemSearchBar.$2", + "type": "Any", + "tags": [], + "label": "context", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "initialIsOpen": false diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 4abe6fedf3bf3..77d1f454f2f5a 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Protections Experience Team](https://github.com/orgs/elastic/teams/prot | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 20 | 0 | 4 | 3 | +| 26 | 0 | 8 | 3 | ## Client diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 692501acd7de0..a010695c5c771 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-03 +date: 2022-10-10 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 9603096cd2b99..4d851dc086fe4 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index d6351e414ae46..e9e5908c5ee26 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -3139,7 +3139,7 @@ "description": [], "signature": [ "BasicFields", - " & { tags?: string[] | undefined; kibana?: string[] | undefined; \"@timestamp\"?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"event.action\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; } & { [x: string]: unknown[]; }" + " & { tags?: string[] | undefined; kibana?: string[] | undefined; \"@timestamp\"?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"event.action\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.time_range\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; } & { [x: string]: unknown[]; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -3942,6 +3942,22 @@ "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.FieldBrowserOptions.preselectedCategoryIds", + "type": "Array", + "tags": [], + "label": "preselectedCategoryIds", + "description": [ + "\nCategories that should be selected initially" + ], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/field_browser/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -8025,6 +8041,42 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.getRuleSnoozeModal", + "type": "Function", + "tags": [], + "label": "getRuleSnoozeModal", + "description": [], + "signature": [ + "(props: ", + "RuleSnoozeModalProps", + ") => React.ReactElement<", + "RuleSnoozeModalProps", + ", string | React.JSXElementConstructor>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.getRuleSnoozeModal.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "RuleSnoozeModalProps" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "lifecycle": "start", @@ -8164,6 +8216,18 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-server.TIME_SERIES_BUCKET_SELECTOR_FIELD", + "type": "string", + "tags": [], + "label": "TIME_SERIES_BUCKET_SELECTOR_FIELD", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-server.TimeSeriesQuery", @@ -8545,6 +8609,17 @@ "path": "x-pack/plugins/triggers_actions_ui/common/data/index.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-common.TimeSeriesResult.truncated", + "type": "boolean", + "tags": [], + "label": "truncated", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/common/data/index.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index a9c60110ba139..a4ab7aaa8680e 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.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 | |-------------------|-----------|------------------------|-----------------| -| 512 | 1 | 485 | 48 | +| 517 | 1 | 489 | 49 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 734e896cbdf27..ba35eb86cbf0e 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-03 +date: 2022-10-10 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 5ac50aa29c085..45bddb4318d64 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-03 +date: 2022-10-10 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 2818641379fb7..3b29a19fa6be6 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-03 +date: 2022-10-10 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 ed31e74c5d7eb..bdeec48aa8ea9 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-03 +date: 2022-10-10 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 e75d86bbab218..191e2461415cf 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-03 +date: 2022-10-10 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 0ac5762c42af3..47c97e65e6724 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-03 +date: 2022-10-10 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 b493774dc1eb3..f8c3a14d66b2d 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-03 +date: 2022-10-10 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 f3c60dc8b7b43..f4689a3d2597d 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-03 +date: 2022-10-10 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 ec4208a4a06da..b292069fca384 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-03 +date: 2022-10-10 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 43f73daa08070..f36a18ef40bd0 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-03 +date: 2022-10-10 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 d537797699cab..89dfc16e9f523 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-03 +date: 2022-10-10 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 08324c72b813f..1e7017d80aabf 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-03 +date: 2022-10-10 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 7d052e7dd40fb..79dde1fcfab44 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-03 +date: 2022-10-10 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 696edbd5d321e..21d856b2f4dd3 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-03 +date: 2022-10-10 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 ef87c344661e6..2c7ec032c466e 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-03 +date: 2022-10-10 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 1a5a44160bf46..fba1745b81e6b 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-03 +date: 2022-10-10 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 8651bb82ad813..e211f35e5c3c7 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-03 +date: 2022-10-10 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 440839b51793a..db8a77c924275 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index befca8a834867..e283e14d8ba8f 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -9481,6 +9481,42 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.MinMax", + "type": "Interface", + "tags": [], + "label": "MinMax", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/common.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-common.MinMax.min", + "type": "number", + "tags": [], + "label": "min", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/common.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MinMax.max", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/common.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.MovingAverageParams", @@ -13164,6 +13200,29 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.PercentageModeConfig", + "type": "Type", + "tags": [], + "label": "PercentageModeConfig", + "description": [], + "signature": [ + "({ isPercentageMode: true; } & ", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.MinMax", + "text": "MinMax" + }, + ") | { isPercentageMode: boolean; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.PercentileColumn", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 2edc6b736c5b6..e09c5b43fe526 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-03 +date: 2022-10-10 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 725 | 12 | 695 | 18 | +| 729 | 12 | 699 | 18 | ## Client 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/api/saved-objects/find.asciidoc b/docs/api/saved-objects/find.asciidoc index 43c7f4cde8fa8..275bd1c21f9ed 100644 --- a/docs/api/saved-objects/find.asciidoc +++ b/docs/api/saved-objects/find.asciidoc @@ -52,6 +52,15 @@ experimental[] Retrieve a paginated set of {kib} saved objects by various condit `has_reference`:: (Optional, object) Filters to objects that have a relationship with the type and ID combination. +`has_reference_operator`:: + (Optional, string) The operator to use for the `has_reference` parameter. Either `OR` or `AND`. Defaults to `OR`. + +`has_no_reference`:: + (Optional, object) Filters to objects that do not have a relationship with the type and ID combination. + +`has_no_reference_operator`:: + (Optional, string) The operator to use for the `has_no_reference` parameter. Either `OR` or `AND`. Defaults to `OR`. + `filter`:: (Optional, string) The filter is a KQL string with the caveat that if you filter with an attribute from your saved object type, it should look like that: `savedObjectType.attributes.title: "myTitle"`. However, If you use a root attribute of a saved 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/concepts/data-views.asciidoc b/docs/concepts/data-views.asciidoc index 8726b3a55cbd0..2ce89474a003a 100644 --- a/docs/concepts/data-views.asciidoc +++ b/docs/concepts/data-views.asciidoc @@ -65,14 +65,14 @@ based on different timestamps. . To specify your own {data-source} name, click *Show advanced settings*, then enter the name in the *Custom {data-source} ID* field. For example, enter your {es} index alias name. -. Click *Save {data-source} to {kib}*. +. [[reload-fields]] To use this data in searches and visualizations that you intend to save, click *Save {data-source} to {kib}*. + +. To explore your data without creating a {data-source}, click *Use without saving*. + -[[reload-fields]] {kib} is now configured to use your {es} data. When a new field is added to an index, -the {data-source} field list is updated -the next time the {data-source} is loaded, for example, when you load the page or -move between {kib} apps. +This allows you to explore your data in *Discover*, *Lens*, and *Maps* +without making it visible to others in your space. You can save the {data-source} later +if you create a search or visualization that you want to share. -. Select this {data-source} when you search and visualize your data. [float] [[rollup-data-view]] diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index f4fc9c67508ef..4853d859b371a 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -71,7 +71,7 @@ as uiSettings within the code. |{kib-repo}blob/{branch}/src/plugins/data_views/README.mdx[dataViews] |The data views API provides a consistent method of structuring and formatting documents -and field lists across the various Kibana apps. Its typically used in conjunction with +and field lists across the various Kibana apps. It's typically used in conjunction with for composing queries. @@ -424,10 +424,22 @@ The plugin exposes the static DefaultEditorController class to consume. |The cloud plugin adds Cloud-specific features to Kibana. +|{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_chat/README.md[cloudChat] +|Integrates with DriftChat in order to provide live support to our Elastic Cloud users. This plugin should only run on Elastic Cloud. + + |{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_experiments/README.mdx[cloudExperiments] |The Cloud Experiments Service provides the necessary APIs to implement A/B testing scenarios, fetching the variations in configuration and reporting back metrics to track conversion rates of the experiments. +|{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_full_story/README.md[cloudFullStory] +|Integrates with FullStory in order to provide better product analytics, so we can understand how our users make use of Kibana. This plugin should only run on Elastic Cloud. + + +|{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_links/README.md[cloudLinks] +|Adds all the links to the Elastic Cloud console. + + |{kib-repo}blob/{branch}/x-pack/plugins/cloud_security_posture/README.md[cloudSecurityPosture] |Cloud Posture automates the identification and remediation of risks across cloud infrastructures diff --git a/docs/management/index-patterns/images/create-data-view.png b/docs/management/index-patterns/images/create-data-view.png index 229ed0f490b41..af5b01111cd2e 100644 Binary files a/docs/management/index-patterns/images/create-data-view.png and b/docs/management/index-patterns/images/create-data-view.png differ diff --git a/docs/settings/search-sessions-settings.asciidoc b/docs/settings/search-sessions-settings.asciidoc index 7628ecfb397e1..7a51c23c9a1cc 100644 --- a/docs/settings/search-sessions-settings.asciidoc +++ b/docs/settings/search-sessions-settings.asciidoc @@ -10,9 +10,6 @@ Configure the search session settings in your `kibana.yml` configuration file. `data.search.sessions.enabled` {ess-icon}:: Set to `true` (default) to enable search sessions. -`data.search.sessions.trackingInterval` {ess-icon}:: -The frequency for updating the state of a search session. The default is `10s`. - `data.search.sessions.pageSize` {ess-icon}:: How many search sessions {kib} processes at once while monitoring session progress. The default is `100`. @@ -21,9 +18,6 @@ session progress. The default is `100`. How long {kib} stores search results from unsaved sessions, after the last search in the session completes. The default is `5m`. -`data.search.sessions.notTouchedInProgressTimeout` {ess-icon}:: -How long a search session can run after a user navigates away without saving a session. The default is `1m`. - `data.search.sessions.maxUpdateRetries` {ess-icon}:: How many retries {kib} can perform while attempting to save a search session. The default is `3`. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 47bc5642604bd..69316af1593a1 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -80,9 +80,18 @@ configuration is effectively ignored when <> is enable *Default: `true`* [[elasticsearch-maxSockets]] `elasticsearch.maxSockets`:: -The maximum number of sockets that can be used for communications with elasticsearch. +The maximum number of sockets that can be used for communications with {es}. *Default: `Infinity`* + +[[elasticsearch-maxIdleSockets]] `elasticsearch.maxIdleSockets`:: +The maximum number of idle sockets to keep open between {kib} and {es}. If more sockets become idle, they will be closed. +*Default: `256`* + +[[elasticsearch-idleSocketTimeout]] `elasticsearch.idleSocketTimeout`:: +The timeout for idle sockets kept open between {kib} and {es}. If the socket is idle for longer than this timeout, it will be closed. If you have a transparent proxy between {kib} and {es} be sure to set this value lower than or equal to the proxy's timeout. +*Default: `60s`* + `elasticsearch.customHeaders`:: | Header names and values to send to {es}. Any custom headers cannot be overwritten by client-side headers, regardless of the 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 f122f532d5274..bd550a1ea2c48 100644 --- a/package.json +++ b/package.json @@ -116,10 +116,11 @@ "@elastic/react-search-ui": "^1.14.0", "@elastic/request-crypto": "2.0.1", "@elastic/search-ui-app-search-connector": "^1.14.0", - "@emotion/cache": "^11.9.3", - "@emotion/css": "^11.9.0", - "@emotion/react": "^11.9.3", - "@emotion/serialize": "^1.0.4", + "@emotion/cache": "^11.10.3", + "@emotion/css": "^11.10.0", + "@emotion/react": "^11.10.4", + "@emotion/serialize": "^1.1.0", + "@emotion/server": "^11.10.0", "@grpc/grpc-js": "^1.6.7", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.4", @@ -215,6 +216,9 @@ "@kbn/core-http-context-server-mocks": "link:bazel-bin/packages/core/http/core-http-context-server-mocks", "@kbn/core-http-request-handler-context-server": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server", "@kbn/core-http-request-handler-context-server-internal": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal", + "@kbn/core-http-resources-server": "link:bazel-bin/packages/core/http/core-http-resources-server", + "@kbn/core-http-resources-server-internal": "link:bazel-bin/packages/core/http/core-http-resources-server-internal", + "@kbn/core-http-resources-server-mocks": "link:bazel-bin/packages/core/http/core-http-resources-server-mocks", "@kbn/core-http-router-server-internal": "link:bazel-bin/packages/core/http/core-http-router-server-internal", "@kbn/core-http-router-server-mocks": "link:bazel-bin/packages/core/http/core-http-router-server-mocks", "@kbn/core-http-server": "link:bazel-bin/packages/core/http/core-http-server", @@ -291,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", @@ -440,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", @@ -482,7 +488,6 @@ "fast-deep-equal": "^3.1.1", "fflate": "^0.6.9", "file-saver": "^1.3.8", - "fnv-plus": "^1.3.1", "font-awesome": "4.7.0", "formik": "^2.2.9", "fp-ts": "^2.3.1", @@ -658,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", @@ -672,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", @@ -687,8 +692,8 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/synthetics": "^1.0.0-beta.22", - "@emotion/babel-preset-css-prop": "^11.2.0", - "@emotion/jest": "^11.9.4", + "@emotion/babel-preset-css-prop": "^11.10.0", + "@emotion/jest": "^11.10.0", "@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/schema": "^0.1.2", "@jest/console": "^26.6.2", @@ -816,7 +821,6 @@ "@types/fetch-mock": "^7.3.1", "@types/file-saver": "^2.0.0", "@types/flot": "^0.0.31", - "@types/fnv-plus": "^1.3.0", "@types/geojson": "7946.0.7", "@types/getos": "^3.0.0", "@types/gulp": "^4.0.6", @@ -942,6 +946,9 @@ "@types/kbn__core-http-context-server-mocks": "link:bazel-bin/packages/core/http/core-http-context-server-mocks/npm_module_types", "@types/kbn__core-http-request-handler-context-server": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server/npm_module_types", "@types/kbn__core-http-request-handler-context-server-internal": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal/npm_module_types", + "@types/kbn__core-http-resources-server": "link:bazel-bin/packages/core/http/core-http-resources-server/npm_module_types", + "@types/kbn__core-http-resources-server-internal": "link:bazel-bin/packages/core/http/core-http-resources-server-internal/npm_module_types", + "@types/kbn__core-http-resources-server-mocks": "link:bazel-bin/packages/core/http/core-http-resources-server-mocks/npm_module_types", "@types/kbn__core-http-router-server-internal": "link:bazel-bin/packages/core/http/core-http-router-server-internal/npm_module_types", "@types/kbn__core-http-router-server-mocks": "link:bazel-bin/packages/core/http/core-http-router-server-mocks/npm_module_types", "@types/kbn__core-http-server": "link:bazel-bin/packages/core/http/core-http-server/npm_module_types", @@ -1020,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", @@ -1279,7 +1287,7 @@ "babel-plugin-require-context-hook": "^1.0.0", "babel-plugin-styled-components": "^2.0.7", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "backport": "^8.9.4", + "backport": "^8.9.7", "callsites": "^3.1.0", "chance": "1.0.18", "chokidar": "^3.5.3", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 0d5ecd4bc4cfc..aae46a111c053 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -81,6 +81,9 @@ filegroup( "//packages/core/http/core-http-context-server-mocks:build", "//packages/core/http/core-http-request-handler-context-server:build", "//packages/core/http/core-http-request-handler-context-server-internal:build", + "//packages/core/http/core-http-resources-server:build", + "//packages/core/http/core-http-resources-server-internal:build", + "//packages/core/http/core-http-resources-server-mocks:build", "//packages/core/http/core-http-router-server-internal:build", "//packages/core/http/core-http-router-server-mocks:build", "//packages/core/http/core-http-server:build", @@ -157,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", @@ -418,6 +422,9 @@ filegroup( "//packages/core/http/core-http-context-server-mocks:build_types", "//packages/core/http/core-http-request-handler-context-server:build_types", "//packages/core/http/core-http-request-handler-context-server-internal:build_types", + "//packages/core/http/core-http-resources-server:build_types", + "//packages/core/http/core-http-resources-server-internal:build_types", + "//packages/core/http/core-http-resources-server-mocks:build_types", "//packages/core/http/core-http-router-server-internal:build_types", "//packages/core/http/core-http-router-server-mocks:build_types", "//packages/core/http/core-http-server:build_types", @@ -494,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/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts b/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts index ac40eedfccb7d..dd750a56fbf2d 100644 --- a/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts +++ b/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts @@ -61,6 +61,19 @@ const mockedResponse: StatusResponse = { '15m': 0.1, }, }, + elasticsearch_client: { + protocol: 'https', + connectedNodes: 3, + nodesWithActiveSockets: 3, + nodesWithIdleSockets: 1, + totalActiveSockets: 25, + totalIdleSockets: 2, + totalQueuedRequests: 0, + mostActiveNodeSockets: 15, + averageActiveSocketsPerNode: 8, + mostIdleNodeSockets: 2, + averageIdleSocketsPerNode: 0.5, + }, process: { pid: 1, memory: { diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/index.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/index.ts index aa1364c179e18..6f1f276c7d089 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/index.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/index.ts @@ -9,7 +9,7 @@ export { ScopedClusterClient } from './src/scoped_cluster_client'; export { ClusterClient } from './src/cluster_client'; export { configureClient } from './src/configure_client'; -export { AgentManager } from './src/agent_manager'; +export { type AgentStore, AgentManager } from './src/agent_manager'; export { getRequestDebugMeta, getErrorMessage } from './src/log_query_and_deprecation'; export { PRODUCT_RESPONSE_HEADER, diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.test.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.test.ts index 811d9d95831ef..0f374bd8311e5 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.test.ts @@ -44,35 +44,27 @@ describe('AgentManager', () => { expect(httpsAgent).toEqual(mockedHttpsAgent); }); - it('provides Agents with a valid default configuration', () => { - const agentManager = new AgentManager(); - const agentFactory = agentManager.getAgentFactory(); - agentFactory({ url: new URL('http://elastic-node-1:9200') }); - expect(HttpAgent).toBeCalledTimes(1); - expect(HttpAgent).toBeCalledWith({ - keepAlive: true, - keepAliveMsecs: 1000, - maxFreeSockets: 256, - maxSockets: 256, - scheduling: 'lifo', - }); - }); - it('takes into account the provided configurations', () => { - const agentManager = new AgentManager({ maxFreeSockets: 32, maxSockets: 2048 }); + const agentManager = new AgentManager(); const agentFactory = agentManager.getAgentFactory({ - maxSockets: 1024, + maxTotalSockets: 1024, scheduling: 'fifo', }); agentFactory({ url: new URL('http://elastic-node-1:9200') }); - expect(HttpAgent).toBeCalledTimes(1); + const agentFactory2 = agentManager.getAgentFactory({ + maxFreeSockets: 10, + scheduling: 'lifo', + }); + agentFactory2({ url: new URL('http://elastic-node-2:9200') }); + expect(HttpAgent).toBeCalledTimes(2); expect(HttpAgent).toBeCalledWith({ - keepAlive: true, - keepAliveMsecs: 1000, - maxFreeSockets: 32, - maxSockets: 1024, + maxTotalSockets: 1024, scheduling: 'fifo', }); + expect(HttpAgent).toBeCalledWith({ + maxFreeSockets: 10, + scheduling: 'lifo', + }); }); it('provides Agents that match the URLs protocol', () => { @@ -86,7 +78,7 @@ describe('AgentManager', () => { expect(HttpsAgent).toHaveBeenCalledTimes(1); }); - it('provides the same Agent iif URLs use the same protocol', () => { + it('provides the same Agent if URLs use the same protocol', () => { const agentManager = new AgentManager(); const agentFactory = agentManager.getAgentFactory(); const agent1 = agentFactory({ url: new URL('http://elastic-node-1:9200') }); @@ -104,10 +96,10 @@ describe('AgentManager', () => { const agentFactory = agentManager.getAgentFactory(); const agent = agentFactory({ url: new URL('http://elastic-node-1:9200') }); // eslint-disable-next-line dot-notation - expect(agentManager['httpStore'].has(agent)).toEqual(true); + expect(agentManager['agents'].has(agent)).toEqual(true); agent.destroy(); // eslint-disable-next-line dot-notation - expect(agentManager['httpStore'].has(agent)).toEqual(false); + expect(agentManager['agents'].has(agent)).toEqual(false); }); }); @@ -122,4 +114,21 @@ describe('AgentManager', () => { }); }); }); + + describe('#getAgents()', () => { + it('returns the created HTTP and HTTPs Agent instances', () => { + const agentManager = new AgentManager(); + const agentFactory1 = agentManager.getAgentFactory(); + const agentFactory2 = agentManager.getAgentFactory(); + const agent1 = agentFactory1({ url: new URL('http://elastic-node-1:9200') }); + const agent2 = agentFactory2({ url: new URL('http://elastic-node-1:9200') }); + const agent3 = agentFactory1({ url: new URL('https://elastic-node-1:9200') }); + const agent4 = agentFactory2({ url: new URL('https://elastic-node-1:9200') }); + + const agents = agentManager.getAgents(); + + expect(agents.size).toEqual(4); + expect([...agents]).toEqual(expect.arrayContaining([agent1, agent2, agent3, agent4])); + }); + }); }); diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts index eb68014561d77..9f654216bdce8 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/agent_manager.ts @@ -6,22 +6,23 @@ * Side Public License, v 1. */ -import { Agent as HttpAgent } from 'http'; +import { Agent as HttpAgent, type AgentOptions } from 'http'; import { Agent as HttpsAgent } from 'https'; -import { ConnectionOptions, HttpAgentOptions } from '@elastic/elasticsearch'; +import type { ConnectionOptions, HttpAgentOptions } from '@elastic/elasticsearch'; const HTTPS = 'https:'; -const DEFAULT_CONFIG: HttpAgentOptions = { - keepAlive: true, - keepAliveMsecs: 1000, - maxSockets: 256, - maxFreeSockets: 256, - scheduling: 'lifo', -}; export type NetworkAgent = HttpAgent | HttpsAgent; export type AgentFactory = (connectionOpts: ConnectionOptions) => NetworkAgent; +export interface AgentFactoryProvider { + getAgentFactory(agentOptions?: HttpAgentOptions): AgentFactory; +} + +export interface AgentStore { + getAgents(): Set; +} + /** * Allows obtaining Agent factories, which can then be fed into elasticsearch-js's Client class. * Ideally, we should obtain one Agent factory for each ES Client class. @@ -33,18 +34,14 @@ export type AgentFactory = (connectionOpts: ConnectionOptions) => NetworkAgent; * exposes methods that can modify the underlying pools, effectively impacting the connections of other Clients. * @internal **/ -export class AgentManager { - // Stores Https Agent instances - private httpsStore: Set; - // Stores Http Agent instances - private httpStore: Set; +export class AgentManager implements AgentFactoryProvider, AgentStore { + private agents: Set; - constructor(private agentOptions: HttpAgentOptions = DEFAULT_CONFIG) { - this.httpsStore = new Set(); - this.httpStore = new Set(); + constructor() { + this.agents = new Set(); } - public getAgentFactory(agentOptions?: HttpAgentOptions): AgentFactory { + public getAgentFactory(agentOptions?: AgentOptions): AgentFactory { // a given agent factory always provides the same Agent instances (for the same protocol) // we keep references to the instances at factory level, to be able to reuse them let httpAgent: HttpAgent; @@ -53,37 +50,34 @@ export class AgentManager { return (connectionOpts: ConnectionOptions): NetworkAgent => { if (connectionOpts.url.protocol === HTTPS) { if (!httpsAgent) { - const config = Object.assign( - {}, - DEFAULT_CONFIG, - this.agentOptions, - agentOptions, - connectionOpts.tls - ); + const config = Object.assign({}, agentOptions, connectionOpts.tls); httpsAgent = new HttpsAgent(config); - this.httpsStore.add(httpsAgent); - dereferenceOnDestroy(this.httpsStore, httpsAgent); + this.agents.add(httpsAgent); + dereferenceOnDestroy(this.agents, httpsAgent); } return httpsAgent; } if (!httpAgent) { - const config = Object.assign({}, DEFAULT_CONFIG, this.agentOptions, agentOptions); - httpAgent = new HttpAgent(config); - this.httpStore.add(httpAgent); - dereferenceOnDestroy(this.httpStore, httpAgent); + httpAgent = new HttpAgent(agentOptions); + this.agents.add(httpAgent); + dereferenceOnDestroy(this.agents, httpAgent); } return httpAgent; }; } + + public getAgents(): Set { + return this.agents; + } } -const dereferenceOnDestroy = (protocolStore: Set, agent: NetworkAgent) => { +const dereferenceOnDestroy = (store: Set, agent: NetworkAgent) => { const doDestroy = agent.destroy.bind(agent); agent.destroy = () => { - protocolStore.delete(agent); + store.delete(agent); doDestroy(); }; }; diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.test.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.test.ts index 3c791c44bcb42..50424ddf3746f 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.test.ts @@ -18,6 +18,8 @@ const createConfig = ( customHeaders: {}, compression: false, maxSockets: Infinity, + maxIdleSockets: 300, + idleSocketTimeout: duration(30, 'seconds'), sniffOnStart: false, sniffOnConnectionFault: false, sniffInterval: false, @@ -41,15 +43,13 @@ describe('parseClientOptions', () => { ); }); - it('specifies `headers.maxSockets` Infinity and `keepAlive` true by default', () => { + it('specifies `maxTotalSockets` Infinity and `keepAlive` true by default', () => { const config = createConfig({}); - expect(parseClientOptions(config, false, kibanaVersion)).toEqual( + expect(parseClientOptions(config, false, kibanaVersion).agent).toEqual( expect.objectContaining({ - agent: { - keepAlive: true, - maxSockets: Infinity, - }, + keepAlive: true, + maxTotalSockets: Infinity, }) ); }); @@ -119,18 +119,35 @@ describe('parseClientOptions', () => { }); describe('`maxSockets` option', () => { - it('uses the specified config value', () => { + it('sets the agent.maxTotalSockets config value', () => { const options = parseClientOptions( createConfig({ maxSockets: 1024 }), false, kibanaVersion ); - expect(options.agent).toHaveProperty('maxSockets', 1024); + expect(options.agent).toHaveProperty('maxTotalSockets', 1024); }); + }); - it('defaults to `Infinity` if not specified by the config', () => { - const options = parseClientOptions(createConfig({}), false, kibanaVersion); - expect(options.agent).toHaveProperty('maxSockets', Infinity); + describe('`maxIdleSockets` option', () => { + it('sets the agent.maxFreeSockets config value', () => { + const options = parseClientOptions( + createConfig({ maxIdleSockets: 1024 }), + false, + kibanaVersion + ); + expect(options.agent).toHaveProperty('maxFreeSockets', 1024); + }); + }); + + describe('`idleSocketTimeout` option', () => { + it('sets the agent.timeout config value', () => { + const options = parseClientOptions( + createConfig({ idleSocketTimeout: duration(1000, 's') }), + false, + kibanaVersion + ); + expect(options.agent).toHaveProperty('timeout', 1_000_000); }); }); diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.ts index b0142ee0b1585..58660a9917348 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/client_config.ts @@ -9,11 +9,12 @@ import { ConnectionOptions as TlsConnectionOptions } from 'tls'; import { URL } from 'url'; import { Duration } from 'moment'; -import type { ClientOptions, HttpAgentOptions } from '@elastic/elasticsearch'; +import type { ClientOptions } from '@elastic/elasticsearch'; import type { ElasticsearchClientConfig } from '@kbn/core-elasticsearch-server'; +import { AgentOptions } from 'https'; import { getDefaultHeaders } from './headers'; -export type ParsedClientOptions = Omit & { agent: HttpAgentOptions }; +export type ParsedClientOptions = Omit & { agent: AgentOptions }; /** * Parse the client options from given client config and `scoped` flag. @@ -38,8 +39,10 @@ export function parseClientOptions( // fixes https://github.com/elastic/kibana/issues/101944 disablePrototypePoisoningProtection: true, agent: { - maxSockets: config.maxSockets, + maxTotalSockets: config.maxSockets, keepAlive: config.keepAlive ?? true, + timeout: getDurationAsMs(config.idleSocketTimeout), + maxFreeSockets: config.maxIdleSockets, }, compression: config.compression, }; diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.test.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.test.ts index e5be9fc0ab718..bcf5011395bf6 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.test.ts @@ -18,6 +18,7 @@ import type { ElasticsearchClientConfig } from '@kbn/core-elasticsearch-server'; import { ClusterClient } from './cluster_client'; import { DEFAULT_HEADERS, getDefaultHeaders } from './headers'; import { AgentManager } from './agent_manager'; +import { duration } from 'moment'; const createConfig = ( parts: Partial = {} @@ -27,6 +28,8 @@ const createConfig = ( sniffOnConnectionFault: false, sniffInterval: false, maxSockets: Infinity, + maxIdleSockets: 200, + idleSocketTimeout: duration('30s'), compression: false, requestHeadersWhitelist: ['authorization'], customHeaders: {}, @@ -46,7 +49,7 @@ describe('ClusterClient', () => { let authHeaders: ReturnType; let internalClient: jest.Mocked; let scopedClient: jest.Mocked; - let agentManager: AgentManager; + let agentFactoryProvider: AgentManager; const mockTransport = { mockTransport: true }; @@ -54,7 +57,7 @@ describe('ClusterClient', () => { logger = loggingSystemMock.createLogger(); internalClient = createClient(); scopedClient = createClient(); - agentManager = new AgentManager(); + agentFactoryProvider = new AgentManager(); authHeaders = httpServiceMock.createAuthHeaderStorage(); authHeaders.get.mockImplementation(() => ({ @@ -84,21 +87,21 @@ describe('ClusterClient', () => { authHeaders, type: 'custom-type', getExecutionContext: getExecutionContextMock, - agentManager, + agentFactoryProvider, kibanaVersion, }); expect(configureClientMock).toHaveBeenCalledTimes(2); expect(configureClientMock).toHaveBeenCalledWith(config, { logger, - agentManager, + agentFactoryProvider, kibanaVersion, type: 'custom-type', getExecutionContext: getExecutionContextMock, }); expect(configureClientMock).toHaveBeenCalledWith(config, { logger, - agentManager, + agentFactoryProvider, kibanaVersion, type: 'custom-type', getExecutionContext: getExecutionContextMock, @@ -113,7 +116,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -128,7 +131,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest(); @@ -155,7 +158,7 @@ describe('ClusterClient', () => { authHeaders, getExecutionContext, getUnauthorizedErrorHandler, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest(); @@ -179,7 +182,7 @@ describe('ClusterClient', () => { authHeaders, getExecutionContext, getUnauthorizedErrorHandler, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest(); @@ -212,7 +215,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest(); @@ -237,7 +240,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -271,7 +274,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({}); @@ -305,7 +308,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -344,7 +347,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({}); @@ -373,7 +376,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -410,7 +413,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({}); @@ -445,7 +448,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -482,7 +485,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest(); @@ -513,7 +516,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -547,7 +550,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = httpServerMock.createKibanaRequest({ @@ -579,7 +582,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = { @@ -612,7 +615,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); const request = { @@ -640,7 +643,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -658,7 +661,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -703,7 +706,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -720,7 +723,7 @@ describe('ClusterClient', () => { logger, type: 'custom-type', authHeaders, - agentManager, + agentFactoryProvider, kibanaVersion, }); diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.ts index f243c98ecf798..2a2f6ef1334a2 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/cluster_client.ts @@ -24,9 +24,12 @@ import type { ElasticsearchClientConfig } from '@kbn/core-elasticsearch-server'; import { configureClient } from './configure_client'; import { ScopedClusterClient } from './scoped_cluster_client'; import { getDefaultHeaders } from './headers'; -import { createInternalErrorHandler, InternalUnauthorizedErrorHandler } from './retry_unauthorized'; +import { + createInternalErrorHandler, + type InternalUnauthorizedErrorHandler, +} from './retry_unauthorized'; import { createTransport } from './create_transport'; -import { AgentManager } from './agent_manager'; +import type { AgentFactoryProvider } from './agent_manager'; const noop = () => undefined; @@ -49,7 +52,7 @@ export class ClusterClient implements ICustomClusterClient { authHeaders, getExecutionContext = noop, getUnauthorizedErrorHandler = noop, - agentManager, + agentFactoryProvider, kibanaVersion, }: { config: ElasticsearchClientConfig; @@ -58,7 +61,7 @@ export class ClusterClient implements ICustomClusterClient { authHeaders?: IAuthHeadersStorage; getExecutionContext?: () => string | undefined; getUnauthorizedErrorHandler?: () => UnauthorizedErrorHandler | undefined; - agentManager: AgentManager; + agentFactoryProvider: AgentFactoryProvider; kibanaVersion: string; }) { this.config = config; @@ -71,7 +74,7 @@ export class ClusterClient implements ICustomClusterClient { logger, type, getExecutionContext, - agentManager, + agentFactoryProvider, kibanaVersion, }); this.rootScopedClient = configureClient(config, { @@ -79,7 +82,7 @@ export class ClusterClient implements ICustomClusterClient { type, getExecutionContext, scoped: true, - agentManager, + agentFactoryProvider, kibanaVersion, }); } diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.test.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.test.ts index 40824d306ac48..fe511f46278d9 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.test.ts @@ -10,7 +10,6 @@ jest.mock('./log_query_and_deprecation', () => ({ __esModule: true, instrumentEsQueryAndDeprecationLogger: jest.fn(), })); -jest.mock('./agent_manager'); import { Agent } from 'http'; import { @@ -24,9 +23,8 @@ import { ClusterConnectionPool } from '@elastic/elasticsearch'; import type { ElasticsearchClientConfig } from '@kbn/core-elasticsearch-server'; import { configureClient } from './configure_client'; import { instrumentEsQueryAndDeprecationLogger } from './log_query_and_deprecation'; -import { AgentManager } from './agent_manager'; +import { type AgentFactoryProvider, AgentManager } from './agent_manager'; -const AgentManagerMock = AgentManager as jest.Mock; const kibanaVersion = '1.0.0'; const createFakeConfig = ( @@ -46,31 +44,17 @@ const createFakeClient = () => { return client; }; -const createFakeAgentFactory = (logger: MockedLogger) => { - const agentFactory = () => new Agent(); - - AgentManagerMock.mockImplementationOnce(() => { - const agentManager = new AgentManager(); - agentManager.getAgentFactory = () => agentFactory; - return agentManager; - }); - - const agentManager = new AgentManager(); - - return { agentManager, agentFactory }; -}; - describe('configureClient', () => { let logger: MockedLogger; let config: ElasticsearchClientConfig; - let agentManager: AgentManager; + let agentFactoryProvider: AgentFactoryProvider; beforeEach(() => { logger = loggingSystemMock.createLogger(); config = createFakeConfig(); parseClientOptionsMock.mockReturnValue({}); ClientMock.mockImplementation(() => createFakeClient()); - agentManager = new AgentManager(); + agentFactoryProvider = new AgentManager(); }); afterEach(() => { @@ -80,14 +64,26 @@ describe('configureClient', () => { }); it('calls `parseClientOptions` with the correct parameters', () => { - configureClient(config, { logger, type: 'test', scoped: false, agentManager, kibanaVersion }); + configureClient(config, { + logger, + type: 'test', + scoped: false, + agentFactoryProvider, + kibanaVersion, + }); expect(parseClientOptionsMock).toHaveBeenCalledTimes(1); expect(parseClientOptionsMock).toHaveBeenCalledWith(config, false, kibanaVersion); parseClientOptionsMock.mockClear(); - configureClient(config, { logger, type: 'test', scoped: true, agentManager, kibanaVersion }); + configureClient(config, { + logger, + type: 'test', + scoped: true, + agentFactoryProvider, + kibanaVersion, + }); expect(parseClientOptionsMock).toHaveBeenCalledTimes(1); expect(parseClientOptionsMock).toHaveBeenCalledWith(config, true, kibanaVersion); @@ -103,7 +99,7 @@ describe('configureClient', () => { logger, type: 'test', scoped: false, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -112,13 +108,17 @@ describe('configureClient', () => { expect(client).toBe(ClientMock.mock.results[0].value); }); - it('constructs a client using the provided `agentManager`', () => { - const { agentManager: customAgentManager, agentFactory } = createFakeAgentFactory(logger); + it('constructs a client using the provided `agentFactoryProvider`', () => { + const agentFactory = () => new Agent(); + const customAgentFactoryProvider = { + getAgentFactory: () => agentFactory, + }; + const client = configureClient(config, { logger, type: 'test', scoped: false, - agentManager: customAgentManager, + agentFactoryProvider: customAgentFactoryProvider, kibanaVersion, }); @@ -134,7 +134,7 @@ describe('configureClient', () => { type: 'test', scoped: false, getExecutionContext, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -148,7 +148,7 @@ describe('configureClient', () => { type: 'test', scoped: true, getExecutionContext, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -164,7 +164,7 @@ describe('configureClient', () => { logger, type: 'test', scoped: false, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -185,7 +185,7 @@ describe('configureClient', () => { logger, type: 'test', scoped: false, - agentManager, + agentFactoryProvider, kibanaVersion, }); @@ -203,7 +203,7 @@ describe('configureClient', () => { logger, type: 'test', scoped: false, - agentManager, + agentFactoryProvider, kibanaVersion, }); diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts index e1c8048c6a89e..2fd7a4d4a74bb 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-internal/src/configure_client.ts @@ -12,7 +12,7 @@ import type { ElasticsearchClientConfig } from '@kbn/core-elasticsearch-server'; import { parseClientOptions } from './client_config'; import { instrumentEsQueryAndDeprecationLogger } from './log_query_and_deprecation'; import { createTransport } from './create_transport'; -import { AgentManager } from './agent_manager'; +import type { AgentFactoryProvider } from './agent_manager'; const noop = () => undefined; @@ -23,14 +23,14 @@ export const configureClient = ( type, scoped = false, getExecutionContext = noop, - agentManager, + agentFactoryProvider, kibanaVersion, }: { logger: Logger; type: string; scoped?: boolean; getExecutionContext?: () => string | undefined; - agentManager: AgentManager; + agentFactoryProvider: AgentFactoryProvider; kibanaVersion: string; } ): Client => { @@ -38,7 +38,7 @@ export const configureClient = ( const KibanaTransport = createTransport({ getExecutionContext }); const client = new Client({ ...clientOptions, - agent: agentManager.getAgentFactory(clientOptions.agent), + agent: agentFactoryProvider.getAgentFactory(clientOptions.agent), Transport: KibanaTransport, Connection: HttpConnection, // using ClusterConnectionPool until https://github.com/elastic/elasticsearch-js/issues/1714 is addressed diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/index.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/index.ts index c46381d57a7b6..0b66d449df013 100644 --- a/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/index.ts +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/index.ts @@ -15,3 +15,4 @@ export type { DeeplyMockedApi, ElasticsearchClientMock, } from './src/mocks'; +export { createAgentStoreMock } from './src/agent_manager.mocks'; diff --git a/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/src/agent_manager.mocks.ts b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/src/agent_manager.mocks.ts new file mode 100644 index 0000000000000..2fd8812b3aae0 --- /dev/null +++ b/packages/core/elasticsearch/core-elasticsearch-client-server-mocks/src/agent_manager.mocks.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { AgentStore, NetworkAgent } from '@kbn/core-elasticsearch-client-server-internal'; + +export const createAgentStoreMock = (agents: Set = new Set()): AgentStore => ({ + getAgents: jest.fn(() => agents), +}); diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts index f8299533fecc4..b81683d1cc11f 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts @@ -36,7 +36,9 @@ test('set correct defaults', () => { "hosts": Array [ "http://localhost:9200", ], + "idleSocketTimeout": "PT1M", "ignoreVersionMismatch": false, + "maxIdleSockets": 256, "maxSockets": Infinity, "password": undefined, "pingTimeout": "PT30S", diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts index 8420d8d72346f..bc2ec78acac06 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts @@ -37,6 +37,8 @@ export const configSchema = schema.object({ defaultValue: 'http://localhost:9200', }), maxSockets: schema.number({ defaultValue: Infinity, min: 1 }), + maxIdleSockets: schema.number({ defaultValue: 256, min: 1 }), + idleSocketTimeout: schema.duration({ defaultValue: '60s' }), compression: schema.boolean({ defaultValue: false }), username: schema.maybe( schema.string({ @@ -304,6 +306,16 @@ export class ElasticsearchConfig implements IElasticsearchConfig { */ public readonly maxSockets: number; + /** + * The maximum number of idle sockets to keep open between Kibana and Elasticsearch. If more sockets become idle, they will be closed. + */ + public readonly maxIdleSockets: number; + + /** + * The timeout for idle sockets kept open between Kibana and Elasticsearch. If the socket is idle for longer than this timeout, it will be closed. + */ + public readonly idleSocketTimeout: Duration; + /** * Whether to use compression for communications with elasticsearch. */ @@ -409,6 +421,8 @@ export class ElasticsearchConfig implements IElasticsearchConfig { this.serviceAccountToken = rawConfig.serviceAccountToken; this.customHeaders = rawConfig.customHeaders; this.maxSockets = rawConfig.maxSockets; + this.maxIdleSockets = rawConfig.maxIdleSockets; + this.idleSocketTimeout = rawConfig.idleSocketTimeout; this.compression = rawConfig.compression; this.skipStartupConnectionCheck = rawConfig.skipStartupConnectionCheck; diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.mocks.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.mocks.ts index cd6d36f0cb111..68a56ff28bc8d 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.mocks.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.mocks.ts @@ -6,8 +6,14 @@ * Side Public License, v 1. */ +import type { AgentManager } from '@kbn/core-elasticsearch-client-server-internal'; + export const MockClusterClient = jest.fn(); -export const MockAgentManager = jest.fn(); +export const MockAgentManager: jest.MockedClass = jest.fn().mockReturnValue({ + getAgents: jest.fn(), + getAgentFactory: jest.fn(), +}); + jest.mock('@kbn/core-elasticsearch-client-server-internal', () => ({ ClusterClient: MockClusterClient, AgentManager: MockAgentManager, diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.ts index 5b54a2c35683e..ecd364b4283cf 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.test.ts @@ -135,7 +135,7 @@ describe('#preboot', () => { ); }); - it('creates a ClusterClient using the internal AgentManager', async () => { + it('creates a ClusterClient using the internal AgentManager as AgentFactoryProvider ', async () => { const prebootContract = await elasticsearchService.preboot(); const customConfig = { keepAlive: true }; const clusterClient = prebootContract.createClient('custom-type', customConfig); @@ -145,7 +145,7 @@ describe('#preboot', () => { expect(MockClusterClient).toHaveBeenCalledTimes(1); expect(MockClusterClient.mock.calls[0][0]).toEqual( // eslint-disable-next-line dot-notation - expect.objectContaining({ agentManager: elasticsearchService['agentManager'] }) + expect.objectContaining({ agentFactoryProvider: elasticsearchService['agentManager'] }) ); }); @@ -201,6 +201,11 @@ describe('#setup', () => { ); }); + it('returns an AgentStore as part of the contract', async () => { + const setupContract = await elasticsearchService.setup(setupDeps); + expect(typeof setupContract.agentStore.getAgents).toEqual('function'); + }); + it('esNodeVersionCompatibility$ only starts polling when subscribed to', async () => { const mockedClient = mockClusterClientInstance.asInternalUser; mockedClient.nodes.info.mockImplementation(() => diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.ts index f345732c7a7c4..fddff84293140 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_service.ts @@ -120,6 +120,7 @@ export class ElasticsearchService } this.unauthorizedErrorHandler = handler; }, + agentStore: this.agentManager, }; } @@ -182,7 +183,7 @@ export class ElasticsearchService authHeaders: this.authHeaders, getExecutionContext: () => this.executionContextClient?.getAsHeader(), getUnauthorizedErrorHandler: () => this.unauthorizedErrorHandler, - agentManager: this.agentManager, + agentFactoryProvider: this.agentManager, kibanaVersion: this.kibanaVersion, }); } diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts index 8d05ad0c4cd0a..b03b86c7bdd1c 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/types.ts @@ -12,6 +12,7 @@ import type { ElasticsearchServiceStart, ElasticsearchServiceSetup, } from '@kbn/core-elasticsearch-server'; +import type { AgentStore } from '@kbn/core-elasticsearch-client-server-internal'; import type { ServiceStatus } from '@kbn/core-status-common'; import type { NodesVersionCompatibility, NodeInfo } from './version_check/ensure_es_version'; import type { ClusterInfo } from './get_cluster_info'; @@ -21,6 +22,7 @@ export type InternalElasticsearchServicePreboot = ElasticsearchServicePreboot; /** @internal */ export interface InternalElasticsearchServiceSetup extends ElasticsearchServiceSetup { + agentStore: AgentStore; clusterInfo$: Observable; esNodesCompatibility$: Observable; status$: Observable>; diff --git a/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts b/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts index a1323be0ea71b..26d81da24318c 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts @@ -13,6 +13,7 @@ import { elasticsearchClientMock, type ClusterClientMock, type CustomClusterClientMock, + createAgentStoreMock, } from '@kbn/core-elasticsearch-client-server-mocks'; import type { ElasticsearchClientConfig, @@ -94,6 +95,7 @@ const createInternalSetupContractMock = () => { level: ServiceStatusLevels.available, summary: 'Elasticsearch is available', }), + agentStore: createAgentStoreMock(), }; return internalSetupContract; }; diff --git a/packages/core/elasticsearch/core-elasticsearch-server/src/client/client_config.ts b/packages/core/elasticsearch/core-elasticsearch-server/src/client/client_config.ts index 8c8fa6343e546..8bd5003e903a6 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server/src/client/client_config.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server/src/client/client_config.ts @@ -17,6 +17,8 @@ export interface ElasticsearchClientConfig { customHeaders: Record; requestHeadersWhitelist: string[]; maxSockets: number; + maxIdleSockets: number; + idleSocketTimeout: Duration; compression: boolean; sniffOnStart: boolean; sniffOnConnectionFault: boolean; diff --git a/packages/core/elasticsearch/core-elasticsearch-server/src/client/cluster_client.ts b/packages/core/elasticsearch/core-elasticsearch-server/src/client/cluster_client.ts index 57eadf70ef68a..a8e065d357ee1 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server/src/client/cluster_client.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server/src/client/cluster_client.ts @@ -7,8 +7,8 @@ */ import type { ElasticsearchClient } from './client'; -import { ScopeableRequest } from './scopeable_request'; -import { IScopedClusterClient } from './scoped_cluster_client'; +import type { ScopeableRequest } from './scopeable_request'; +import type { IScopedClusterClient } from './scoped_cluster_client'; /** * Represents an Elasticsearch cluster API client created by the platform. diff --git a/packages/core/elasticsearch/core-elasticsearch-server/src/elasticsearch_config.ts b/packages/core/elasticsearch/core-elasticsearch-server/src/elasticsearch_config.ts index a0cd6a34cca89..ae1dc281b29fb 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server/src/elasticsearch_config.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server/src/elasticsearch_config.ts @@ -32,6 +32,16 @@ export interface IElasticsearchConfig { */ readonly maxSockets: number; + /** + * The maximum number of idle sockets to keep open between Kibana and Elasticsearch. If more sockets become idle, they will be closed. + */ + readonly maxIdleSockets: number; + + /** + * The timeout for idle sockets kept open between Kibana and Elasticsearch. If the socket is idle for longer than this timeout, it will be closed. + */ + readonly idleSocketTimeout: Duration; + /** * Whether to use compression for communications with elasticsearch. */ diff --git a/packages/core/http/core-http-resources-server-internal/BUILD.bazel b/packages/core/http/core-http-resources-server-internal/BUILD.bazel new file mode 100644 index 0000000000000..8c286485efafb --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/BUILD.bazel @@ -0,0 +1,115 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-http-resources-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-http-resources-server-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//elastic-apm-node", + "//packages/kbn-logging", + "//packages/kbn-apm-config-loader", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//elastic-apm-node", + "//packages/kbn-apm-config-loader:npm_module_types", + "//packages/kbn-logging:npm_module_types", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/http/core-http-resources-server:npm_module_types", + "//packages/core/rendering/core-rendering-server-internal:npm_module_types", + "//packages/core/http/core-http-request-handler-context-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/http/core-http-resources-server-internal/README.md b/packages/core/http/core-http-resources-server-internal/README.md new file mode 100644 index 0000000000000..e1fa72ba6a294 --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-http-resources-server-internal + +This package contains the internal types and implementation for Core's internal `http` resources service. diff --git a/packages/core/http/core-http-resources-server-internal/index.ts b/packages/core/http/core-http-resources-server-internal/index.ts new file mode 100644 index 0000000000000..270425ba81f27 --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/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 { HttpResourcesService } from './src'; + +export type { InternalHttpResourcesPreboot, InternalHttpResourcesSetup } from './src'; diff --git a/packages/core/http/core-http-resources-server-internal/jest.config.js b/packages/core/http/core-http-resources-server-internal/jest.config.js new file mode 100644 index 0000000000000..2b328b864899a --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/http/core-http-resources-server-internal'], +}; diff --git a/packages/core/http/core-http-resources-server-internal/kibana.jsonc b/packages/core/http/core-http-resources-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..bea96a4a60844 --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-resources-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/http/core-http-resources-server-internal/package.json b/packages/core/http/core-http-resources-server-internal/package.json new file mode 100644 index 0000000000000..71e4a44a35504 --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-http-resources-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/http_resources/get_apm_config.test.mocks.ts b/packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.mocks.ts similarity index 100% rename from src/core/server/http_resources/get_apm_config.test.mocks.ts rename to packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.mocks.ts diff --git a/src/core/server/http_resources/get_apm_config.test.ts b/packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.ts similarity index 100% rename from src/core/server/http_resources/get_apm_config.test.ts rename to packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.ts diff --git a/src/core/server/http_resources/get_apm_config.ts b/packages/core/http/core-http-resources-server-internal/src/get_apm_config.ts similarity index 100% rename from src/core/server/http_resources/get_apm_config.ts rename to packages/core/http/core-http-resources-server-internal/src/get_apm_config.ts diff --git a/src/core/server/http_resources/http_resources_service.test.mocks.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.mocks.ts similarity index 100% rename from src/core/server/http_resources/http_resources_service.test.mocks.ts rename to packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.mocks.ts diff --git a/src/core/server/http_resources/http_resources_service.test.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts similarity index 91% rename from src/core/server/http_resources/http_resources_service.test.ts rename to packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts index ea04f30847508..9bf0de0eca829 100644 --- a/src/core/server/http_resources/http_resources_service.test.ts +++ b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts @@ -12,11 +12,13 @@ import type { RouteConfig } from '@kbn/core-http-server'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks'; -import { coreMock } from '../mocks'; import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; import { HttpResourcesService, PrebootDeps, SetupDeps } from './http_resources_service'; -import { httpResourcesMock } from './http_resources_service.mock'; -import { HttpResources } from '..'; +import type { HttpResources } from '@kbn/core-http-resources-server'; +import { + createCoreRequestHandlerContextMock, + createHttpResourcesResponseFactory, +} from './test_helpers/http_resources_service_test_mocks'; const coreContext = mockCoreContext.create(); @@ -26,7 +28,7 @@ describe('HttpResources service', () => { let setupDeps: SetupDeps; let router: ReturnType; const kibanaRequest = httpServerMock.createKibanaRequest(); - const context = coreMock.createCustomRequestHandlerContext({}); + const context = createCoreRequestHandlerContextMock(); const apmConfig = { mockApmConfig: true }; beforeEach(() => { @@ -66,7 +68,7 @@ describe('HttpResources service', () => { }); const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(getDeps().rendering.render).toHaveBeenCalledWith( kibanaRequest, @@ -92,7 +94,7 @@ describe('HttpResources service', () => { const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ @@ -112,7 +114,7 @@ describe('HttpResources service', () => { }); const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(getDeps().rendering.render).toHaveBeenCalledWith( kibanaRequest, @@ -138,7 +140,7 @@ describe('HttpResources service', () => { const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ @@ -159,7 +161,7 @@ describe('HttpResources service', () => { }); const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ body: htmlBody, @@ -186,7 +188,7 @@ describe('HttpResources service', () => { const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ @@ -208,7 +210,7 @@ describe('HttpResources service', () => { }); const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ body: jsBody, @@ -235,7 +237,7 @@ describe('HttpResources service', () => { const [[, routeHandler]] = router.get.mock.calls; - const responseFactory = httpResourcesMock.createResponseFactory(); + const responseFactory = createHttpResourcesResponseFactory(); await routeHandler(context, kibanaRequest, responseFactory); expect(responseFactory.ok).toHaveBeenCalledWith({ diff --git a/src/core/server/http_resources/http_resources_service.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts similarity index 96% rename from src/core/server/http_resources/http_resources_service.ts rename to packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts index 7cc88699ea7ba..1032611748d36 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts @@ -23,21 +23,29 @@ import type { InternalRenderingServiceSetup, } from '@kbn/core-rendering-server-internal'; import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; -import { - InternalHttpResourcesSetup, +import type { HttpResources, HttpResourcesResponseOptions, HttpResourcesRenderOptions, HttpResourcesRequestHandler, HttpResourcesServiceToolkit, -} from './types'; +} from '@kbn/core-http-resources-server'; + +import type { InternalHttpResourcesSetup } from './types'; + import { getApmConfig } from './get_apm_config'; +/** + * @internal + */ export interface PrebootDeps { http: InternalHttpServicePreboot; rendering: InternalRenderingServicePreboot; } +/** + * @internal + */ export interface SetupDeps { http: InternalHttpServiceSetup; rendering: InternalRenderingServiceSetup; diff --git a/packages/core/http/core-http-resources-server-internal/src/index.ts b/packages/core/http/core-http-resources-server-internal/src/index.ts new file mode 100644 index 0000000000000..b4c1502a92cab --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/src/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { HttpResourcesService } from './http_resources_service'; + +export type { InternalHttpResourcesPreboot, InternalHttpResourcesSetup } from './types'; diff --git a/packages/core/http/core-http-resources-server-internal/src/test_helpers/http_resources_service_test_mocks.ts b/packages/core/http/core-http-resources-server-internal/src/test_helpers/http_resources_service_test_mocks.ts new file mode 100644 index 0000000000000..c9c0251355012 --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/src/test_helpers/http_resources_service_test_mocks.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { HttpResourcesServiceToolkit } from '@kbn/core-http-resources-server'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; + +// partial duplicate of coreMock +export function createCoreRequestHandlerContextMock() { + return { + core: { + uiSettings: { client: uiSettingsServiceMock.createClient() }, + }, + }; +} + +// duplicate of public mock for internal testing only +export function createHttpResourcesResponseFactory() { + const mocked: jest.Mocked = { + renderCoreApp: jest.fn(), + renderAnonymousCoreApp: jest.fn(), + renderHtml: jest.fn(), + renderJs: jest.fn(), + }; + + return { + ...httpServerMock.createResponseFactory(), + ...mocked, + }; +} diff --git a/packages/core/http/core-http-resources-server-internal/src/types.ts b/packages/core/http/core-http-resources-server-internal/src/types.ts new file mode 100644 index 0000000000000..f68520a6387bb --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/src/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { IRouter } from '@kbn/core-http-server'; +import type { HttpResources } from '@kbn/core-http-resources-server'; + +/** + * Allows to configure HTTP response parameters + * @internal + */ +export interface InternalHttpResourcesPreboot { + createRegistrar(router: IRouter): HttpResources; +} + +/** + * Allows to configure HTTP response parameters + * @internal + */ +export type InternalHttpResourcesSetup = InternalHttpResourcesPreboot; diff --git a/packages/core/http/core-http-resources-server-internal/tsconfig.json b/packages/core/http/core-http-resources-server-internal/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/http/core-http-resources-server-internal/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/http/core-http-resources-server-mocks/BUILD.bazel b/packages/core/http/core-http-resources-server-mocks/BUILD.bazel new file mode 100644 index 0000000000000..81eefd0db2ee2 --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/BUILD.bazel @@ -0,0 +1,110 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-http-resources-server-mocks" +PKG_REQUIRE_NAME = "@kbn/core-http-resources-server-mocks" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/core/http/core-http-server-mocks", + "//packages/core/http/core-http-resources-server-internal", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-utility-types:npm_module_types", + "//packages/core/http/core-http-server-mocks:npm_module_types", + "//packages/core/http/core-http-resources-server:npm_module_types", + "//packages/core/http/core-http-resources-server-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/http/core-http-resources-server-mocks/README.md b/packages/core/http/core-http-resources-server-mocks/README.md new file mode 100644 index 0000000000000..6d9bd3de0dad2 --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/README.md @@ -0,0 +1,3 @@ +# @kbn/core-http-resources-server-mocks + +This package contains the mocks for Core's internal `http` resources service. diff --git a/packages/core/http/core-http-resources-server-mocks/index.ts b/packages/core/http/core-http-resources-server-mocks/index.ts new file mode 100644 index 0000000000000..9b848c2bd32a1 --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { httpResourcesMock } from './src/http_resources_server.mock'; +export type { HttpResourcesMock } from './src/http_resources_server.mock'; diff --git a/packages/core/http/core-http-resources-server-mocks/jest.config.js b/packages/core/http/core-http-resources-server-mocks/jest.config.js new file mode 100644 index 0000000000000..7241454e43f5a --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/http/core-http-resources-server-mocks'], +}; diff --git a/packages/core/http/core-http-resources-server-mocks/kibana.jsonc b/packages/core/http/core-http-resources-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..e8703bdd42aa3 --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-resources-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/http/core-http-resources-server-mocks/package.json b/packages/core/http/core-http-resources-server-mocks/package.json new file mode 100644 index 0000000000000..47247cd2abaf5 --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-http-resources-server-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/http_resources/http_resources_service.mock.ts b/packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts similarity index 65% rename from src/core/server/http_resources/http_resources_service.mock.ts rename to packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts index 6c50d720ceab2..aec435e650893 100644 --- a/src/core/server/http_resources/http_resources_service.mock.ts +++ b/packages/core/http/core-http-resources-server-mocks/src/http_resources_server.mock.ts @@ -7,7 +7,25 @@ */ import { httpServerMock } from '@kbn/core-http-server-mocks'; -import { HttpResources, HttpResourcesServiceToolkit } from './types'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import { HttpResourcesService } from '@kbn/core-http-resources-server-internal'; +import type { HttpResources, HttpResourcesServiceToolkit } from '@kbn/core-http-resources-server'; + +export type HttpResourcesMock = jest.Mocked>; + +function createHttpResourcesService() { + const mock: HttpResourcesMock = { + preboot: jest.fn(), + setup: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + }; + + mock.preboot.mockReturnValue(createInternalHttpResourcesPreboot()); + mock.setup.mockReturnValue(createInternalHttpResourcesSetup()); + + return mock; +} const createHttpResourcesMock = (): jest.Mocked => ({ register: jest.fn(), @@ -38,6 +56,7 @@ function createHttpResourcesResponseFactory() { } export const httpResourcesMock = { + create: createHttpResourcesService, createRegistrar: createHttpResourcesMock, createPrebootContract: createInternalHttpResourcesPreboot, createSetupContract: createInternalHttpResourcesSetup, diff --git a/packages/core/http/core-http-resources-server-mocks/tsconfig.json b/packages/core/http/core-http-resources-server-mocks/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/http/core-http-resources-server-mocks/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/http/core-http-resources-server/BUILD.bazel b/packages/core/http/core-http-resources-server/BUILD.bazel new file mode 100644 index 0000000000000..16583b6801b4a --- /dev/null +++ b/packages/core/http/core-http-resources-server/BUILD.bazel @@ -0,0 +1,107 @@ +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-http-resources-server" +PKG_REQUIRE_NAME = "@kbn/core-http-resources-server" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/http/core-http-request-handler-context-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/http/core-http-resources-server/README.md b/packages/core/http/core-http-resources-server/README.md new file mode 100644 index 0000000000000..a2f4321f19214 --- /dev/null +++ b/packages/core/http/core-http-resources-server/README.md @@ -0,0 +1,3 @@ +# @kbn/core-http-resources-server + +This package contains the public types for Core's HTTP resources service. diff --git a/packages/core/http/core-http-resources-server/index.ts b/packages/core/http/core-http-resources-server/index.ts new file mode 100644 index 0000000000000..c4dbd3842b2ee --- /dev/null +++ b/packages/core/http/core-http-resources-server/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { + HttpResourcesRenderOptions, + HttpResourcesResponseOptions, + HttpResourcesServiceToolkit, + HttpResourcesRequestHandler, + HttpResources, +} from './src'; diff --git a/packages/core/http/core-http-resources-server/jest.config.js b/packages/core/http/core-http-resources-server/jest.config.js new file mode 100644 index 0000000000000..bfa504fb873d5 --- /dev/null +++ b/packages/core/http/core-http-resources-server/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/http/core-http-resources-server'], +}; diff --git a/packages/core/http/core-http-resources-server/kibana.jsonc b/packages/core/http/core-http-resources-server/kibana.jsonc new file mode 100644 index 0000000000000..a05c1223a7817 --- /dev/null +++ b/packages/core/http/core-http-resources-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-resources-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/http/core-http-resources-server/package.json b/packages/core/http/core-http-resources-server/package.json new file mode 100644 index 0000000000000..156bc4c8948b4 --- /dev/null +++ b/packages/core/http/core-http-resources-server/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-http-resources-server", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/http_resources/index.ts b/packages/core/http/core-http-resources-server/src/index.ts similarity index 80% rename from src/core/server/http_resources/index.ts rename to packages/core/http/core-http-resources-server/src/index.ts index 9148c08a922e9..8fbe0e3c1a5e2 100644 --- a/src/core/server/http_resources/index.ts +++ b/packages/core/http/core-http-resources-server/src/index.ts @@ -6,14 +6,10 @@ * Side Public License, v 1. */ -export { HttpResourcesService } from './http_resources_service'; - export type { HttpResourcesRenderOptions, HttpResourcesResponseOptions, HttpResourcesServiceToolkit, HttpResourcesRequestHandler, HttpResources, - InternalHttpResourcesPreboot, - InternalHttpResourcesSetup, } from './types'; diff --git a/src/core/server/http_resources/types.ts b/packages/core/http/core-http-resources-server/src/types.ts similarity index 91% rename from src/core/server/http_resources/types.ts rename to packages/core/http/core-http-resources-server/src/types.ts index 246a7394c3ade..6d2e6728e28f6 100644 --- a/src/core/server/http_resources/types.ts +++ b/packages/core/http/core-http-resources-server/src/types.ts @@ -7,7 +7,6 @@ */ import type { - IRouter, RouteConfig, IKibanaResponse, ResponseHeaders, @@ -85,20 +84,6 @@ export type HttpResourcesRequestHandler< Context extends RequestHandlerContext = RequestHandlerContext > = RequestHandler; -/** - * Allows to configure HTTP response parameters - * @internal - */ -export interface InternalHttpResourcesPreboot { - createRegistrar(router: IRouter): HttpResources; -} - -/** - * Allows to configure HTTP response parameters - * @internal - */ -export type InternalHttpResourcesSetup = InternalHttpResourcesPreboot; - /** * HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. * Provides API allowing plug-ins to respond with: diff --git a/packages/core/http/core-http-resources-server/tsconfig.json b/packages/core/http/core-http-resources-server/tsconfig.json new file mode 100644 index 0000000000000..71bb40fe57f3f --- /dev/null +++ b/packages/core/http/core-http-resources-server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/BUILD.bazel b/packages/core/metrics/core-metrics-collectors-server-internal/BUILD.bazel index 2b789e97cbe69..9761bcbf1cefb 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/BUILD.bazel +++ b/packages/core/metrics/core-metrics-collectors-server-internal/BUILD.bazel @@ -39,6 +39,8 @@ RUNTIME_DEPS = [ "//packages/kbn-logging", "@npm//moment", "@npm//getos", + ### test dependencies + "//packages/core/elasticsearch/core-elasticsearch-client-server-mocks", ] TYPES_DEPS = [ @@ -50,6 +52,7 @@ TYPES_DEPS = [ "@npm//@types/hapi__hapi", "//packages/kbn-logging:npm_module_types", "//packages/core/metrics/core-metrics-server:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-client-server-internal:npm_module_types", ] jsts_transpiler( diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/index.ts b/packages/core/metrics/core-metrics-collectors-server-internal/index.ts index a4639202353e1..351129cdc8ba3 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/index.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/index.ts @@ -11,3 +11,4 @@ export type { OpsMetricsCollectorOptions } from './src/os'; export { ProcessMetricsCollector } from './src/process'; export { ServerMetricsCollector } from './src/server'; export { EventLoopDelaysMonitor } from './src/event_loop_delays_monitor'; +export { ElasticsearchClientsMetricsCollector } from './src/elasticsearch_client'; diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.test.ts new file mode 100644 index 0000000000000..363fca6430dbe --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.test.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 { Agent as HttpAgent } from 'http'; +import { Agent as HttpsAgent } from 'https'; +import { sampleEsClientMetrics } from '@kbn/core-metrics-server-mocks'; +import { createAgentStoreMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { getAgentsSocketsStatsMock } from './get_agents_sockets_stats.test.mocks'; +import { ElasticsearchClientsMetricsCollector } from './elasticsearch_client'; +import { getAgentsSocketsStats } from './get_agents_sockets_stats'; + +jest.mock('@kbn/core-elasticsearch-client-server-internal'); + +describe('ElasticsearchClientsMetricsCollector', () => { + test('#collect calls getAgentsSocketsStats with the Agents managed by the provided AgentManager', async () => { + const agents = new Set([new HttpAgent(), new HttpsAgent()]); + const agentStore = createAgentStoreMock(agents); + getAgentsSocketsStatsMock.mockReturnValueOnce(sampleEsClientMetrics); + + const esClientsMetricsCollector = new ElasticsearchClientsMetricsCollector(agentStore); + const metrics = await esClientsMetricsCollector.collect(); + + expect(agentStore.getAgents).toHaveBeenCalledTimes(1); + expect(getAgentsSocketsStats).toHaveBeenCalledTimes(1); + expect(getAgentsSocketsStats).toHaveBeenNthCalledWith(1, agents); + expect(metrics).toEqual(sampleEsClientMetrics); + }); +}); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts new file mode 100644 index 0000000000000..278fd0218f8c0 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/elasticsearch_client.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ElasticsearchClientsMetrics, MetricsCollector } from '@kbn/core-metrics-server'; +import type { AgentStore } from '@kbn/core-elasticsearch-client-server-internal'; +import { getAgentsSocketsStats } from './get_agents_sockets_stats'; + +export class ElasticsearchClientsMetricsCollector + implements MetricsCollector +{ + constructor(private readonly agentStore: AgentStore) {} + + public async collect(): Promise { + return await getAgentsSocketsStats(this.agentStore.getAgents()); + } + + public reset() { + // we do not have a state in this Collector, aka metrics are not accumulated over time. + // Thus, we don't need to perform any cleanup to reset the collected metrics + } +} diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.mocks.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.mocks.ts new file mode 100644 index 0000000000000..4e9688ccc91b9 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.mocks.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Agent as HttpAgent } from 'http'; +import { Agent as HttpsAgent } from 'https'; + +import { getAgentsSocketsStats } from './get_agents_sockets_stats'; + +export const getHttpAgentMock = (overrides: Partial) => { + return Object.assign(new HttpAgent(), overrides); +}; + +export const getHttpsAgentMock = (overrides: Partial) => { + return Object.assign(new HttpsAgent(), overrides); +}; + +export const getAgentsSocketsStatsMock: jest.MockedFunction = + jest.fn(); + +jest.doMock('./get_agents_sockets_stats', () => { + return { + getAgentsSocketsStats: getAgentsSocketsStatsMock, + }; +}); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.ts new file mode 100644 index 0000000000000..513bf2caa8545 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.test.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Socket } from 'net'; +import { Agent, IncomingMessage } from 'http'; +import { getAgentsSocketsStats } from './get_agents_sockets_stats'; +import { getHttpAgentMock, getHttpsAgentMock } from './get_agents_sockets_stats.test.mocks'; + +jest.mock('net'); + +const mockSocket = new Socket(); +const mockIncomingMessage = new IncomingMessage(mockSocket); + +describe('getAgentsSocketsStats()', () => { + it('extracts aggregated stats from the specified agents', () => { + const agent1 = getHttpAgentMock({ + sockets: { + node1: [mockSocket, mockSocket, mockSocket], + node2: [mockSocket], + }, + freeSockets: { + node1: [mockSocket], + node3: [mockSocket, mockSocket, mockSocket, mockSocket], + }, + requests: { + node1: [mockIncomingMessage, mockIncomingMessage], + }, + }); + + const agent2 = getHttpAgentMock({ + sockets: { + node1: [mockSocket, mockSocket, mockSocket], + node4: [mockSocket], + }, + freeSockets: { + node3: [mockSocket, mockSocket, mockSocket, mockSocket], + }, + requests: { + node4: [mockIncomingMessage, mockIncomingMessage, mockIncomingMessage, mockIncomingMessage], + }, + }); + + const stats = getAgentsSocketsStats(new Set([agent1, agent2])); + expect(stats).toEqual({ + averageActiveSocketsPerNode: 2.6666666666666665, + averageIdleSocketsPerNode: 4.5, + connectedNodes: 4, + mostActiveNodeSockets: 6, + mostIdleNodeSockets: 8, + nodesWithActiveSockets: 3, + nodesWithIdleSockets: 2, + protocol: 'http', + totalActiveSockets: 8, + totalIdleSockets: 9, + totalQueuedRequests: 6, + }); + }); + + it('takes into account Agent types to determine the `protocol`', () => { + const httpAgent = getHttpAgentMock({ + sockets: { node1: [mockSocket] }, + freeSockets: {}, + requests: {}, + }); + + const httpsAgent = getHttpsAgentMock({ + sockets: { node1: [mockSocket] }, + freeSockets: {}, + requests: {}, + }); + + const noAgents = new Set(); + const httpAgents = new Set([httpAgent, httpAgent]); + const httpsAgents = new Set([httpsAgent, httpsAgent]); + const mixedAgents = new Set([httpAgent, httpsAgent]); + + expect(getAgentsSocketsStats(noAgents).protocol).toEqual('none'); + expect(getAgentsSocketsStats(httpAgents).protocol).toEqual('http'); + expect(getAgentsSocketsStats(httpsAgents).protocol).toEqual('https'); + expect(getAgentsSocketsStats(mixedAgents).protocol).toEqual('mixed'); + }); + + it('does not take into account those Agents that have not had any connection to any node', () => { + const pristineAgentProps = { + sockets: {}, + freeSockets: {}, + requests: {}, + }; + const agent1 = getHttpAgentMock(pristineAgentProps); + const agent2 = getHttpAgentMock(pristineAgentProps); + const agent3 = getHttpAgentMock(pristineAgentProps); + + const stats = getAgentsSocketsStats(new Set([agent1, agent2, agent3])); + + expect(stats).toEqual({ + averageActiveSocketsPerNode: 0, + averageIdleSocketsPerNode: 0, + connectedNodes: 0, + mostActiveNodeSockets: 0, + mostIdleNodeSockets: 0, + nodesWithActiveSockets: 0, + nodesWithIdleSockets: 0, + protocol: 'none', + totalActiveSockets: 0, + totalIdleSockets: 0, + totalQueuedRequests: 0, + }); + }); + + it('takes into account those Agents that have hold mappings to one or more nodes, but that do not currently have any pending requests, active connections or idle connections', () => { + const emptyAgentProps = { + sockets: { + node1: [], + }, + freeSockets: { + node2: [], + }, + requests: { + node3: [], + }, + }; + + const agent1 = getHttpAgentMock(emptyAgentProps); + const agent2 = getHttpAgentMock(emptyAgentProps); + + const stats = getAgentsSocketsStats(new Set([agent1, agent2])); + + expect(stats).toEqual({ + averageActiveSocketsPerNode: 0, + averageIdleSocketsPerNode: 0, + connectedNodes: 3, + mostActiveNodeSockets: 0, + mostIdleNodeSockets: 0, + nodesWithActiveSockets: 0, + nodesWithIdleSockets: 0, + protocol: 'http', + totalActiveSockets: 0, + totalIdleSockets: 0, + totalQueuedRequests: 0, + }); + }); +}); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.ts new file mode 100644 index 0000000000000..e28c92a56a8a4 --- /dev/null +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/get_agents_sockets_stats.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { NetworkAgent } from '@kbn/core-elasticsearch-client-server-internal'; +import { Agent as HttpsAgent } from 'https'; +import { mean } from 'lodash'; +import type { + ElasticsearchClientProtocol, + ElasticsearchClientsMetrics, +} from '@kbn/core-metrics-server'; + +export const getAgentsSocketsStats = (agents: Set): ElasticsearchClientsMetrics => { + const nodes = new Set(); + let totalActiveSockets = 0; + let totalIdleSockets = 0; + let totalQueuedRequests = 0; + let http: boolean = false; + let https: boolean = false; + + const nodesWithActiveSockets: Record = {}; + const nodesWithIdleSockets: Record = {}; + + agents.forEach((agent) => { + const agentRequests = Object.entries(agent.requests) ?? []; + const agentSockets = Object.entries(agent.sockets) ?? []; + const agentFreeSockets = Object.entries(agent.freeSockets) ?? []; + + if (agentRequests.length || agentSockets.length || agentFreeSockets.length) { + if (agent instanceof HttpsAgent) https = true; + else http = true; + + agentRequests.forEach(([node, queue]) => { + nodes.add(node); + totalQueuedRequests += queue?.length ?? 0; + }); + + agentSockets.forEach(([node, sockets]) => { + nodes.add(node); + const activeSockets = sockets?.length ?? 0; + totalActiveSockets += activeSockets; + nodesWithActiveSockets[node] = (nodesWithActiveSockets[node] ?? 0) + activeSockets; + }); + + agentFreeSockets.forEach(([node, freeSockets]) => { + nodes.add(node); + const idleSockets = freeSockets?.length ?? 0; + totalIdleSockets += idleSockets; + nodesWithIdleSockets[node] = (nodesWithIdleSockets[node] ?? 0) + idleSockets; + }); + } + }); + + const activeSocketCounters = Object.values(nodesWithActiveSockets); + const idleSocketCounters = Object.values(nodesWithIdleSockets); + const protocol: ElasticsearchClientProtocol = http + ? https + ? 'mixed' + : 'http' + : https + ? 'https' + : 'none'; + + return { + protocol, + connectedNodes: nodes.size, + nodesWithActiveSockets: activeSocketCounters.filter(Boolean).length, + nodesWithIdleSockets: idleSocketCounters.filter(Boolean).length, + totalActiveSockets, + totalIdleSockets, + totalQueuedRequests, + mostActiveNodeSockets: activeSocketCounters.length ? Math.max(...activeSocketCounters) : 0, + averageActiveSocketsPerNode: activeSocketCounters.length ? mean(activeSocketCounters) : 0, + mostIdleNodeSockets: idleSocketCounters.length ? Math.max(...idleSocketCounters) : 0, + averageIdleSocketsPerNode: idleSocketCounters.length ? mean(idleSocketCounters) : 0, + }; +}; diff --git a/packages/core/metrics/core-metrics-server-internal/BUILD.bazel b/packages/core/metrics/core-metrics-server-internal/BUILD.bazel index da7883016afd2..0a7f393ec0b31 100644 --- a/packages/core/metrics/core-metrics-server-internal/BUILD.bazel +++ b/packages/core/metrics/core-metrics-server-internal/BUILD.bazel @@ -37,11 +37,15 @@ NPM_MODULE_EXTRA_FILES = [ RUNTIME_DEPS = [ "@npm//rxjs", "@npm//moment", - "//packages/kbn-logging-mocks", "//packages/kbn-config-schema", - "//packages/core/http/core-http-server-mocks", "//packages/core/metrics/core-metrics-collectors-server-internal", + "//packages/core/elasticsearch/core-elasticsearch-server-internal", + ### test dependencies + "//packages/kbn-logging-mocks", + "//packages/core/http/core-http-server-mocks", + "//packages/core/metrics/core-metrics-server-mocks", "//packages/core/metrics/core-metrics-collectors-server-mocks", + "//packages/core/elasticsearch/core-elasticsearch-server-mocks", ] @@ -57,6 +61,7 @@ TYPES_DEPS = [ "//packages/core/http/core-http-server-internal:npm_module_types", "//packages/core/metrics/core-metrics-server:npm_module_types", "//packages/core/metrics/core-metrics-collectors-server-internal:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-server-internal:npm_module_types", ] diff --git a/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts b/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts index 8de7a5fa5dadf..d997433667e27 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.test.ts @@ -8,6 +8,7 @@ import type { OpsMetrics } from '@kbn/core-metrics-server'; import { getEcsOpsMetricsLog } from './get_ops_metrics_log'; +import { sampleEsClientMetrics } from '@kbn/core-metrics-server-mocks'; import { collectorMock } from '@kbn/core-metrics-collectors-server-mocks'; function createBaseOpsMetrics(): OpsMetrics { @@ -24,6 +25,7 @@ function createBaseOpsMetrics(): OpsMetrics { memory: { total_in_bytes: 1, free_in_bytes: 1, used_in_bytes: 1 }, uptime_in_millis: 1, }, + elasticsearch_client: sampleEsClientMetrics, response_times: { avg_in_millis: 1, max_in_millis: 1 }, requests: { disconnects: 1, total: 1, statusCodes: { '200': 1 } }, concurrent_connections: 1, diff --git a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts index de78b534b2dc7..351e2aca43f56 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts @@ -8,13 +8,15 @@ import moment from 'moment'; +import { take } from 'rxjs/operators'; import { configServiceMock } from '@kbn/config-mocks'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { mockOpsCollector } from './metrics_service.test.mocks'; import { MetricsService } from './metrics_service'; -import { take } from 'rxjs/operators'; +import { OpsMetricsCollector } from './ops_metrics_collector'; const testInterval = 100; @@ -24,6 +26,7 @@ const logger = loggingSystemMock.create(); describe('MetricsService', () => { const httpMock = httpServiceMock.createInternalSetupContract(); + const esServiceMock = elasticsearchServiceMock.createInternalSetup(); let metricsService: MetricsService; beforeEach(() => { @@ -43,9 +46,16 @@ describe('MetricsService', () => { describe('#start', () => { it('invokes setInterval with the configured interval', async () => { - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); await metricsService.start(); + expect(OpsMetricsCollector).toHaveBeenCalledTimes(1); + expect(OpsMetricsCollector).toHaveBeenCalledWith( + httpMock.server, + esServiceMock.agentStore, + expect.objectContaining({ logger: logger.get('metrics') }) + ); + expect(setInterval).toHaveBeenCalledTimes(1); expect(setInterval).toHaveBeenCalledWith(expect.any(Function), testInterval); }); @@ -53,7 +63,7 @@ describe('MetricsService', () => { it('collects the metrics at every interval', async () => { mockOpsCollector.collect.mockResolvedValue(dummyMetrics); - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); await metricsService.start(); expect(mockOpsCollector.collect).toHaveBeenCalledTimes(1); @@ -68,7 +78,7 @@ describe('MetricsService', () => { it('resets the collector after each collection', async () => { mockOpsCollector.collect.mockResolvedValue(dummyMetrics); - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); const { getOpsMetrics$ } = await metricsService.start(); // `advanceTimersByTime` only ensure the interval handler is executed @@ -108,7 +118,7 @@ describe('MetricsService', () => { .mockResolvedValueOnce(firstMetrics) .mockResolvedValueOnce(secondMetrics); - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); const { getOpsMetrics$ } = await metricsService.start(); const nextEmission = async () => { @@ -157,7 +167,7 @@ describe('MetricsService', () => { mockOpsCollector.collect .mockResolvedValueOnce(firstMetrics) .mockResolvedValueOnce(secondMetrics); - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); const { getOpsMetrics$ } = await metricsService.start(); const nextEmission = async () => { @@ -176,7 +186,7 @@ describe('MetricsService', () => { it('omits metrics from log message if they are missing or malformed', async () => { const opsLogger = logger.get('metrics', 'ops'); mockOpsCollector.collect.mockResolvedValueOnce({ secondMetrics: 'metrics' }); - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); await metricsService.start(); expect(loggingSystemMock.collect(opsLogger).debug[0]).toMatchInlineSnapshot(` Array [ @@ -219,7 +229,7 @@ describe('MetricsService', () => { describe('#stop', () => { it('stops the metrics interval', async () => { - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); const { getOpsMetrics$ } = await metricsService.start(); expect(mockOpsCollector.collect).toHaveBeenCalledTimes(1); @@ -235,7 +245,7 @@ describe('MetricsService', () => { }); it('completes the metrics observable', async () => { - await metricsService.setup({ http: httpMock }); + await metricsService.setup({ http: httpMock, elasticsearchService: esServiceMock }); const { getOpsMetrics$ } = await metricsService.start(); let completed = false; diff --git a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts index 8a05b4b57843c..95a9dc09bba57 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.ts @@ -10,6 +10,7 @@ import { firstValueFrom, ReplaySubject } from 'rxjs'; import type { CoreContext, CoreService } from '@kbn/core-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import type { InternalElasticsearchServiceSetup } from '@kbn/core-elasticsearch-server-internal'; import type { OpsMetrics, MetricsServiceSetup, @@ -21,6 +22,7 @@ import { getEcsOpsMetricsLog } from './logging'; export interface MetricsServiceSetupDeps { http: InternalHttpServiceSetup; + elasticsearchService: InternalElasticsearchServiceSetup; } /** @internal */ @@ -45,12 +47,15 @@ export class MetricsService this.opsMetricsLogger = coreContext.logger.get('metrics', 'ops'); } - public async setup({ http }: MetricsServiceSetupDeps): Promise { + public async setup({ + http, + elasticsearchService, + }: MetricsServiceSetupDeps): Promise { const config = await firstValueFrom( this.coreContext.configService.atPath(OPS_CONFIG_PATH) ); - this.metricsCollector = new OpsMetricsCollector(http.server, { + this.metricsCollector = new OpsMetricsCollector(http.server, elasticsearchService.agentStore, { logger: this.logger, ...config.cGroupOverrides, }); diff --git a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.mocks.ts b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.mocks.ts index b96449fdc2f64..d70753b9f4644 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.mocks.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.mocks.ts @@ -11,11 +11,13 @@ import { collectorMock } from '@kbn/core-metrics-collectors-server-mocks'; export const mockOsCollector = collectorMock.create(); export const mockProcessCollector = collectorMock.create(); export const mockServerCollector = collectorMock.create(); +export const mockEsClientCollector = collectorMock.create(); jest.doMock('@kbn/core-metrics-collectors-server-internal', () => { return { OsMetricsCollector: jest.fn().mockImplementation(() => mockOsCollector), ProcessMetricsCollector: jest.fn().mockImplementation(() => mockProcessCollector), ServerMetricsCollector: jest.fn().mockImplementation(() => mockServerCollector), + ElasticsearchClientsMetricsCollector: jest.fn().mockImplementation(() => mockEsClientCollector), }; }); diff --git a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.ts b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.ts index cd80c35b37f86..87011a663404f 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.test.ts @@ -8,7 +8,10 @@ import { loggerMock } from '@kbn/logging-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; +import { sampleEsClientMetrics } from '@kbn/core-metrics-server-mocks'; +import { AgentManager } from '@kbn/core-elasticsearch-client-server-internal'; import { + mockEsClientCollector, mockOsCollector, mockProcessCollector, mockServerCollector, @@ -20,7 +23,8 @@ describe('OpsMetricsCollector', () => { beforeEach(() => { const hapiServer = httpServiceMock.createInternalSetupContract().server; - collector = new OpsMetricsCollector(hapiServer, { logger: loggerMock.create() }); + const agentManager = new AgentManager(); + collector = new OpsMetricsCollector(hapiServer, agentManager, { logger: loggerMock.create() }); mockOsCollector.collect.mockResolvedValue('osMetrics'); }); @@ -33,12 +37,14 @@ describe('OpsMetricsCollector', () => { requests: 'serverRequestsMetrics', response_times: 'serverTimingMetrics', }); + mockEsClientCollector.collect.mockResolvedValue(sampleEsClientMetrics); const metrics = await collector.collect(); expect(mockOsCollector.collect).toHaveBeenCalledTimes(1); expect(mockProcessCollector.collect).toHaveBeenCalledTimes(1); expect(mockServerCollector.collect).toHaveBeenCalledTimes(1); + expect(mockEsClientCollector.collect).toHaveBeenCalledTimes(1); expect(metrics).toEqual({ collected_at: expect.any(Date), @@ -47,6 +53,7 @@ describe('OpsMetricsCollector', () => { os: 'osMetrics', requests: 'serverRequestsMetrics', response_times: 'serverTimingMetrics', + elasticsearch_client: sampleEsClientMetrics, }); }); }); diff --git a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts index 10958d93c2562..8a10f4071b11b 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts @@ -8,28 +8,33 @@ import { Server as HapiServer } from '@hapi/hapi'; import type { OpsMetrics, MetricsCollector } from '@kbn/core-metrics-server'; +import type { AgentStore } from '@kbn/core-elasticsearch-client-server-internal'; import { ProcessMetricsCollector, OsMetricsCollector, type OpsMetricsCollectorOptions, ServerMetricsCollector, + ElasticsearchClientsMetricsCollector, } from '@kbn/core-metrics-collectors-server-internal'; export class OpsMetricsCollector implements MetricsCollector { private readonly processCollector: ProcessMetricsCollector; private readonly osCollector: OsMetricsCollector; private readonly serverCollector: ServerMetricsCollector; + private readonly esClientCollector: ElasticsearchClientsMetricsCollector; - constructor(server: HapiServer, opsOptions: OpsMetricsCollectorOptions) { + constructor(server: HapiServer, agentStore: AgentStore, opsOptions: OpsMetricsCollectorOptions) { this.processCollector = new ProcessMetricsCollector(); this.osCollector = new OsMetricsCollector(opsOptions); this.serverCollector = new ServerMetricsCollector(server); + this.esClientCollector = new ElasticsearchClientsMetricsCollector(agentStore); } public async collect(): Promise { - const [processes, os, server] = await Promise.all([ + const [processes, os, esClient, server] = await Promise.all([ this.processCollector.collect(), this.osCollector.collect(), + this.esClientCollector.collect(), this.serverCollector.collect(), ]); @@ -43,6 +48,7 @@ export class OpsMetricsCollector implements MetricsCollector { process: processes[0], processes, os, + elasticsearch_client: esClient, ...server, }; } diff --git a/packages/core/metrics/core-metrics-server-mocks/index.ts b/packages/core/metrics/core-metrics-server-mocks/index.ts index d252b2253243e..02d13b8ed5ad8 100644 --- a/packages/core/metrics/core-metrics-server-mocks/index.ts +++ b/packages/core/metrics/core-metrics-server-mocks/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { metricsServiceMock } from './src/metrics_service.mock'; +export { metricsServiceMock, sampleEsClientMetrics } from './src/metrics_service.mock'; diff --git a/packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts b/packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts index 6bbe176ce37e8..44601caeaa85c 100644 --- a/packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts +++ b/packages/core/metrics/core-metrics-server-mocks/src/metrics_service.mock.ts @@ -17,7 +17,25 @@ import { mocked as eventLoopDelaysMonitorMock, collectorMock, } from '@kbn/core-metrics-collectors-server-mocks'; -import type { MetricsServiceSetup, MetricsServiceStart } from '@kbn/core-metrics-server'; +import type { + ElasticsearchClientsMetrics, + MetricsServiceSetup, + MetricsServiceStart, +} from '@kbn/core-metrics-server'; + +export const sampleEsClientMetrics: ElasticsearchClientsMetrics = { + protocol: 'https', + connectedNodes: 3, + nodesWithActiveSockets: 3, + nodesWithIdleSockets: 1, + totalActiveSockets: 25, + totalIdleSockets: 2, + totalQueuedRequests: 0, + mostActiveNodeSockets: 15, + averageActiveSocketsPerNode: 8, + mostIdleNodeSockets: 2, + averageIdleSocketsPerNode: 0.5, +}; const createInternalSetupContractMock = () => { const setupContract: jest.Mocked = { @@ -39,6 +57,7 @@ const createInternalSetupContractMock = () => { memory: { total_in_bytes: 1, free_in_bytes: 1, used_in_bytes: 1 }, uptime_in_millis: 1, }, + elasticsearch_client: sampleEsClientMetrics, response_times: { avg_in_millis: 1, max_in_millis: 1 }, requests: { disconnects: 1, total: 1, statusCodes: { '200': 1 } }, concurrent_connections: 1, diff --git a/packages/core/metrics/core-metrics-server/index.ts b/packages/core/metrics/core-metrics-server/index.ts index 51e0b7fe3d95d..49bd2a4251623 100644 --- a/packages/core/metrics/core-metrics-server/index.ts +++ b/packages/core/metrics/core-metrics-server/index.ts @@ -14,4 +14,6 @@ export type { OpsProcessMetrics, OpsOsMetrics, OpsServerMetrics, + ElasticsearchClientProtocol, + ElasticsearchClientsMetrics, } from './src/metrics'; diff --git a/packages/core/metrics/core-metrics-server/src/metrics.ts b/packages/core/metrics/core-metrics-server/src/metrics.ts index dbfa643c8eccc..958f6b75f55e4 100644 --- a/packages/core/metrics/core-metrics-server/src/metrics.ts +++ b/packages/core/metrics/core-metrics-server/src/metrics.ts @@ -40,6 +40,44 @@ export interface IntervalHistogram { }; } +/** + * Protocol(s) used by the Elasticsearch Client + * @public + */ + +export type ElasticsearchClientProtocol = 'none' | 'http' | 'https' | 'mixed'; + +/** + * Metrics related to the elasticsearch clients + * @public + */ +export interface ElasticsearchClientsMetrics { + /** The protocol (or protocols) that these Agents are using */ + protocol: ElasticsearchClientProtocol; + /** Number of ES nodes that ES-js client is connecting to */ + connectedNodes: number; + /** Number of nodes with active connections */ + nodesWithActiveSockets: number; + /** Number of nodes with available connections (alive but idle). + * Note that a node can have both active and idle connections at the same time + */ + nodesWithIdleSockets: number; + /** Total number of active sockets (all nodes, all connections) */ + totalActiveSockets: number; + /** Total number of available sockets (alive but idle, all nodes, all connections) */ + totalIdleSockets: number; + /** Total number of queued requests (all nodes, all connections) */ + totalQueuedRequests: number; + /** Number of active connections of the node with most active connections */ + mostActiveNodeSockets: number; + /** Average of active sockets per node (all connections) */ + averageActiveSocketsPerNode: number; + /** Number of idle connections of the node with most idle connections */ + mostIdleNodeSockets: number; + /** Average of available (idle) sockets per node (all connections) */ + averageIdleSocketsPerNode: number; +} + /** * Process related metrics * @public @@ -165,6 +203,10 @@ export interface OpsServerMetrics { export interface OpsMetrics { /** Time metrics were recorded at. */ collected_at: Date; + /** + * Metrics related to the elasticsearch client + */ + elasticsearch_client: ElasticsearchClientsMetrics; /** * Process related metrics. * @deprecated use the processes field instead. diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts index 0739c9acab8f5..d9a65f984c222 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts @@ -4182,6 +4182,10 @@ describe('SavedObjectsRepository', () => { type: 'foo', id: '1', }, + hasNoReference: { + type: 'bar', + id: '1', + }, }; it(`passes mappings, registry, and search options to getSearchDsl`, async () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts index 5569141c7fa0e..f48e031bd23c4 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts @@ -1129,6 +1129,8 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { rootSearchFields, hasReference, hasReferenceOperator, + hasNoReference, + hasNoReferenceOperator, page = FIND_DEFAULT_PAGE, perPage = FIND_DEFAULT_PER_PAGE, pit, @@ -1235,6 +1237,8 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { typeToNamespacesMap, hasReference, hasReferenceOperator, + hasNoReference, + hasNoReferenceOperator, kueryNode, }), }, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.test.ts index c502665468e6c..20ce3a2f46b2e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.test.ts @@ -195,17 +195,18 @@ describe('#getQueryParams', () => { }); }); - describe('reference filter clause', () => { - describe('`hasReference` parameter', () => { - it('does not call `getReferencesFilter` when `hasReference` is not specified', () => { - getQueryParams({ - registry, - hasReference: undefined, - }); - - expect(getReferencesFilterMock).not.toHaveBeenCalled(); + describe('reference/noreference filter clause', () => { + it('does not call `getReferencesFilter` when neither `hasReference` nor `hasNoReference` are specified', () => { + getQueryParams({ + registry, + hasReference: undefined, + hasNoReference: undefined, }); + expect(getReferencesFilterMock).not.toHaveBeenCalled(); + }); + + describe('`hasReference` parameter', () => { it('calls `getReferencesFilter` with the correct parameters', () => { const hasReference = { id: 'foo', type: 'bar' }; getQueryParams({ @@ -235,6 +236,38 @@ describe('#getQueryParams', () => { expect(filters.some((filter) => filter.references_filter === true)).toBeDefined(); }); }); + + describe('`hasNoReference` parameter', () => { + it('calls `getReferencesFilter` with the correct parameters', () => { + const hasNoReference = { id: 'noFoo', type: 'bar' }; + getQueryParams({ + registry, + hasNoReference, + hasNoReferenceOperator: 'AND', + }); + + expect(getReferencesFilterMock).toHaveBeenCalledTimes(1); + expect(getReferencesFilterMock).toHaveBeenCalledWith({ + must: false, + references: [hasNoReference], + operator: 'AND', + }); + }); + + it('includes the return of `getReferencesFilter` in the `filter` clause', () => { + getReferencesFilterMock.mockReturnValue({ references_filter: true }); + + const hasNoReference = { id: 'noFoo', type: 'bar' }; + const result = getQueryParams({ + registry, + hasNoReference, + hasReferenceOperator: 'AND', + }); + + const filters: any[] = result.query.bool.filter; + expect(filters.some((filter) => filter.references_filter === true)).toBeDefined(); + }); + }); }); describe('type filter clauses', () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.ts index 669f2a273569b..896b934c90b80 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/query_params.ts @@ -7,6 +7,7 @@ */ import * as esKuery from '@kbn/es-query'; +import type { SavedObjectTypeIdTuple } from '@kbn/core-saved-objects-common'; type KueryNode = any; @@ -123,11 +124,6 @@ function getClauseForType( }; } -export interface HasReferenceQueryParams { - type: string; - id: string; -} - export type SearchOperator = 'AND' | 'OR'; interface QueryParams { @@ -139,8 +135,10 @@ interface QueryParams { defaultSearchOperator?: SearchOperator; searchFields?: string[]; rootSearchFields?: string[]; - hasReference?: HasReferenceQueryParams | HasReferenceQueryParams[]; + hasReference?: SavedObjectTypeIdTuple | SavedObjectTypeIdTuple[]; hasReferenceOperator?: SearchOperator; + hasNoReference?: SavedObjectTypeIdTuple | SavedObjectTypeIdTuple[]; + hasNoReferenceOperator?: SearchOperator; kueryNode?: KueryNode; } @@ -148,6 +146,13 @@ interface QueryParams { const uniqNamespaces = (namespacesToNormalize?: string[]) => namespacesToNormalize ? Array.from(new Set(namespacesToNormalize)) : undefined; +const toArray = (val: unknown) => { + if (typeof val === 'undefined') { + return val; + } + return !Array.isArray(val) ? [val] : val; +}; + /** * Get the "query" related keys for the search body */ @@ -162,6 +167,8 @@ export function getQueryParams({ defaultSearchOperator, hasReference, hasReferenceOperator, + hasNoReference, + hasNoReferenceOperator, kueryNode, }: QueryParams) { const types = getTypes( @@ -169,9 +176,8 @@ export function getQueryParams({ typeToNamespacesMap ? Array.from(typeToNamespacesMap.keys()) : type ); - if (hasReference && !Array.isArray(hasReference)) { - hasReference = [hasReference]; - } + hasReference = toArray(hasReference); + hasNoReference = toArray(hasNoReference); const bool: any = { filter: [ @@ -184,6 +190,15 @@ export function getQueryParams({ }), ] : []), + ...(hasNoReference?.length + ? [ + getReferencesFilter({ + references: hasNoReference, + operator: hasNoReferenceOperator, + must: false, + }), + ] + : []), { bool: { should: types.map((shouldType) => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.test.ts index 9a042579c8e8f..127f3a94edd21 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.test.ts @@ -20,29 +20,141 @@ describe('getReferencesFilter', () => { }, }); - describe('when using the `OR` operator', () => { - it('generates one `should` clause per type of reference', () => { + describe('for "must" match clauses', () => { + describe('when using the `OR` operator', () => { + it('generates one `should` clause per type of reference', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'foo', id: 'foo-3' }, + { type: 'bar', id: 'bar-1' }, + { type: 'bar', id: 'bar-2' }, + ]; + const clause = getReferencesFilter({ + references, + operator: 'OR', + }); + + expect(clause).toEqual({ + bool: { + should: [ + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-1', 'foo-2', 'foo-3'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { term: { 'references.type': 'bar' } }, + ]), + ], + minimum_should_match: 1, + }, + }); + }); + + it('does not include more than `maxTermsPerClause` per `terms` clauses', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'foo', id: 'foo-3' }, + { type: 'foo', id: 'foo-4' }, + { type: 'foo', id: 'foo-5' }, + { type: 'bar', id: 'bar-1' }, + { type: 'bar', id: 'bar-2' }, + { type: 'bar', id: 'bar-3' }, + { type: 'dolly', id: 'dolly-1' }, + ]; + const clause = getReferencesFilter({ + references, + operator: 'OR', + maxTermsPerClause: 2, + }); + + expect(clause).toEqual({ + bool: { + should: [ + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-1', 'foo-2'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-3', 'foo-4'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-5'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { term: { 'references.type': 'bar' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-3'] } }, + { term: { 'references.type': 'bar' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['dolly-1'] } }, + { term: { 'references.type': 'dolly' } }, + ]), + ], + minimum_should_match: 1, + }, + }); + }); + }); + + describe('when using the `AND` operator', () => { + it('generates one `must` clause per reference', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'bar', id: 'bar-1' }, + ]; + + const clause = getReferencesFilter({ + references, + operator: 'AND', + }); + + expect(clause).toEqual({ + bool: { + must: references.map((ref) => ({ + nested: { + path: 'references', + query: { + bool: { + must: [ + { term: { 'references.id': ref.id } }, + { term: { 'references.type': ref.type } }, + ], + }, + }, + }, + })), + }, + }); + }); + }); + + it('defaults to using the `OR` operator', () => { const references = [ { type: 'foo', id: 'foo-1' }, - { type: 'foo', id: 'foo-2' }, - { type: 'foo', id: 'foo-3' }, { type: 'bar', id: 'bar-1' }, - { type: 'bar', id: 'bar-2' }, ]; const clause = getReferencesFilter({ references, - operator: 'OR', }); expect(clause).toEqual({ bool: { should: [ nestedRefMustClauses([ - { terms: { 'references.id': ['foo-1', 'foo-2', 'foo-3'] } }, + { terms: { 'references.id': ['foo-1'] } }, { term: { 'references.type': 'foo' } }, ]), nestedRefMustClauses([ - { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { terms: { 'references.id': ['bar-1'] } }, { term: { 'references.type': 'bar' } }, ]), ], @@ -50,115 +162,156 @@ describe('getReferencesFilter', () => { }, }); }); + }); + + describe('for "must_not" match clauses', () => { + describe('when using the `OR` operator', () => { + it('generates one `must_not` clause per type of reference', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'foo', id: 'foo-3' }, + { type: 'bar', id: 'bar-1' }, + { type: 'bar', id: 'bar-2' }, + ]; + const clause = getReferencesFilter({ + references, + operator: 'OR', + must: false, + }); + + expect(clause).toEqual({ + bool: { + must_not: [ + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-1', 'foo-2', 'foo-3'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { term: { 'references.type': 'bar' } }, + ]), + ], + }, + }); + }); + + it('does not include more than `maxTermsPerClause` per `terms` clauses', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'foo', id: 'foo-3' }, + { type: 'foo', id: 'foo-4' }, + { type: 'foo', id: 'foo-5' }, + { type: 'bar', id: 'bar-1' }, + { type: 'bar', id: 'bar-2' }, + { type: 'bar', id: 'bar-3' }, + { type: 'dolly', id: 'dolly-1' }, + ]; + const clause = getReferencesFilter({ + references, + operator: 'OR', + maxTermsPerClause: 2, + must: false, + }); + + expect(clause).toEqual({ + bool: { + must_not: [ + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-1', 'foo-2'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-3', 'foo-4'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['foo-5'] } }, + { term: { 'references.type': 'foo' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { term: { 'references.type': 'bar' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['bar-3'] } }, + { term: { 'references.type': 'bar' } }, + ]), + nestedRefMustClauses([ + { terms: { 'references.id': ['dolly-1'] } }, + { term: { 'references.type': 'dolly' } }, + ]), + ], + }, + }); + }); + }); - it('does not include mode than `maxTermsPerClause` per `terms` clauses', () => { + describe('when using the `AND` operator', () => { + it('generates one `must` clause per reference', () => { + const references = [ + { type: 'foo', id: 'foo-1' }, + { type: 'foo', id: 'foo-2' }, + { type: 'bar', id: 'bar-1' }, + ]; + + const clause = getReferencesFilter({ + references, + operator: 'AND', + must: false, + }); + + expect(clause).toEqual({ + bool: { + must_not: [ + { + bool: { + must: references.map((ref) => ({ + nested: { + path: 'references', + query: { + bool: { + must: [ + { term: { 'references.id': ref.id } }, + { term: { 'references.type': ref.type } }, + ], + }, + }, + }, + })), + }, + }, + ], + }, + }); + }); + }); + + it('defaults to using the `OR` operator', () => { const references = [ { type: 'foo', id: 'foo-1' }, - { type: 'foo', id: 'foo-2' }, - { type: 'foo', id: 'foo-3' }, - { type: 'foo', id: 'foo-4' }, - { type: 'foo', id: 'foo-5' }, { type: 'bar', id: 'bar-1' }, - { type: 'bar', id: 'bar-2' }, - { type: 'bar', id: 'bar-3' }, - { type: 'dolly', id: 'dolly-1' }, ]; const clause = getReferencesFilter({ references, - operator: 'OR', - maxTermsPerClause: 2, + must: false, }); expect(clause).toEqual({ bool: { - should: [ - nestedRefMustClauses([ - { terms: { 'references.id': ['foo-1', 'foo-2'] } }, - { term: { 'references.type': 'foo' } }, - ]), + must_not: [ nestedRefMustClauses([ - { terms: { 'references.id': ['foo-3', 'foo-4'] } }, + { terms: { 'references.id': ['foo-1'] } }, { term: { 'references.type': 'foo' } }, ]), nestedRefMustClauses([ - { terms: { 'references.id': ['foo-5'] } }, - { term: { 'references.type': 'foo' } }, - ]), - nestedRefMustClauses([ - { terms: { 'references.id': ['bar-1', 'bar-2'] } }, + { terms: { 'references.id': ['bar-1'] } }, { term: { 'references.type': 'bar' } }, ]), - nestedRefMustClauses([ - { terms: { 'references.id': ['bar-3'] } }, - { term: { 'references.type': 'bar' } }, - ]), - nestedRefMustClauses([ - { terms: { 'references.id': ['dolly-1'] } }, - { term: { 'references.type': 'dolly' } }, - ]), ], - minimum_should_match: 1, - }, - }); - }); - }); - - describe('when using the `AND` operator', () => { - it('generates one `must` clause per reference', () => { - const references = [ - { type: 'foo', id: 'foo-1' }, - { type: 'foo', id: 'foo-2' }, - { type: 'bar', id: 'bar-1' }, - ]; - - const clause = getReferencesFilter({ - references, - operator: 'AND', - }); - - expect(clause).toEqual({ - bool: { - must: references.map((ref) => ({ - nested: { - path: 'references', - query: { - bool: { - must: [ - { term: { 'references.id': ref.id } }, - { term: { 'references.type': ref.type } }, - ], - }, - }, - }, - })), }, }); }); }); - - it('defaults to using the `OR` operator', () => { - const references = [ - { type: 'foo', id: 'foo-1' }, - { type: 'bar', id: 'bar-1' }, - ]; - const clause = getReferencesFilter({ - references, - }); - - expect(clause).toEqual({ - bool: { - should: [ - nestedRefMustClauses([ - { terms: { 'references.id': ['foo-1'] } }, - { term: { 'references.type': 'foo' } }, - ]), - nestedRefMustClauses([ - { terms: { 'references.id': ['bar-1'] } }, - { term: { 'references.type': 'bar' } }, - ]), - ], - minimum_should_match: 1, - }, - }); - }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.ts index b0849560d2e43..4dd6bc640f174 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/references_filter.ts @@ -6,35 +6,61 @@ * Side Public License, v 1. */ -import type { HasReferenceQueryParams, SearchOperator } from './query_params'; +import type { SavedObjectTypeIdTuple } from '@kbn/core-saved-objects-common'; + +import type { SearchOperator } from './query_params'; export function getReferencesFilter({ references, operator = 'OR', maxTermsPerClause = 1000, + must = true, }: { - references: HasReferenceQueryParams[]; + references: SavedObjectTypeIdTuple[]; operator?: SearchOperator; maxTermsPerClause?: number; + must?: boolean; }) { if (operator === 'AND') { + if (must) { + return { + bool: { + must: references.map(getNestedTermClauseForReference), + }, + }; + } + return { bool: { - must: references.map(getNestedTermClauseForReference), + must_not: [ + { + bool: { + must: references.map(getNestedTermClauseForReference), + }, + }, + ], }, }; } else { + if (must) { + return { + bool: { + should: getAggregatedTermsClauses(references, maxTermsPerClause), + minimum_should_match: 1, + }, + }; + } + return { bool: { - should: getAggregatedTermsClauses(references, maxTermsPerClause), - minimum_should_match: 1, + must_not: getAggregatedTermsClauses(references, maxTermsPerClause), }, }; } } const getAggregatedTermsClauses = ( - references: HasReferenceQueryParams[], + references: SavedObjectTypeIdTuple[], maxTermsPerClause: number ) => { const refTypeToIds = references.reduce((map, { type, id }) => { @@ -58,7 +84,7 @@ const createChunks = (array: T[], chunkSize: number): T[][] => { return chunks; }; -export const getNestedTermClauseForReference = (reference: HasReferenceQueryParams) => { +export const getNestedTermClauseForReference = (reference: SavedObjectTypeIdTuple) => { return { nested: { path: 'references', diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.test.ts index d1ed7251b2414..84ef7c232d775 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.test.ts @@ -49,7 +49,7 @@ describe('getSearchDsl', () => { }); describe('passes control', () => { - it('passes (mappings, schema, namespaces, type, typeToNamespacesMap, search, searchFields, rootSearchFields, hasReference, hasReferenceOperator) to getQueryParams', () => { + it('passes (mappings, schema, namespaces, type, typeToNamespacesMap, search, searchFields, rootSearchFields, hasReference, hasReferenceOperator, hasNoReference, hasNoReferenceOperator) to getQueryParams', () => { const opts = { namespaces: ['foo-namespace'], type: 'foo', @@ -63,6 +63,11 @@ describe('getSearchDsl', () => { id: '1', }, hasReferenceOperator: 'AND' as queryParamsNS.SearchOperator, + hasNoReference: { + type: 'noBar', + id: '1', + }, + hasNoReferenceOperator: 'AND' as queryParamsNS.SearchOperator, }; getSearchDsl(mappings, registry, opts); @@ -78,6 +83,8 @@ describe('getSearchDsl', () => { defaultSearchOperator: opts.defaultSearchOperator, hasReference: opts.hasReference, hasReferenceOperator: opts.hasReferenceOperator, + hasNoReference: opts.hasNoReference, + hasNoReferenceOperator: opts.hasNoReferenceOperator, }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.ts index 980bf800755b9..381f20069d25a 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/search_dsl/search_dsl.ts @@ -12,7 +12,8 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SavedObjectsPitParams } from '@kbn/core-saved-objects-api-server'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; -import { getQueryParams, HasReferenceQueryParams, SearchOperator } from './query_params'; +import type { SavedObjectTypeIdTuple } from '@kbn/core-saved-objects-common'; +import { getQueryParams, SearchOperator } from './query_params'; import { getPitParams } from './pit_params'; import { getSortingParams } from './sorting_params'; @@ -30,8 +31,10 @@ interface GetSearchDslOptions { namespaces?: string[]; pit?: SavedObjectsPitParams; typeToNamespacesMap?: Map; - hasReference?: HasReferenceQueryParams | HasReferenceQueryParams[]; + hasReference?: SavedObjectTypeIdTuple | SavedObjectTypeIdTuple[]; hasReferenceOperator?: SearchOperator; + hasNoReference?: SavedObjectTypeIdTuple | SavedObjectTypeIdTuple[]; + hasNoReferenceOperator?: SearchOperator; kueryNode?: KueryNode; } @@ -54,6 +57,8 @@ export function getSearchDsl( typeToNamespacesMap, hasReference, hasReferenceOperator, + hasNoReference, + hasNoReferenceOperator, kueryNode, } = options; @@ -77,6 +82,8 @@ export function getSearchDsl( defaultSearchOperator, hasReference, hasReferenceOperator, + hasNoReference, + hasNoReferenceOperator, kueryNode, }), ...getSortingParams(mappings, type, sortField, sortOrder), diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts index 49042029f334b..a50506c96c8e5 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts @@ -66,11 +66,23 @@ export interface SavedObjectsFindOptions { * Use `hasReferenceOperator` to specify the operator to use when searching for multiple references. */ hasReference?: SavedObjectsFindOptionsReference | SavedObjectsFindOptionsReference[]; + /** * The operator to use when searching by multiple references using the `hasReference` option. Defaults to `OR` */ hasReferenceOperator?: 'AND' | 'OR'; + /** + * Search for documents *not* having a reference to the specified objects. + * Use `hasNoReferenceOperator` to specify the operator to use when searching for multiple references. + */ + hasNoReference?: SavedObjectsFindOptionsReference | SavedObjectsFindOptionsReference[]; + + /** + * The operator to use when searching by multiple references using the `hasNoReference` option. Defaults to `OR` + */ + hasNoReferenceOperator?: 'AND' | 'OR'; + /** * The search operator to use with the provided filter. Defaults to `OR` */ diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts index 6c2966ee9775f..7825b09cf29bd 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts @@ -612,6 +612,7 @@ describe('SavedObjectsClient', () => { defaultSearchOperator: 'OR' as const, fields: ['title'], hasReference: { id: '1', type: 'reference' }, + hasNoReference: { id: '1', type: 'reference' }, page: 10, perPage: 100, search: 'what is the meaning of life?|life', @@ -633,6 +634,7 @@ describe('SavedObjectsClient', () => { "fields": Array [ "title", ], + "has_no_reference": "{\\"id\\":\\"1\\",\\"type\\":\\"reference\\"}", "has_reference": "{\\"id\\":\\"1\\",\\"type\\":\\"reference\\"}", "page": 10, "per_page": 100, diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts index dd2feed58123f..1fd111186f551 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts @@ -292,6 +292,8 @@ export class SavedObjectsClient implements SavedObjectsClientContract { fields: 'fields', hasReference: 'has_reference', hasReferenceOperator: 'has_reference_operator', + hasNoReference: 'has_no_reference', + hasNoReferenceOperator: 'has_no_reference_operator', page: 'page', perPage: 'per_page', search: 'search', @@ -315,6 +317,9 @@ export class SavedObjectsClient implements SavedObjectsClientContract { if (query.has_reference) { query.has_reference = JSON.stringify(query.has_reference); } + if (query.has_no_reference) { + query.has_no_reference = JSON.stringify(query.has_no_reference); + } // `aggs` is a structured object. we need to stringify it before sending it, as `fetch` // is not doing it implicitly. diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts index 4587cb1ebeb09..983b31caf7a2b 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts @@ -45,6 +45,10 @@ export const registerFindRoute = ( schema.oneOf([referenceSchema, schema.arrayOf(referenceSchema)]) ), has_reference_operator: searchOperatorSchema, + has_no_reference: schema.maybe( + schema.oneOf([referenceSchema, schema.arrayOf(referenceSchema)]) + ), + has_no_reference_operator: searchOperatorSchema, fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), filter: schema.maybe(schema.string()), aggs: schema.maybe(schema.string()), @@ -88,6 +92,8 @@ export const registerFindRoute = ( sortField: query.sort_field, hasReference: query.has_reference, hasReferenceOperator: query.has_reference_operator, + hasNoReference: query.has_no_reference, + hasNoReferenceOperator: query.has_no_reference_operator, fields: typeof query.fields === 'string' ? [query.fields] : query.fields, filter: query.filter, aggs, diff --git a/packages/core/status/core-status-server-internal/src/routes/status.ts b/packages/core/status/core-status-server-internal/src/routes/status.ts index 34a5a9b4dcd20..199f55159a7c6 100644 --- a/packages/core/status/core-status-server-internal/src/routes/status.ts +++ b/packages/core/status/core-status-server-internal/src/routes/status.ts @@ -135,6 +135,7 @@ export const registerStatusRoute = ({ ...lastMetrics.requests, status_codes: lastMetrics.requests.statusCodes, }, + elasticsearch_client: lastMetrics.elasticsearch_client, }, }; 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/aggregators/service_metrics_aggregator.ts b/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts index 618c9e52b9f2c..9ed6f805d1546 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts @@ -34,21 +34,19 @@ export type ServiceFields = Fields & | 'service.name' | 'service.version' | 'service.environment' - | 'transaction.type' > & Partial<{ _doc_count: number; transaction: { - duration: { - summary: { - min: number; - max: number; - sum: number; - value_count: number; - }; - }; failure_count: number; success_count: number; + type: string; + 'duration.summary': { + min: number; + max: number; + sum: number; + value_count: number; + }; }; }>; @@ -86,10 +84,10 @@ export class ServicMetricsAggregator implements StreamAggregator { }, }, failure_count: { - type: { type: 'long' }, + type: 'long', }, success_count: { - type: { type: 'long' }, + type: 'long', }, }, }, @@ -141,22 +139,24 @@ export class ServicMetricsAggregator implements StreamAggregator { } const state = this.state[key]; - state.count++; - - switch (event['event.outcome']) { - case 'failure': - state.failure_count++; - break; - case 'success': - state.success_count++; - break; - } const duration = Number(event['transaction.duration.us']); + if (duration >= 0) { + state.count++; + state.sum += duration; if (duration > state.max) state.max = duration; if (duration < state.min) state.min = Math.min(0, duration); + + switch (event['event.outcome']) { + case 'failure': + state.failure_count++; + break; + case 'success': + state.success_count++; + break; + } } }; @@ -197,18 +197,16 @@ export class ServicMetricsAggregator implements StreamAggregator { 'processor.event': 'metric', 'service.name': state['service.name'], 'service.environment': state['service.environment'], - 'transaction.type': state['transaction.type'], transaction: { - duration: { - summary: { - min: state.min, - max: state.max, - sum: state.sum, - value_count: state.count, - }, + 'duration.summary': { + min: state.min, + max: state.max, + sum: state.sum, + value_count: state.count, }, - failure_count: state.failure_count, success_count: state.success_count, + failure_count: state.failure_count, + type: state['transaction.type'] ?? 'request', }, }; } 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 3a0b89c1f0d1b..b7a3326942582 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -130,6 +130,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { crawlerGettingStarted: `${ENTERPRISE_SEARCH_DOCS}crawler-getting-started.html`, crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`, crawlerOverview: `${ENTERPRISE_SEARCH_DOCS}crawler.html`, + deployTrainedModels: `${MACHINE_LEARNING_DOCS}ml-nlp-deploy-models.html`, documentLevelSecurity: `${ELASTICSEARCH_DOCS}document-level-security.html`, ingestPipelines: `${ENTERPRISE_SEARCH_DOCS}ingest-pipelines.html`, languageAnalyzers: `${ELASTICSEARCH_DOCS}analysis-lang-analyzer.html`, @@ -363,6 +364,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { eventFilters: `${SECURITY_SOLUTION_DOCS}event-filters.html`, blocklist: `${SECURITY_SOLUTION_DOCS}blocklist.html`, threatIntelInt: `${SECURITY_SOLUTION_DOCS}es-threat-intel-integrations.html`, + endpointArtifacts: `${SECURITY_SOLUTION_DOCS}endpoint-artifacts.html`, policyResponseTroubleshooting: { full_disk_access: `${SECURITY_SOLUTION_DOCS}deploy-elastic-endpoint.html#enable-fda-endpoint`, macos_system_ext: `${SECURITY_SOLUTION_DOCS}deploy-elastic-endpoint.html#system-extension-endpoint`, @@ -373,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`, @@ -456,6 +459,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { userExperience: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/user-experience.html`, createAlerts: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/create-alerts.html`, syntheticsCommandReference: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/synthetics-configuration.html#synthetics-configuration-playwright-options`, + syntheticsProjectMonitors: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/synthetic-run-tests.html#synthetic-monitor-choose-project`, }, alerting: { guide: `${KIBANA_DOCS}create-and-manage-rules.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 7cd785ee194fa..16a80e7f7bf33 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -115,6 +115,7 @@ export interface DocLinks { readonly crawlerGettingStarted: string; readonly crawlerManaging: string; readonly crawlerOverview: string; + readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly ingestPipelines: string; readonly languageAnalyzers: string; @@ -265,6 +266,7 @@ export interface DocLinks { readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; + readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; @@ -275,6 +277,7 @@ export interface DocLinks { }; readonly threatIntelInt: string; readonly responseActions: string; + readonly configureEndpointIntegrationPolicy: string; }; readonly query: { readonly eql: string; @@ -338,6 +341,7 @@ export interface DocLinks { userExperience: string; createAlerts: string; syntheticsCommandReference: string; + syntheticsProjectMonitors: string; }>; readonly alerting: Record; readonly maps: Readonly<{ diff --git a/packages/kbn-es-query/index.ts b/packages/kbn-es-query/index.ts index 7495d37fc990c..43c660544bc90 100644 --- a/packages/kbn-es-query/index.ts +++ b/packages/kbn-es-query/index.ts @@ -67,6 +67,7 @@ export { buildEmptyFilter, buildExistsFilter, buildFilter, + buildCombinedFilter, buildPhraseFilter, buildPhrasesFilter, buildQueryFilter, @@ -89,6 +90,7 @@ export { isFilterPinned, isFilters, isMatchAllFilter, + isCombinedFilter, isPhraseFilter, isPhrasesFilter, isQueryStringFilter, diff --git a/packages/kbn-es-query/src/es_query/from_filters.ts b/packages/kbn-es-query/src/es_query/from_filters.ts index 2200648a52c4f..9e2599cc6c70b 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.ts +++ b/packages/kbn-es-query/src/es_query/from_filters.ts @@ -13,6 +13,7 @@ import { filterMatchesIndex } from './filter_matches_index'; import { Filter, cleanFilter, isFilterDisabled } from '../filters'; import { BoolQuery, DataViewBase } from './types'; import { handleNestedFilter } from './handle_nested_filter'; +import { handleCombinedFilter } from './handle_combined_filter'; /** * Create a filter that can be reversed for filters with negate set @@ -66,10 +67,11 @@ export interface EsQueryFiltersConfig { export const buildQueryFromFilters = ( inputFilters: Filter[] = [], inputDataViews: DataViewBase | DataViewBase[] | undefined, - { ignoreFilterIfFieldNotInIndex = false, nestedIgnoreUnmapped }: EsQueryFiltersConfig = { + options: EsQueryFiltersConfig = { ignoreFilterIfFieldNotInIndex: false, } ): BoolQuery => { + const { ignoreFilterIfFieldNotInIndex = false, nestedIgnoreUnmapped } = options; const filters = inputFilters.filter((filter) => filter && !isFilterDisabled(filter)); const indexPatterns = Array.isArray(inputDataViews) ? inputDataViews : [inputDataViews]; @@ -92,6 +94,7 @@ export const buildQueryFromFilters = ( ignoreUnmapped: nestedIgnoreUnmapped, }); }) + .map((filter) => handleCombinedFilter(filter, inputDataViews, options)) .map(cleanFilter) .map(translateToQuery); }; diff --git a/packages/kbn-es-query/src/es_query/handle_combined_filter.test.ts b/packages/kbn-es-query/src/es_query/handle_combined_filter.test.ts new file mode 100644 index 0000000000000..0cac2cb12f607 --- /dev/null +++ b/packages/kbn-es-query/src/es_query/handle_combined_filter.test.ts @@ -0,0 +1,610 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fields } from '../filters/stubs'; +import { DataViewBase } from './types'; +import { handleCombinedFilter } from './handle_combined_filter'; +import { + buildExistsFilter, + buildCombinedFilter, + buildPhraseFilter, + buildPhrasesFilter, + buildRangeFilter, +} from '../filters'; + +describe('#handleCombinedFilter', function () { + const indexPattern: DataViewBase = { + id: 'logstash-*', + fields, + title: 'dataView', + }; + + const getField = (fieldName: string) => { + const field = fields.find(({ name }) => fieldName === name); + if (!field) throw new Error(`field ${name} does not exist`); + return field; + }; + + it('Handles an empty list of filters', () => { + const filter = buildCombinedFilter([]); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [], + }, + } + `); + }); + + it('Handles a simple list of filters', () => { + const filters = [ + buildPhraseFilter(getField('extension'), 'value', indexPattern), + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + buildExistsFilter(getField('machine.os'), indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "exists": Object { + "field": "machine.os", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Handles a combination of filters and filter arrays', () => { + const filters = [ + buildPhraseFilter(getField('extension'), 'value', indexPattern), + [ + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + buildExistsFilter(getField('machine.os'), indexPattern), + ], + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + Object { + "exists": Object { + "field": "machine.os", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Handles nested COMBINED filters', () => { + const nestedCombinedFilter = buildCombinedFilter([ + buildPhraseFilter(getField('machine.os'), 'value', indexPattern), + buildPhraseFilter(getField('extension'), 'value', indexPattern), + ]); + const filters = [ + buildPhraseFilter(getField('extension'), 'value2', indexPattern), + nestedCombinedFilter, + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + buildExistsFilter(getField('machine.os.raw'), indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value2", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "machine.os": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "exists": Object { + "field": "machine.os.raw", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Handles negated sub-filters', () => { + const negatedFilter = buildPhrasesFilter(getField('extension'), ['tar', 'gz'], indexPattern); + negatedFilter.meta.negate = true; + + const filters = [ + [negatedFilter, buildPhraseFilter(getField('extension'), 'value', indexPattern)], + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + buildExistsFilter(getField('machine.os'), indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "extension": "tar", + }, + }, + Object { + "match_phrase": Object { + "extension": "gz", + }, + }, + ], + }, + }, + ], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "exists": Object { + "field": "machine.os", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Handles disabled filters within a filter array', () => { + const disabledFilter = buildPhraseFilter(getField('ssl'), false, indexPattern); + disabledFilter.meta.disabled = true; + const filters = [ + buildPhraseFilter(getField('extension'), 'value', indexPattern), + [disabledFilter, buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern)], + buildExistsFilter(getField('machine.os'), indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "exists": Object { + "field": "machine.os", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Handles complex-nested filters with ANDs and ORs', () => { + const filters = [ + [ + buildPhrasesFilter(getField('extension'), ['tar', 'gz'], indexPattern), + buildPhraseFilter(getField('ssl'), false, indexPattern), + buildCombinedFilter([ + buildPhraseFilter(getField('extension'), 'value', indexPattern), + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + ]), + buildExistsFilter(getField('machine.os'), indexPattern), + ], + buildPhrasesFilter(getField('machine.os.keyword'), ['foo', 'bar'], indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const result = handleCombinedFilter(filter); + expect(result.query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "extension": "tar", + }, + }, + Object { + "match_phrase": Object { + "extension": "gz", + }, + }, + ], + }, + }, + Object { + "match_phrase": Object { + "ssl": false, + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "match_phrase": Object { + "extension": "value", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + }, + Object { + "exists": Object { + "field": "machine.os", + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "machine.os.keyword": "foo", + }, + }, + Object { + "match_phrase": Object { + "machine.os.keyword": "bar", + }, + }, + ], + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + } + `); + }); + + it('Preserves filter properties', () => { + const filters = [ + buildPhraseFilter(getField('extension'), 'value', indexPattern), + buildRangeFilter(getField('bytes'), { gte: 10 }, indexPattern), + buildExistsFilter(getField('machine.os'), indexPattern), + ]; + const filter = buildCombinedFilter(filters); + const { query, ...rest } = handleCombinedFilter(filter); + expect(rest).toMatchInlineSnapshot(` + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": undefined, + "negate": false, + "params": Array [ + Object { + "meta": Object { + "index": "logstash-*", + }, + "query": Object { + "match_phrase": Object { + "extension": "value", + }, + }, + }, + Object { + "meta": Object { + "field": "bytes", + "index": "logstash-*", + "params": Object {}, + }, + "query": Object { + "range": Object { + "bytes": Object { + "gte": 10, + }, + }, + }, + }, + Object { + "meta": Object { + "index": "logstash-*", + }, + "query": Object { + "exists": Object { + "field": "machine.os", + }, + }, + }, + ], + "type": "combined", + }, + } + `); + }); +}); diff --git a/packages/kbn-es-query/src/es_query/handle_combined_filter.ts b/packages/kbn-es-query/src/es_query/handle_combined_filter.ts new file mode 100644 index 0000000000000..a9daf3fc4f33b --- /dev/null +++ b/packages/kbn-es-query/src/es_query/handle_combined_filter.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Filter, FilterItem, isCombinedFilter } from '../filters'; +import { DataViewBase } from './types'; +import { buildQueryFromFilters, EsQueryFiltersConfig } from './from_filters'; + +/** @internal */ +export const handleCombinedFilter = ( + filter: Filter, + inputDataViews?: DataViewBase | DataViewBase[], + options: EsQueryFiltersConfig = {} +): Filter => { + if (!isCombinedFilter(filter)) return filter; + const { params } = filter.meta; + const should = params.map((subFilter) => { + const subFilters = Array.isArray(subFilter) ? subFilter : [subFilter]; + return { bool: buildQueryFromFilters(flattenFilters(subFilters), inputDataViews, options) }; + }); + return { + ...filter, + query: { + bool: { + should, + minimum_should_match: 1, + }, + }, + }; +}; + +function flattenFilters(filters: FilterItem[]): Filter[] { + return filters.reduce((result, filter) => { + if (Array.isArray(filter)) return [...result, ...flattenFilters(filter)]; + return [...result, filter]; + }, []); +} diff --git a/packages/kbn-es-query/src/filters/build_filters/combined_filter.ts b/packages/kbn-es-query/src/filters/build_filters/combined_filter.ts new file mode 100644 index 0000000000000..4054f25ce45f6 --- /dev/null +++ b/packages/kbn-es-query/src/filters/build_filters/combined_filter.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Filter, FilterMeta, FILTERS } from './types'; +import { buildEmptyFilter } from './build_empty_filter'; + +/** + * Each item in an COMBINED filter may represent either one filter (to be ORed) or an array of filters (ANDed together before + * becoming part of the OR clause). + * @public + */ +export type FilterItem = Filter | FilterItem[]; + +/** + * @public + */ +export interface CombinedFilterMeta extends FilterMeta { + type: typeof FILTERS.COMBINED; + params: FilterItem[]; +} + +/** + * @public + */ +export interface CombinedFilter extends Filter { + meta: CombinedFilterMeta; +} + +/** + * @public + */ +export function isCombinedFilter(filter: Filter): filter is CombinedFilter { + return filter?.meta?.type === FILTERS.COMBINED; +} + +/** + * Builds an COMBINED filter. An COMBINED filter is a filter with multiple sub-filters. Each sub-filter (FilterItem) represents a + * condition. + * @param filters An array of CombinedFilterItem + * @public + */ +export function buildCombinedFilter(filters: FilterItem[]): CombinedFilter { + const filter = buildEmptyFilter(false); + return { + ...filter, + meta: { + ...filter.meta, + type: FILTERS.COMBINED, + params: filters, + }, + }; +} diff --git a/packages/kbn-es-query/src/filters/build_filters/index.ts b/packages/kbn-es-query/src/filters/build_filters/index.ts index d9d4bbb82aeb1..0b80363703dac 100644 --- a/packages/kbn-es-query/src/filters/build_filters/index.ts +++ b/packages/kbn-es-query/src/filters/build_filters/index.ts @@ -14,6 +14,7 @@ export * from './exists_filter'; export * from './get_filter_field'; export * from './get_filter_params'; export * from './match_all_filter'; +export * from './combined_filter'; export * from './phrase_filter'; export * from './phrases_filter'; export * from './query_string_filter'; diff --git a/packages/kbn-es-query/src/filters/build_filters/types.ts b/packages/kbn-es-query/src/filters/build_filters/types.ts index 5e920d11bcab5..27140c9a72e99 100644 --- a/packages/kbn-es-query/src/filters/build_filters/types.ts +++ b/packages/kbn-es-query/src/filters/build_filters/types.ts @@ -37,6 +37,7 @@ export enum FILTERS { RANGE = 'range', RANGE_FROM_VALUE = 'range_from_value', SPATIAL_FILTER = 'spatial_filter', + COMBINED = 'combined', } /** diff --git a/packages/kbn-es-query/src/filters/index.ts b/packages/kbn-es-query/src/filters/index.ts index 820559d5f9069..93efb9b1cd61f 100644 --- a/packages/kbn-es-query/src/filters/index.ts +++ b/packages/kbn-es-query/src/filters/index.ts @@ -34,6 +34,8 @@ export { export { isExistsFilter, isMatchAllFilter, + buildCombinedFilter, + isCombinedFilter, isPhraseFilter, isPhrasesFilter, isRangeFilter, @@ -75,6 +77,9 @@ export type { CustomFilter, RangeFilterParams, QueryStringFilter, + CombinedFilter, + CombinedFilterMeta, + FilterItem, } from './build_filters'; export { FilterStateStore, FILTERS } from './build_filters/types'; diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 5cd1458028626..67064af8cddc5 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -10,7 +10,10 @@ pageLoadAssetSize: cases: 144442 charts: 55000 cloud: 21076 + cloudChat: 19894 cloudExperiments: 59358 + cloudFullStory: 18493 + cloudLinks: 17629 cloudSecurityPosture: 19109 console: 46091 controls: 40000 diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index e6b6494e68a56..672c25d4e8fab 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -34,6 +34,7 @@ const ALERT_REASON = `${ALERT_NAMESPACE}.reason` as const; const ALERT_RISK_SCORE = `${ALERT_NAMESPACE}.risk_score` as const; const ALERT_SEVERITY = `${ALERT_NAMESPACE}.severity` as const; const ALERT_START = `${ALERT_NAMESPACE}.start` as const; +const ALERT_TIME_RANGE = `${ALERT_NAMESPACE}.time_range` as const; const ALERT_STATUS = `${ALERT_NAMESPACE}.status` as const; const ALERT_SYSTEM_STATUS = `${ALERT_NAMESPACE}.system_status` as const; const ALERT_UUID = `${ALERT_NAMESPACE}.uuid` as const; @@ -126,6 +127,7 @@ const fields = { ALERT_RULE_UPDATED_BY, ALERT_RULE_VERSION, ALERT_START, + ALERT_TIME_RANGE, ALERT_SEVERITY, ALERT_STATUS, ALERT_SYSTEM_STATUS, @@ -183,6 +185,7 @@ export { ALERT_RULE_VERSION, ALERT_SEVERITY, ALERT_START, + ALERT_TIME_RANGE, ALERT_SYSTEM_STATUS, ALERT_UUID, ECS_VERSION, diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx index dcdba80c268d7..59ff70f60d594 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx @@ -114,4 +114,30 @@ describe('FieldComponent', () => { expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('_source') ); }); + + it('it allows custom user input if "acceptsCustomOptions" is "true"', async () => { + const mockOnChange = jest.fn(); + const wrapper = render( + + ); + + const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput'); + fireEvent.change(fieldAutocompleteComboBox, { target: { value: 'custom' } }); + await waitFor(() => + expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('custom') + ); + }); }); diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/use_field.test.ts b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/use_field.test.ts index 68748bf82a20f..d060f585e9118 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/use_field.test.ts +++ b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/use_field.test.ts @@ -346,6 +346,18 @@ describe('useField', () => { ]); }); }); + it('should invoke onChange with custom option if one is sent', () => { + const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + act(() => { + result.current.handleCreateCustomOption('madeUpField'); + expect(onChangeMock).toHaveBeenCalledWith([ + { + name: 'madeUpField', + type: 'text', + }, + ]); + }); + }); }); describe('fieldWidth', () => { diff --git a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field/index.tsx index dad13434779e7..4c5f958b82754 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field/index.tsx @@ -25,16 +25,17 @@ export const FieldComponent: React.FC = ({ onChange, placeholder, selectedField, + acceptsCustomOptions = false, }): JSX.Element => { const { isInvalid, comboOptions, selectedComboOptions, fieldWidth, - renderFields, handleTouch, handleValuesChange, + handleCreateCustomOption, } = useField({ indexPattern, fieldTypeFilter, @@ -43,6 +44,29 @@ export const FieldComponent: React.FC = ({ fieldInputWidth, onChange, }); + + if (acceptsCustomOptions) { + return ( + + ); + } + return ( { const [touched, setIsTouched] = useState(false); + const [customOption, setCustomOption] = useState(null); - const { availableFields, selectedFields } = useMemo( - () => getComboBoxFields(indexPattern, selectedField, fieldTypeFilter), - [indexPattern, fieldTypeFilter, selectedField] - ); + const { availableFields, selectedFields } = useMemo(() => { + const indexPatternsToUse = + customOption != null && indexPattern != null + ? { ...indexPattern, fields: [...indexPattern?.fields, customOption] } + : indexPattern; + return getComboBoxFields(indexPatternsToUse, selectedField, fieldTypeFilter); + }, [indexPattern, fieldTypeFilter, selectedField, customOption]); const { comboOptions, labels, selectedComboOptions, disabledLabelTooltipTexts } = useMemo( () => getComboBoxProps({ availableFields, selectedFields }), @@ -117,6 +121,19 @@ export const useField = ({ [availableFields, labels, onChange] ); + const handleCreateCustomOption = useCallback( + (val: string) => { + const normalizedSearchValue = val.trim().toLowerCase(); + + if (!normalizedSearchValue) { + return; + } + setCustomOption({ name: val, type: 'text' }); + onChange([{ name: val, type: 'text' }]); + }, + [onChange] + ); + const handleTouch = useCallback((): void => { setIsTouched(true); }, [setIsTouched]); @@ -161,5 +178,6 @@ export const useField = ({ renderFields, handleTouch, handleValuesChange, + handleCreateCustomOption, }; }; diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts index adbffa3c728ef..945afbbc8604e 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -14,7 +14,6 @@ import { EntriesArray, Entry, EntryNested, - ExceptionListItemSchema, ExceptionListType, ListSchema, NamespaceType, @@ -27,6 +26,8 @@ import { entry, exceptionListItemSchema, nestedEntryItem, + CreateRuleExceptionListItemSchema, + createRuleExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { DataViewBase, @@ -55,6 +56,7 @@ import { EmptyEntry, EmptyNestedEntry, ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, FormattedBuilderEntry, OperatorOption, } from '../types'; @@ -65,59 +67,60 @@ export const isEntryNested = (item: BuilderEntry): item is EntryNested => { export const filterExceptionItems = ( exceptions: ExceptionsBuilderExceptionItem[] -): Array => { - return exceptions.reduce>( - (acc, exception) => { - const entries = exception.entries.reduce((nestedAcc, singleEntry) => { - const strippedSingleEntry = removeIdFromItem(singleEntry); - - if (entriesNested.is(strippedSingleEntry)) { - const nestedEntriesArray = strippedSingleEntry.entries.filter((singleNestedEntry) => { - const noIdSingleNestedEntry = removeIdFromItem(singleNestedEntry); - const [validatedNestedEntry] = validate(noIdSingleNestedEntry, nestedEntryItem); - return validatedNestedEntry != null; - }); - const noIdNestedEntries = nestedEntriesArray.map((singleNestedEntry) => - removeIdFromItem(singleNestedEntry) - ); - - const [validatedNestedEntry] = validate( - { ...strippedSingleEntry, entries: noIdNestedEntries }, - entriesNested - ); - - if (validatedNestedEntry != null) { - return [...nestedAcc, { ...singleEntry, entries: nestedEntriesArray }]; - } - return nestedAcc; - } else { - const [validatedEntry] = validate(strippedSingleEntry, entry); - - if (validatedEntry != null) { - return [...nestedAcc, singleEntry]; - } - return nestedAcc; +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptions.reduce((acc, exception) => { + const entries = exception.entries.reduce((nestedAcc, singleEntry) => { + const strippedSingleEntry = removeIdFromItem(singleEntry); + if (entriesNested.is(strippedSingleEntry)) { + const nestedEntriesArray = strippedSingleEntry.entries.filter((singleNestedEntry) => { + const noIdSingleNestedEntry = removeIdFromItem(singleNestedEntry); + const [validatedNestedEntry] = validate(noIdSingleNestedEntry, nestedEntryItem); + return validatedNestedEntry != null; + }); + const noIdNestedEntries = nestedEntriesArray.map((singleNestedEntry) => + removeIdFromItem(singleNestedEntry) + ); + + const [validatedNestedEntry] = validate( + { ...strippedSingleEntry, entries: noIdNestedEntries }, + entriesNested + ); + + if (validatedNestedEntry != null) { + return [...nestedAcc, { ...singleEntry, entries: nestedEntriesArray }]; } - }, []); - - if (entries.length === 0) { - return acc; + return nestedAcc; + } else { + const [validatedEntry] = validate(strippedSingleEntry, entry); + if (validatedEntry != null) { + return [...nestedAcc, singleEntry]; + } + return nestedAcc; } + }, []); - const item = { ...exception, entries }; + if (entries.length === 0) { + return acc; + } - if (exceptionListItemSchema.is(item)) { - return [...acc, item]; - } else if (createExceptionListItemSchema.is(item)) { - const { meta, ...rest } = item; - const itemSansMetaId: CreateExceptionListItemSchema = { ...rest, meta: undefined }; - return [...acc, itemSansMetaId]; - } else { - return acc; - } - }, - [] - ); + const item = { ...exception, entries }; + + if (exceptionListItemSchema.is(item)) { + return [...acc, item]; + } else if ( + createExceptionListItemSchema.is(item) || + createRuleExceptionListItemSchema.is(item) + ) { + const { meta, ...rest } = item; + const itemSansMetaId: CreateExceptionListItemSchema | CreateRuleExceptionListItemSchema = { + ...rest, + meta: undefined, + }; + return [...acc, itemSansMetaId]; + } else { + return acc; + } + }, []); }; export const addIdToEntries = (entries: EntriesArray): EntriesArray => { @@ -136,15 +139,15 @@ export const addIdToEntries = (entries: EntriesArray): EntriesArray => { export const getNewExceptionItem = ({ listId, namespaceType, - ruleName, + name, }: { - listId: string; - namespaceType: NamespaceType; - ruleName: string; + listId: string | undefined; + namespaceType: NamespaceType | undefined; + name: string; }): CreateExceptionListItemBuilderSchema => { return { comments: [], - description: `${ruleName} - exception list item`, + description: `Exception list item`, entries: addIdToEntries([ { field: '', @@ -158,7 +161,7 @@ export const getNewExceptionItem = ({ meta: { temporaryUuid: uuid.v4(), }, - name: `${ruleName} - exception list item`, + name, namespace_type: namespaceType, tags: [], type: 'simple', @@ -769,13 +772,15 @@ export const getCorrespondingKeywordField = ({ * @param parent nested entries hold copy of their parent for use in various logic * @param parentIndex corresponds to the entry index, this might seem obvious, but * was added to ensure that nested items could be identified with their parent entry + * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not */ export const getFormattedBuilderEntry = ( indexPattern: DataViewBase, item: BuilderEntry, itemIndex: number, parent: EntryNested | undefined, - parentIndex: number | undefined + parentIndex: number | undefined, + allowCustomFieldOptions: boolean ): FormattedBuilderEntry => { const { fields } = indexPattern; const field = parent != null ? `${parent.field}.${item.field}` : item.field; @@ -800,10 +805,14 @@ export const getFormattedBuilderEntry = ( value: getEntryValue(item), }; } else { + const fieldToUse = allowCustomFieldOptions + ? foundField ?? { name: item.field, type: 'keyword' } + : foundField; + return { correspondingKeywordField, entryIndex: itemIndex, - field: foundField, + field: fieldToUse, id: item.id != null ? item.id : `${itemIndex}`, nested: undefined, operator: getExceptionOperatorSelect(item), @@ -819,8 +828,7 @@ export const getFormattedBuilderEntry = ( * * @param patterns DataViewBase containing available fields on rule index * @param entries exception item entries - * @param addNested boolean noting whether or not UI is currently - * set to add a nested field + * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not * @param parent nested entries hold copy of their parent for use in various logic * @param parentIndex corresponds to the entry index, this might seem obvious, but * was added to ensure that nested items could be identified with their parent entry @@ -828,6 +836,7 @@ export const getFormattedBuilderEntry = ( export const getFormattedBuilderEntries = ( indexPattern: DataViewBase, entries: BuilderEntry[], + allowCustomFieldOptions: boolean, parent?: EntryNested, parentIndex?: number ): FormattedBuilderEntry[] => { @@ -839,7 +848,8 @@ export const getFormattedBuilderEntries = ( item, index, parent, - parentIndex + parentIndex, + allowCustomFieldOptions ); return [...acc, newItemEntry]; } else { @@ -869,7 +879,13 @@ export const getFormattedBuilderEntries = ( } if (isEntryNested(item)) { - const nestedItems = getFormattedBuilderEntries(indexPattern, item.entries, item, index); + const nestedItems = getFormattedBuilderEntries( + indexPattern, + item.entries, + allowCustomFieldOptions, + item, + index + ); return [...acc, parentEntry, ...nestedItems]; } 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/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts index 24cd482352930..33360bf82ef0c 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts @@ -254,6 +254,7 @@ export class KbnClientSavedObjects { 'epm-packages', 'epm-packages-assets', 'fleet-preconfiguration-deletion-record', + 'fleet-fleet-server-host', ]; const newOptions = { types, space: options?.space }; diff --git a/packages/kbn-tinymath/src/functions/defaults.js b/packages/kbn-tinymath/src/functions/defaults.js new file mode 100644 index 0000000000000..4f2d276626d9e --- /dev/null +++ b/packages/kbn-tinymath/src/functions/defaults.js @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Returns the default provided value when the first value if null. If at least one array is passed into the function, the function will be applied index-wise to each element. + * @param {(any|any[])} a array of any values + * @param {(any)} b a value to use as fallback. + * @return {(any|any[])} The `a` value if not null, `b` otherwise. Returns an array where each element is default to `b` when null, or kept the original value if `a` is an array. + * + * @example + * defaults(null, 1) // returns 1 + * defaults([3, null, 5], 1) // returns [3, 1, 5] + * defaults(5, 1) // returns 5 + */ + +module.exports = { defaults }; + +function defaults(a, b) { + if (Array.isArray(a)) { + return a.map((v) => (v == null ? b : v)); + } + return a == null ? b : a; +} + +defaults.skipNumberValidation = true; diff --git a/packages/kbn-tinymath/src/functions/index.js b/packages/kbn-tinymath/src/functions/index.js index eb58a4d56b569..37c2a13c41cf4 100644 --- a/packages/kbn-tinymath/src/functions/index.js +++ b/packages/kbn-tinymath/src/functions/index.js @@ -14,6 +14,7 @@ const { clamp } = require('./clamp'); const { cos } = require('./cos'); const { count } = require('./count'); const { cube } = require('./cube'); +const { defaults } = require('./defaults'); const { degtorad } = require('./degtorad'); const { divide } = require('./divide'); const { exp } = require('./exp'); @@ -56,6 +57,7 @@ module.exports = { count, cube, degtorad, + defaults, divide, exp, first, diff --git a/packages/kbn-tinymath/test/functions/defaults.test.js b/packages/kbn-tinymath/test/functions/defaults.test.js new file mode 100644 index 0000000000000..4490ba6787f29 --- /dev/null +++ b/packages/kbn-tinymath/test/functions/defaults.test.js @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const { defaults } = require('../../src/functions/defaults'); + +describe('Defaults', () => { + it('number, number', () => { + expect(defaults(10, 2)).toEqual(10); + }); + + it('null, number', () => { + expect(defaults(null, 2)).toEqual(2); + }); + + it('number, null', () => { + expect(defaults(2, null)).toEqual(2); + }); + + it('array, number', () => { + expect(defaults([10, 20, 30, 40], 10)).toEqual([10, 20, 30, 40]); + }); + + it('arrays with null, number', () => { + expect(defaults([null, 20, 30, null], 10)).toEqual([10, 20, 30, 10]); + }); + + it('empty array, number', () => { + expect(defaults([], 10)).toEqual([]); + }); + + it('skips number validation', () => { + expect(defaults).toHaveProperty('skipNumberValidation', true); + }); +}); diff --git a/renovate.json b/renovate.json index 4075b2452bea1..bdaf373932fa1 100644 --- a/renovate.json +++ b/renovate.json @@ -210,7 +210,7 @@ }, { "groupName": "Profiling", - "matchPackageNames": ["fnv-plus", "peggy", "@types/dagre", "@types/fnv-plus"], + "matchPackageNames": ["peggy", "@types/dagre"], "reviewers": ["team:profiling-ui"], "matchBaseBranches": ["main"], "labels": ["release_note:skip", "backport:skip"], diff --git a/src/cli_setup/utils.ts b/src/cli_setup/utils.ts index 5c66fa84c0f30..47b8199f16ea0 100644 --- a/src/cli_setup/utils.ts +++ b/src/cli_setup/utils.ts @@ -48,7 +48,7 @@ export const elasticsearch = new ElasticsearchService(logger, kibanaPackageJson. logger, type, // we use an independent AgentManager for cli_setup, no need to track performance of this one - agentManager: new AgentManager(), + agentFactoryProvider: new AgentManager(), kibanaVersion: kibanaPackageJson.version, }); }, diff --git a/src/core/server/core_app/core_app.test.ts b/src/core/server/core_app/core_app.test.ts index 76663eeed2fd1..1ea3eeef29a09 100644 --- a/src/core/server/core_app/core_app.test.ts +++ b/src/core/server/core_app/core_app.test.ts @@ -12,7 +12,7 @@ import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { mockRouter } from '@kbn/core-http-router-server-mocks'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { coreMock, httpServerMock } from '../mocks'; -import { httpResourcesMock } from '../http_resources/http_resources_service.mock'; +import { httpResourcesMock } from '@kbn/core-http-resources-server-mocks'; import { PluginType } from '../plugins'; import { CoreApp } from './core_app'; import { RequestHandlerContext } from '..'; diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts index b8701d7646b7e..f0940f6abad50 100644 --- a/src/core/server/core_app/core_app.ts +++ b/src/core/server/core_app/core_app.ts @@ -21,7 +21,7 @@ import type { IBasePath, } from '@kbn/core-http-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; -import { HttpResources, HttpResourcesServiceToolkit } from '../http_resources'; +import type { HttpResources, HttpResourcesServiceToolkit } from '@kbn/core-http-resources-server'; import { InternalCorePreboot, InternalCoreSetup } from '../internal_types'; import { registerBundleRoutes } from './bundle_routes'; import type { InternalCoreAppRequestHandlerContext } from './internal_types'; 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/index.ts b/src/core/server/index.ts index dafd53e374fe8..6232a17eb6111 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -69,7 +69,7 @@ import type { I18nServiceSetup } from '@kbn/core-i18n-server'; import type { StatusServiceSetup } from '@kbn/core-status-server'; import type { UiSettingsServiceSetup, UiSettingsServiceStart } from '@kbn/core-ui-settings-server'; import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; -import { HttpResources } from './http_resources'; +import type { HttpResources } from '@kbn/core-http-resources-server'; import { PluginsServiceSetup, PluginsServiceStart } from './plugins'; export type { PluginOpaqueId } from '@kbn/core-base-common'; @@ -228,7 +228,7 @@ export type { HttpResourcesResponseOptions, HttpResourcesServiceToolkit, HttpResourcesRequestHandler, -} from './http_resources'; +} from '@kbn/core-http-resources-server'; export type { LoggingServiceSetup, 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..e7d02da09889d --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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-fleet-server-host": "f00ca963f1bee868806319789cdc33f1f53a97e2", + "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/core/server/integration_tests/saved_objects/migrations/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/type_registrations.test.ts index 4fd5ca5cd2aea..eb9eb8a420695 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/type_registrations.test.ts @@ -58,6 +58,7 @@ const previouslyRegisteredTypes = [ 'fleet-agents', 'fleet-enrollment-api-keys', 'fleet-preconfiguration-deletion-record', + 'fleet-fleet-server-host', 'graph-workspace', 'guided-setup-state', 'guided-onboarding-guide-state', diff --git a/src/core/server/integration_tests/saved_objects/routes/find.test.ts b/src/core/server/integration_tests/saved_objects/routes/find.test.ts index ab3ca6c459dae..2c7b1c9838b50 100644 --- a/src/core/server/integration_tests/saved_objects/routes/find.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/find.test.ts @@ -123,6 +123,7 @@ describe('GET /api/saved_objects/_find', () => { type: ['foo', 'bar'], defaultSearchOperator: 'OR', hasReferenceOperator: 'OR', + hasNoReferenceOperator: 'OR', }); }); @@ -213,6 +214,73 @@ describe('GET /api/saved_objects/_find', () => { ); }); + it('accepts the query parameter has_no_reference as an object', async () => { + const references = querystring.escape( + JSON.stringify({ + id: '1', + type: 'reference', + }) + ); + await supertest(httpSetup.server.listener) + .get(`/api/saved_objects/_find?type=foo&has_no_reference=${references}`) + .expect(200); + + expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); + + const options = savedObjectsClient.find.mock.calls[0][0]; + expect(options.hasNoReference).toEqual({ + id: '1', + type: 'reference', + }); + }); + + it('accepts the query parameter has_no_reference as an array', async () => { + const references = querystring.escape( + JSON.stringify([ + { + id: '1', + type: 'reference', + }, + { + id: '2', + type: 'reference', + }, + ]) + ); + await supertest(httpSetup.server.listener) + .get(`/api/saved_objects/_find?type=foo&has_no_reference=${references}`) + .expect(200); + + expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); + + const options = savedObjectsClient.find.mock.calls[0][0]; + expect(options.hasNoReference).toEqual([ + { + id: '1', + type: 'reference', + }, + { + id: '2', + type: 'reference', + }, + ]); + }); + + it('accepts the query parameter has_no_reference_operator', async () => { + await supertest(httpSetup.server.listener) + .get('/api/saved_objects/_find?type=foo&has_no_reference_operator=AND') + .expect(200); + + expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); + + const options = savedObjectsClient.find.mock.calls[0][0]; + expect(options).toEqual( + expect.objectContaining({ + hasNoReferenceOperator: 'AND', + }) + ); + }); + it('accepts the query parameter search_fields', async () => { await supertest(httpSetup.server.listener) .get('/api/saved_objects/_find?type=foo&search_fields=title') diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index 683d08fe4f849..c66fdf9a968d2 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -59,7 +59,10 @@ import type { InternalUiSettingsServiceStart, } from '@kbn/core-ui-settings-server-internal'; import type { InternalRenderingServiceSetup } from '@kbn/core-rendering-server-internal'; -import { InternalHttpResourcesPreboot, InternalHttpResourcesSetup } from './http_resources'; +import type { + InternalHttpResourcesPreboot, + InternalHttpResourcesSetup, +} from '@kbn/core-http-resources-server-internal'; /** @internal */ export interface InternalCorePreboot { diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index cd0429415e7cb..356fd4deb44d6 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -32,6 +32,7 @@ import { i18nServiceMock } from '@kbn/core-i18n-server-mocks'; import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; +import { httpResourcesMock } from '@kbn/core-http-resources-server-mocks'; import type { PluginInitializerContext, CoreSetup, @@ -40,7 +41,6 @@ import type { CorePreboot, RequestHandlerContext, } from '.'; -import { httpResourcesMock } from './http_resources/http_resources_service.mock'; import { SharedGlobalConfig } from './plugins'; export { configServiceMock, configDeprecationsMock } from '@kbn/config-mocks'; @@ -48,7 +48,7 @@ export { loggingSystemMock } from '@kbn/core-logging-server-mocks'; export { httpServerMock, sessionStorageMock, httpServiceMock } from '@kbn/core-http-server-mocks'; export { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; export { typeRegistryMock as savedObjectsTypeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; -export { httpResourcesMock } from './http_resources/http_resources_service.mock'; +export { httpResourcesMock } from '@kbn/core-http-resources-server-mocks'; export { savedObjectsServiceMock } from '@kbn/core-saved-objects-server-mocks'; export { savedObjectsClientMock, diff --git a/src/core/server/server.ts b/src/core/server/server.ts index b7f41dd31dd04..07e10cc72845a 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -68,8 +68,8 @@ import type { } from '@kbn/core-http-request-handler-context-server'; import { RenderingService } from '@kbn/core-rendering-server-internal'; +import { HttpResourcesService } from '@kbn/core-http-resources-server-internal'; import { CoreApp } from './core_app'; -import { HttpResourcesService } from './http_resources'; import { PluginsService, config as pluginsConfig } from './plugins'; import { InternalCorePreboot, InternalCoreSetup, InternalCoreStart } from './internal_types'; import { DiscoveredPlugins } from './plugins'; @@ -280,7 +280,10 @@ export class Server { executionContext: executionContextSetup, }); - const metricsSetup = await this.metrics.setup({ http: httpSetup }); + const metricsSetup = await this.metrics.setup({ + http: httpSetup, + elasticsearchService: elasticsearchServiceSetup, + }); const coreUsageDataSetup = this.coreUsageData.setup({ http: httpSetup, 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/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index b4224e154def5..6f82ec078f7ab 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -11,7 +11,7 @@ export const storybookAliases = { apm: 'x-pack/plugins/apm/.storybook', canvas: 'x-pack/plugins/canvas/storybook', ci_composite: '.ci/.storybook', - cloud: 'x-pack/plugins/cloud/.storybook', + cloud_chat: 'x-pack/plugins/cloud_integrations/cloud_chat/.storybook', coloring: 'packages/kbn-coloring/.storybook', chart_icons: 'packages/kbn-chart-icons/.storybook', content_management: 'packages/content-management/.storybook', diff --git a/src/plugins/bfetch/server/plugin.ts b/src/plugins/bfetch/server/plugin.ts index 0f51f5da62353..85720480cf9a0 100644 --- a/src/plugins/bfetch/server/plugin.ts +++ b/src/plugins/bfetch/server/plugin.ts @@ -20,6 +20,7 @@ import { } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { map$ } from '@kbn/std'; +import { RouteConfigOptions } from '@kbn/core-http-server'; import { StreamingResponseHandler, BatchRequestData, @@ -54,7 +55,8 @@ export interface BfetchServerSetup { context: RequestHandlerContext ) => StreamingResponseHandler, method?: 'GET' | 'POST' | 'PUT' | 'DELETE', - pluginRouter?: ReturnType + pluginRouter?: ReturnType, + options?: RouteConfigOptions<'get' | 'post' | 'put' | 'delete'> ) => void; } @@ -117,14 +119,16 @@ export class BfetchServerPlugin router: ReturnType; logger: Logger; }): BfetchServerSetup['addStreamingResponseRoute'] => - (path, handler, method = 'POST', pluginRouter) => { + (path, handler, method = 'POST', pluginRouter, options) => { const httpRouter = pluginRouter || router; + const routeDefinition = { path: `/${removeLeadingSlash(path)}`, validate: { body: schema.any(), query: schema.object({ compress: schema.boolean({ defaultValue: false }) }), }, + options, }; const routeHandler: RequestHandler = async ( context: RequestHandlerContext, diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx index 5a03e2dd3187c..6588cefdb8f2b 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx @@ -211,7 +211,7 @@ describe('GaugeComponent', function () { }); describe('ticks and color bands', () => { - it('sets proper color bands for values smaller than maximum', () => { + it('sets proper color bands and ticks on color bands for values smaller than maximum', () => { const palette = { type: 'palette' as const, name: 'custom', @@ -236,6 +236,7 @@ describe('GaugeComponent', function () { }, } as GaugeRenderProps; const goal = shallowWithIntl().find(Goal); + expect(goal.prop('ticks')).toEqual([0, 1, 2, 3, 4, 10]); expect(goal.prop('bands')).toEqual([0, 1, 2, 3, 4, 10]); }); it('sets proper color bands if palette steps are smaller than minimum', () => { diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index b75d613f814a7..37e3ca6bdd8f1 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -18,6 +18,8 @@ import { GaugeLabelMajorMode, GaugeLabelMajorModes, GaugeColorModes, + GaugeShapes, + GaugeTicksPositions, } from '../../common'; import { getAccessorsFromArgs, @@ -30,7 +32,7 @@ import { } from './utils'; import { getIcons } from './utils/icons'; import './index.scss'; -import { GaugeCentralMajorMode } from '../../common/types'; +import { GaugeCentralMajorMode, GaugeTicksPosition } from '../../common/types'; import { isBulletShape, isRoundShape } from '../../common/utils'; import './gauge.scss'; @@ -135,6 +137,35 @@ const getPreviousSectionValue = (value: number, bands: number[]) => { return prevSectionValue; }; +function getTicksLabels(baseStops: number[]) { + const tenPercentRange = (Math.max(...baseStops) - Math.min(...baseStops)) * 0.1; + const lastIndex = baseStops.length - 1; + return baseStops.filter((stop, i) => { + if (i === 0 || i === lastIndex) { + return true; + } + + return !( + stop - baseStops[i - 1] < tenPercentRange || baseStops[lastIndex] - stop < tenPercentRange + ); + }); +} + +function getTicks( + ticksPosition: GaugeTicksPosition, + range: [number, number], + colorBands?: number[], + percentageMode?: boolean +) { + if (ticksPosition === GaugeTicksPositions.HIDDEN) { + return []; + } + + if (ticksPosition === GaugeTicksPositions.BANDS && colorBands) { + return colorBands && getTicksLabels(colorBands); + } +} + export const GaugeComponent: FC = memo( ({ data, args, uiState, formatFactory, paletteService, chartsThemeService, renderComplete }) => { const { @@ -146,6 +177,7 @@ export const GaugeComponent: FC = memo( labelMajorMode, centralMajor, centralMajorMode, + ticksPosition, commonLabel, } = args; @@ -294,6 +326,12 @@ export const GaugeComponent: FC = memo( actualValue = actualValueToPercentsLegacy(palette?.params as CustomPaletteState, actualValue); } + const totalTicks = getTicks(ticksPosition, [min, max], bands, args.percentageMode); + const ticks = + totalTicks && gaugeType === GaugeShapes.CIRCLE + ? totalTicks.slice(0, totalTicks.length - 1) + : totalTicks; + const goalConfig = getGoalConfig(gaugeType); const labelMajorTitle = getTitle(labelMajorMode, labelMajor, metricColumn?.name); @@ -329,6 +367,7 @@ export const GaugeComponent: FC = memo( tickValueFormatter={({ value: tickValue }) => tickFormatter.convert(tickValue)} tooltipValueFormatter={(tooltipValue) => tickFormatter.convert(tooltipValue)} bands={bands} + ticks={ticks} domain={{ min, max }} bandFillColor={ colorMode === GaugeColorModes.PALETTE 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/components/__snapshots__/metric_component.test.tsx.snap b/src/plugins/chart_expressions/expression_legacy_metric/public/components/__snapshots__/metric_component.test.tsx.snap index 106d45bc4a87c..36c92d316d83c 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/public/components/__snapshots__/metric_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_legacy_metric/public/components/__snapshots__/metric_component.test.tsx.snap @@ -28,7 +28,6 @@ Array [ } } onFilter={[Function]} - renderComplete={[MockFunction]} style={ Object { "bgColor": false, @@ -116,4 +115,4 @@ exports[`MetricVisComponent should render correct structure for single metric 1` } } /> -`; \ No newline at end of file +`; diff --git a/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.test.tsx b/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.test.tsx index 314f837986258..d61e6d19a6eab 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.test.tsx +++ b/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import { Datatable } from '@kbn/expressions-plugin/common'; import MetricVisComponent, { MetricVisComponentProps } from './metric_component'; import { LabelPosition } from '../../common/constants'; @@ -76,15 +76,15 @@ describe('MetricVisComponent', function () { ...propOverrides, }; - return shallow(); + return ; }; it('should render component', () => { - expect(getComponent().exists()).toBe(true); + expect(shallow(getComponent()).exists()).toBe(true); }); it('should render correct structure for single metric', function () { - expect(getComponent()).toMatchSnapshot(); + expect(shallow(getComponent())).toMatchSnapshot(); }); it('should render correct structure for multi-value metrics', function () { @@ -110,6 +110,36 @@ describe('MetricVisComponent', function () { }, }); - expect(component).toMatchSnapshot(); + expect(shallow(component)).toMatchSnapshot(); + }); + + it('should call renderComplete once for multi-value metrics', function () { + const renderComplete = jest.fn(); + const component = getComponent({ + renderComplete, + filterable: [true, false], + visData: { + type: 'datatable', + columns: [ + { id: 'col-0', name: '1st percentile of bytes', meta: { type: 'number' } }, + { id: 'col-1', name: '99th percentile of bytes', meta: { type: 'number' } }, + ], + rows: [{ 'col-0': 182, 'col-1': 445842.4634666484 }], + }, + visParams: { + ...visParams, + dimensions: { + ...visParams.dimensions, + metrics: [ + { accessor: 0, type: 'vis_dimension', format: { id: 'number', params: {} } }, + { accessor: 1, type: 'vis_dimension', format: { id: 'number', params: {} } }, + ], + }, + }, + }); + + mount(component); + + expect(renderComplete).toHaveBeenCalledTimes(1); }); }); diff --git a/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.tsx b/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.tsx index 8da0c4d4d8b8a..2bebcda46bbe5 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.tsx +++ b/src/plugins/chart_expressions/expression_legacy_metric/public/components/metric_component.tsx @@ -127,7 +127,7 @@ class MetricVisComponent extends Component { return this.props.visParams.metric.autoScale && this.props.visParams.metric.colorFullBackground; }; - private renderMetric = (metric: MetricOptions, index: number) => { + private renderMetric = (metric: MetricOptions, index: number, arrayRef: MetricOptions[]) => { const hasBuckets = this.props.visParams.dimensions.bucket !== undefined; const MetricComponent = this.props.visParams.metric.autoScale ? AutoScaleMetricVisValue @@ -157,7 +157,7 @@ class MetricVisComponent extends Component { autoScale={this.props.visParams.metric.autoScale} colorFullBackground={this.props.visParams.metric.colorFullBackground} labelConfig={this.props.visParams.metric.labels} - renderComplete={this.props.renderComplete} + renderComplete={arrayRef.length - 1 === index ? this.props.renderComplete : undefined} /> ); }; 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/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.test.ts index 79427cbe4d3cc..4664902d13876 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.test.ts @@ -24,7 +24,11 @@ describe('layeredXyVis', () => { expect(result).toEqual({ type: 'render', as: XY_VIS, - value: { args: { ...rest, layers: [sampleExtendedLayer] } }, + value: { + args: { ...rest, layers: [sampleExtendedLayer] }, + syncColors: false, + syncTooltips: false, + }, }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts index 1eed6aca1cea5..8dcc58cda01a8 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts @@ -61,6 +61,8 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) (handlers.variables?.embeddableTitle as string) ?? handlers.getExecutionContext?.()?.description, }, + syncColors: handlers?.isSyncColorsEnabled?.() ?? false, + syncTooltips: handlers?.isSyncTooltipsEnabled?.() ?? false, }, }; }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index 7e6afa0dd23a7..67c7ab8d1e294 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -38,6 +38,8 @@ describe('xyVis', () => { }, ], }, + syncColors: false, + syncTooltips: false, }, }); }); @@ -344,6 +346,8 @@ describe('xyVis', () => { }, ], }, + syncColors: false, + syncTooltips: false, }, }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts index defda933784de..e29f1e5ffff3c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -136,6 +136,8 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { (handlers.variables?.embeddableTitle as string) ?? handlers.getExecutionContext?.()?.description, }, + syncColors: handlers?.isSyncColorsEnabled?.() ?? false, + syncTooltips: handlers?.isSyncTooltipsEnabled?.() ?? false, }, }; }; diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index b39002c8549cb..9b404de958722 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -9,6 +9,8 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; +export { LayerTypes } from './constants'; + export type { XYArgs, EndValue, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index 2a4baeb22313e..de387b4113373 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -16,6 +16,8 @@ import { XYProps } from './expression_functions'; export interface XYChartProps { args: XYProps; + syncTooltips: boolean; + syncColors: boolean; } export interface XYRender { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.scss b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.scss index 0fa98d5e5db82..1e705724fbedc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.scss +++ b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.scss @@ -6,12 +6,14 @@ padding: $euiSizeS; table { + table-layout: fixed; + width: 100%; + td, th { text-align: left; padding: $euiSizeXS; overflow-wrap: break-word; - word-wrap: break-word; } } } @@ -22,10 +24,13 @@ } } +.detailedTooltip__labelContainer { + max-width: $euiSizeXL * 5; +} + .detailedTooltip__labelContainer, .detailedTooltip__valueContainer { overflow-wrap: break-word; - word-wrap: break-word; } .detailedTooltip__label { diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 893037a9f75f1..5606bad9050c7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -226,8 +226,8 @@ export const getXyChartRenderer = ({ onClickValue={onClickValue} onSelectRange={onSelectRange} renderMode={handlers.getRenderMode()} - syncColors={handlers.isSyncColorsEnabled()} - syncTooltips={handlers.isSyncTooltipsEnabled()} + syncColors={config.syncColors} + syncTooltips={config.syncTooltips} uiState={handlers.uiState as PersistedState} renderComplete={renderComplete} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts index d39c6bb4e63b4..71d12db8ffb91 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -18,7 +18,7 @@ describe('calculateMinInterval', () => { beforeEach(() => { const { layers, ...restArgs } = sampleArgs().args; - xyProps = { args: { ...restArgs, layers } }; + xyProps = { args: { ...restArgs, layers }, syncColors: false, syncTooltips: false }; layer = xyProps.args.layers[0] as DataLayerConfig; layer.xScaleType = 'time'; }); diff --git a/src/plugins/chart_expressions/expression_xy/public/index.ts b/src/plugins/chart_expressions/expression_xy/public/index.ts index d9447400aa266..7578c4ad89f9a 100755 --- a/src/plugins/chart_expressions/expression_xy/public/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/index.ts @@ -14,4 +14,6 @@ export function plugin() { return new ExpressionXyPlugin(); } +export { LayerTypes } from '../common'; + export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; diff --git a/src/plugins/console/public/application/components/settings_modal.tsx b/src/plugins/console/public/application/components/settings_modal.tsx index 095dde1c29507..67b0e2c0d957a 100644 --- a/src/plugins/console/public/application/components/settings_modal.tsx +++ b/src/plugins/console/public/application/components/settings_modal.tsx @@ -77,9 +77,9 @@ export const DevToolsSettingsModal = (props: DevToolsSettingsModalProps) => { const [polling, setPolling] = useState(props.settings.polling); const [pollInterval, setPollInterval] = useState(props.settings.pollInterval); const [tripleQuotes, setTripleQuotes] = useState(props.settings.tripleQuotes); - const [isHistoryDisabled, setIsHistoryDisabled] = useState(props.settings.isHistoryDisabled); - const [isKeyboardShortcutsDisabled, setIsKeyboardShortcutsDisabled] = useState( - props.settings.isKeyboardShortcutsDisabled + const [isHistoryEnabled, setIsHistoryEnabled] = useState(props.settings.isHistoryEnabled); + const [isKeyboardShortcutsEnabled, setIsKeyboardShortcutsEnabled] = useState( + props.settings.isKeyboardShortcutsEnabled ); const autoCompleteCheckboxes = [ @@ -140,8 +140,8 @@ export const DevToolsSettingsModal = (props: DevToolsSettingsModalProps) => { polling, pollInterval, tripleQuotes, - isHistoryDisabled, - isKeyboardShortcutsDisabled, + isHistoryEnabled, + isKeyboardShortcutsEnabled, }); } @@ -153,17 +153,17 @@ export const DevToolsSettingsModal = (props: DevToolsSettingsModalProps) => { }, []); const toggleKeyboardShortcuts = useCallback( - (isDisabled: boolean) => { + (isEnabled: boolean) => { if (props.editorInstance) { unregisterCommands(props.editorInstance); - setIsKeyboardShortcutsDisabled(isDisabled); + setIsKeyboardShortcutsEnabled(isEnabled); } }, [props.editorInstance] ); const toggleSavingToHistory = useCallback( - (isDisabled: boolean) => setIsHistoryDisabled(isDisabled), + (isEnabled: boolean) => setIsHistoryEnabled(isEnabled), [] ); @@ -289,11 +289,11 @@ export const DevToolsSettingsModal = (props: DevToolsSettingsModalProps) => { } > } onChange={(e) => toggleSavingToHistory(e.target.checked)} @@ -309,11 +309,11 @@ export const DevToolsSettingsModal = (props: DevToolsSettingsModalProps) => { } > } onChange={(e) => toggleKeyboardShortcuts(e.target.checked)} diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 74a052646e198..ed8c87b5df147 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -259,8 +259,8 @@ function EditorUI({ initialTextValue, setEditorInstance }: EditorProps) { }, [settings]); useEffect(() => { - const { isKeyboardShortcutsDisabled } = settings; - if (!isKeyboardShortcutsDisabled) { + const { isKeyboardShortcutsEnabled } = settings; + if (isKeyboardShortcutsEnabled) { registerCommands({ senseEditor: editorInstanceRef.current!, sendCurrentRequest, diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx index 0c7e4c46d95a6..e895ddc135db8 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx +++ b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx @@ -106,7 +106,9 @@ describe('useSendCurrentRequest', () => { (sendRequest as jest.Mock).mockReturnValue( [{ request: {} }, { request: {} }] /* two responses to save history */ ); - (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({}); + (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({ + isHistoryEnabled: true, + }); (mockContextValue.services.history.addToHistory as jest.Mock).mockImplementation(() => { // Mock throwing throw new Error('cannot save!'); diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts index 87f72571a63e6..28d875c246ca3 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts @@ -52,9 +52,9 @@ export const useSendCurrentRequest = () => { const results = await sendRequest({ http, requests }); let saveToHistoryError: undefined | Error; - const { isHistoryDisabled } = settings.toJSON(); + const { isHistoryEnabled } = settings.toJSON(); - if (!isHistoryDisabled) { + if (isHistoryEnabled) { results.forEach(({ request: { path, method, data } }) => { try { history.addToHistory(path, method, data); @@ -84,7 +84,7 @@ export const useSendCurrentRequest = () => { notifications.toasts.remove(toast); }, onDisableSavingToHistory: () => { - settings.setIsHistoryDisabled(true); + settings.setIsHistoryEnabled(false); notifications.toasts.remove(toast); }, }), diff --git a/src/plugins/console/public/services/settings.ts b/src/plugins/console/public/services/settings.ts index aa2280f06064f..e4731dd3f3a31 100644 --- a/src/plugins/console/public/services/settings.ts +++ b/src/plugins/console/public/services/settings.ts @@ -15,8 +15,8 @@ export const DEFAULT_SETTINGS = Object.freeze({ tripleQuotes: true, wrapMode: true, autocomplete: Object.freeze({ fields: true, indices: true, templates: true, dataStreams: true }), - isHistoryDisabled: false, - isKeyboardShortcutsDisabled: false, + isHistoryEnabled: true, + isKeyboardShortcutsEnabled: true, }); export interface DevToolsSettings { @@ -31,8 +31,8 @@ export interface DevToolsSettings { polling: boolean; pollInterval: number; tripleQuotes: boolean; - isHistoryDisabled: boolean; - isKeyboardShortcutsDisabled: boolean; + isHistoryEnabled: boolean; + isKeyboardShortcutsEnabled: boolean; } enum SettingKeys { @@ -42,12 +42,32 @@ enum SettingKeys { AUTOCOMPLETE_SETTINGS = 'autocomplete_settings', CONSOLE_POLLING = 'console_polling', POLL_INTERVAL = 'poll_interval', - IS_HISTORY_DISABLED = 'is_history_disabled', - IS_KEYBOARD_SHORTCUTS_DISABLED = 'is_keyboard_shortcuts_disabled', + IS_HISTORY_ENABLED = 'is_history_enabled', + IS_KEYBOARD_SHORTCUTS_ENABLED = 'is_keyboard_shortcuts_enabled', } export class Settings { - constructor(private readonly storage: Storage) {} + constructor(private readonly storage: Storage) { + // Migration from old settings to new ones + this.addMigrationRule('is_history_disabled', SettingKeys.IS_HISTORY_ENABLED, (value: any) => { + return !value; + }); + this.addMigrationRule( + 'is_keyboard_shortcuts_disabled', + SettingKeys.IS_KEYBOARD_SHORTCUTS_ENABLED, + (value: any) => { + return !value; + } + ); + } + + private addMigrationRule(previousKey: string, newKey: string, migration: (value: any) => any) { + const value = this.storage.get(previousKey); + if (value !== undefined) { + this.storage.set(newKey, migration(value)); + this.storage.delete(previousKey); + } + } getFontSize() { return this.storage.get(SettingKeys.FONT_SIZE, DEFAULT_SETTINGS.fontSize); @@ -94,13 +114,13 @@ export class Settings { return true; } - setIsHistoryDisabled(isDisabled: boolean) { - this.storage.set(SettingKeys.IS_HISTORY_DISABLED, isDisabled); + setIsHistoryEnabled(isEnabled: boolean) { + this.storage.set(SettingKeys.IS_HISTORY_ENABLED, isEnabled); return true; } - getIsHistoryDisabled() { - return this.storage.get(SettingKeys.IS_HISTORY_DISABLED, DEFAULT_SETTINGS.isHistoryDisabled); + getIsHistoryEnabled() { + return this.storage.get(SettingKeys.IS_HISTORY_ENABLED, DEFAULT_SETTINGS.isHistoryEnabled); } setPollInterval(interval: number) { @@ -111,15 +131,15 @@ export class Settings { return this.storage.get(SettingKeys.POLL_INTERVAL, DEFAULT_SETTINGS.pollInterval); } - setIsKeyboardShortcutsDisabled(disable: boolean) { - this.storage.set(SettingKeys.IS_KEYBOARD_SHORTCUTS_DISABLED, disable); + setIsKeyboardShortcutsEnabled(isEnabled: boolean) { + this.storage.set(SettingKeys.IS_KEYBOARD_SHORTCUTS_ENABLED, isEnabled); return true; } getIsKeyboardShortcutsDisabled() { return this.storage.get( - SettingKeys.IS_KEYBOARD_SHORTCUTS_DISABLED, - DEFAULT_SETTINGS.isKeyboardShortcutsDisabled + SettingKeys.IS_KEYBOARD_SHORTCUTS_ENABLED, + DEFAULT_SETTINGS.isKeyboardShortcutsEnabled ); } @@ -131,8 +151,8 @@ export class Settings { fontSize: parseFloat(this.getFontSize()), polling: Boolean(this.getPolling()), pollInterval: this.getPollInterval(), - isHistoryDisabled: Boolean(this.getIsHistoryDisabled()), - isKeyboardShortcutsDisabled: Boolean(this.getIsKeyboardShortcutsDisabled()), + isHistoryEnabled: Boolean(this.getIsHistoryEnabled()), + isKeyboardShortcutsEnabled: Boolean(this.getIsKeyboardShortcutsDisabled()), }; } @@ -143,8 +163,8 @@ export class Settings { autocomplete, polling, pollInterval, - isHistoryDisabled, - isKeyboardShortcutsDisabled, + isHistoryEnabled, + isKeyboardShortcutsEnabled, }: DevToolsSettings) { this.setFontSize(fontSize); this.setWrapMode(wrapMode); @@ -152,8 +172,8 @@ export class Settings { this.setAutocomplete(autocomplete); this.setPolling(polling); this.setPollInterval(pollInterval); - this.setIsHistoryDisabled(isHistoryDisabled); - this.setIsKeyboardShortcutsDisabled(isKeyboardShortcutsDisabled); + this.setIsHistoryEnabled(isHistoryEnabled); + this.setIsKeyboardShortcutsEnabled(isKeyboardShortcutsEnabled); } } 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/common/search/aggs/agg_config.test.ts b/src/plugins/data/common/search/aggs/agg_config.test.ts index bf6dfbd51b26a..6ea409e327d56 100644 --- a/src/plugins/data/common/search/aggs/agg_config.test.ts +++ b/src/plugins/data/common/search/aggs/agg_config.test.ts @@ -191,6 +191,85 @@ describe('AggConfig', () => { expect(dsl.aggs[medianConfig.id]).toHaveProperty('percentiles'); expect(dsl.aggs[medianConfig.id].percentiles).toBe(football); }); + + it('properly handles nested sibling pipeline aggregations', () => { + const customBucket = { + type: 'date_histogram', + params: { + field: '@timestamp', + interval: '1h', + }, + }; + const customMetric = { + type: 'avg_bucket', + params: { + customBucket: { + type: 'date_histogram', + params: { + field: '@timestamp', + interval: '30m', + }, + }, + customMetric: { + type: 'sum', + params: { + field: 'bytes', + }, + }, + }, + }; + const configStates = [ + { + type: 'avg_bucket', + params: { + customBucket, + customMetric, + }, + }, + ]; + const ac = new AggConfigs(indexPattern, configStates, { typesRegistry }, jest.fn()); + const dsl = ac.toDsl(); + + expect(dsl).toMatchInlineSnapshot(` + Object { + "1": Object { + "avg_bucket": Object { + "buckets_path": "1-bucket>1-metric", + }, + }, + "1-bucket": Object { + "aggs": Object { + "1-bucket": Object { + "aggs": Object { + "1-metric": Object { + "sum": Object { + "field": "bytes", + }, + }, + }, + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "30m", + "min_doc_count": 1, + "time_zone": "dateFormat:tz", + }, + }, + "1-metric": Object { + "avg_bucket": Object { + "buckets_path": "1-bucket>1-metric", + }, + }, + }, + "date_histogram": Object { + "calendar_interval": "1h", + "field": "@timestamp", + "min_doc_count": 1, + "time_zone": "dateFormat:tz", + }, + }, + } + `); + }); }); describe('::ensureIds', () => { diff --git a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts index 752bacd7554e5..fb47e53362826 100644 --- a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts +++ b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts @@ -6,21 +6,25 @@ * Side Public License, v 1. */ +import { siblingPipelineType } from '../../../..'; import { IMetricAggConfig } from '../metric_agg_type'; import { METRIC_TYPES } from '../metric_agg_types'; export const siblingPipelineAggWriter = (agg: IMetricAggConfig, output: Record) => { - const customMetric = agg.getParam('customMetric'); - - if (!customMetric) return; - - const metricAgg = customMetric; + const metricAgg = agg.getParam('customMetric'); const bucketAgg = agg.getParam('customBucket'); + if (!metricAgg) return; // if a bucket is selected, we must add this agg as a sibling to it, and add a metric to that bucket (or select one of its) if (metricAgg.type.name !== METRIC_TYPES.COUNT) { bucketAgg.subAggs = (output.subAggs || []).concat(metricAgg); output.params.buckets_path = `${bucketAgg.id}>${metricAgg.id}`; + + // If the metric is another nested sibling pipeline agg, we need to include it as a sub-agg of this agg's bucket agg + if (metricAgg.type.subtype === siblingPipelineType) { + const subAgg = metricAgg.getParam('customBucket'); + if (subAgg) bucketAgg.subAggs.push(subAgg); + } } else { output.params.buckets_path = bucketAgg.id + '>_count'; } diff --git a/src/plugins/data/common/search/session/status.ts b/src/plugins/data/common/search/session/status.ts index 790adbe7ba000..24fac7b69c2a3 100644 --- a/src/plugins/data/common/search/session/status.ts +++ b/src/plugins/data/common/search/session/status.ts @@ -13,3 +13,9 @@ export enum SearchSessionStatus { CANCELLED = 'cancelled', EXPIRED = 'expired', } + +export enum SearchStatus { + IN_PROGRESS = 'in_progress', + ERROR = 'error', + COMPLETE = 'complete', +} diff --git a/src/plugins/data/common/search/session/types.ts b/src/plugins/data/common/search/session/types.ts index cbe3de9be4c73..7824a1db6362e 100644 --- a/src/plugins/data/common/search/session/types.ts +++ b/src/plugins/data/common/search/session/types.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import type { SavedObjectsFindResponse } from '@kbn/core/server'; import { SerializableRecord } from '@kbn/utility-types'; -import { SearchSessionStatus } from './status'; +import type { SearchSessionStatus, SearchStatus } from './status'; export const SEARCH_SESSION_TYPE = 'search-session'; export interface SearchSessionSavedObjectAttributes { @@ -24,25 +25,12 @@ export interface SearchSessionSavedObjectAttributes { * Creation time of the session */ created: string; - /** - * Last touch time of the session - */ - touched: string; + /** * Expiration time of the session. Expiration itself is managed by Elasticsearch. */ expires: string; - /** - * Time of transition into completed state, - * - * Can be "null" in case already completed session - * transitioned into in-progress session - */ - completed?: string | null; - /** - * status - */ - status: SearchSessionStatus; + /** * locatorId (see share.url.locators service) */ @@ -62,10 +50,6 @@ export interface SearchSessionSavedObjectAttributes { */ idMapping: Record; - /** - * This value is true if the session was actively stored by the user. If it is false, the session may be purged by the system. - */ - persisted: boolean; /** * The realm type/name & username uniquely identifies the user who created this search session */ @@ -76,6 +60,11 @@ export interface SearchSessionSavedObjectAttributes { * Version information to display warnings when trying to restore a session from a different version */ version: string; + + /** + * `true` if session was cancelled + */ + isCanceled?: boolean; } export interface SearchSessionRequestInfo { @@ -87,20 +76,30 @@ export interface SearchSessionRequestInfo { * Search strategy used to submit the search request */ strategy: string; - /** - * status - */ - status: string; +} + +export interface SearchSessionRequestStatus { + status: SearchStatus; /** * An optional error. Set if status is set to error. */ error?: string; } -export interface SearchSessionFindOptions { - page?: number; - perPage?: number; - sortField?: string; - sortOrder?: string; - filter?: string; +/** + * On-the-fly calculated search session status + */ +export interface SearchSessionStatusResponse { + status: SearchSessionStatus; +} + +/** + * List of search session objects with on-the-fly calculated search session statuses + */ +export interface SearchSessionsFindResponse + extends SavedObjectsFindResponse { + /** + * Map containing calculated statuses of search sessions from the find response + */ + statuses: Record; } diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index 802af347b8e69..cedfa3ee02274 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -72,6 +72,11 @@ export interface IKibanaSearchResponse { */ isRestored?: boolean; + /** + * Indicates whether the search has been saved to a search-session object and long keepAlive was set + */ + isStored?: boolean; + /** * Optional warnings returned from Elasticsearch (for example, deprecation warnings) */ @@ -119,6 +124,11 @@ export interface ISearchOptions { */ isStored?: boolean; + /** + * Whether the search was successfully polled after session was saved. Search was added to a session saved object and keepAlive extended. + */ + isSearchStored?: boolean; + /** * Whether the session is restored (i.e. search requests should re-use the stored search IDs, * rather than starting from scratch) @@ -148,5 +158,11 @@ export interface ISearchOptions { */ export type ISearchOptionsSerializable = Pick< ISearchOptions, - 'strategy' | 'legacyHitsTotal' | 'sessionId' | 'isStored' | 'isRestore' | 'executionContext' + | 'strategy' + | 'legacyHitsTotal' + | 'sessionId' + | 'isStored' + | 'isSearchStored' + | 'isRestore' + | 'executionContext' >; diff --git a/src/plugins/data/config.ts b/src/plugins/data/config.ts index 0ec794d9bd63c..d4331ecd760b4 100644 --- a/src/plugins/data/config.ts +++ b/src/plugins/data/config.ts @@ -13,42 +13,13 @@ export const searchSessionsConfigSchema = schema.object({ * Turns the feature on \ off (incl. removing indicator and management screens) */ enabled: schema.boolean({ defaultValue: true }), - /** - * pageSize controls how many search session objects we load at once while monitoring - * session completion - */ - pageSize: schema.number({ defaultValue: 100 }), - /** - * trackingInterval controls how often we track persisted search session objects progress - */ - trackingInterval: schema.duration({ defaultValue: '10s' }), - - /** - * cleanupInterval controls how often we track non-persisted search session objects for cleanup - */ - cleanupInterval: schema.duration({ defaultValue: '60s' }), - - /** - * expireInterval controls how often we track persisted search session objects for expiration - */ - expireInterval: schema.duration({ defaultValue: '60m' }), - - /** - * monitoringTaskTimeout controls for how long task manager waits for search session monitoring task to complete before considering it timed out, - * If tasks timeouts it receives cancel signal and next task starts in "trackingInterval" time - */ - monitoringTaskTimeout: schema.duration({ defaultValue: '5m' }), /** - * notTouchedTimeout controls how long do we store unpersisted search session results, - * after the last search in the session has completed + * notTouchedTimeout controls how long user can save a session after all searches completed. + * The client continues to poll searches to keep the alive until this timeout hits */ notTouchedTimeout: schema.duration({ defaultValue: '5m' }), - /** - * notTouchedInProgressTimeout controls how long do allow a search session to run after - * a user has navigated away without persisting - */ - notTouchedInProgressTimeout: schema.duration({ defaultValue: '1m' }), + /** * maxUpdateRetries controls how many retries we perform while attempting to save a search session */ @@ -60,15 +31,15 @@ export const searchSessionsConfigSchema = schema.object({ defaultExpiration: schema.duration({ defaultValue: '7d' }), management: schema.object({ /** - * maxSessions controls how many saved search sessions we display per page on the management screen. + * maxSessions controls how many saved search sessions we load on the management screen. */ - maxSessions: schema.number({ defaultValue: 10000 }), + maxSessions: schema.number({ defaultValue: 100 }), /** - * refreshInterval controls how often we refresh the management screen. + * refreshInterval controls how often we refresh the management screen. 0s as duration means that auto-refresh is turned off. */ - refreshInterval: schema.duration({ defaultValue: '10s' }), + refreshInterval: schema.duration({ defaultValue: '0s' }), /** - * refreshTimeout controls how often we refresh the management screen. + * refreshTimeout controls the timeout for loading search sessions on mgmt screen */ refreshTimeout: schema.duration({ defaultValue: '1m' }), expiresSoonWarning: schema.duration({ defaultValue: '1d' }), diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts index 68f1ff1910c98..73c86d8845a18 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts @@ -548,6 +548,7 @@ describe('SearchInterceptor', () => { sessionId, isStored: true, isRestore: true, + isSearchStored: false, strategy: 'ese', }, }) @@ -732,17 +733,21 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); + const trackSearchComplete = jest.fn(); + sessionService.trackSearch.mockImplementation(() => ({ + complete: trackSearchComplete, + error: () => {}, + beforePoll: () => [{ isSearchStored: false }, () => {}], + })); const response = searchInterceptor.search({}, { pollInterval: 0, sessionId }); response.subscribe({ next, error }); await timeTravel(10); expect(sessionService.trackSearch).toBeCalledTimes(1); - expect(untrack).not.toBeCalled(); + expect(trackSearchComplete).not.toBeCalled(); await timeTravel(300); expect(sessionService.trackSearch).toBeCalledTimes(1); - expect(untrack).toBeCalledTimes(1); + expect(trackSearchComplete).toBeCalledTimes(1); }); test('session service should be able to cancel search', async () => { @@ -752,9 +757,6 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); - const response = searchInterceptor.search({}, { pollInterval: 0, sessionId }); response.subscribe({ next, error }); await timeTravel(10); @@ -778,9 +780,6 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); - const response1 = searchInterceptor.search( {}, { pollInterval: 0, sessionId: 'something different' } @@ -798,9 +797,6 @@ describe('SearchInterceptor', () => { sessionService.getSessionId.mockImplementation(() => undefined); sessionService.isCurrentSession.mockImplementation((_sessionId) => false); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); - const response1 = searchInterceptor.search( {}, { pollInterval: 0, sessionId: 'something different' } @@ -911,15 +907,22 @@ describe('SearchInterceptor', () => { expect(fetchMock).toBeCalledTimes(2); }); - test('should track searches that come from cache', async () => { + test('should not track searches that come from cache', async () => { mockFetchImplementation(partialCompleteResponse); sessionService.isCurrentSession.mockImplementation( (_sessionId) => _sessionId === sessionId ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); + const completeSearch = jest.fn(); + + sessionService.trackSearch.mockImplementation((params) => ({ + complete: completeSearch, + error: jest.fn(), + beforePoll: jest.fn(() => { + return [{ isSearchStored: false }, () => {}]; + }), + })); const req = { params: { @@ -932,14 +935,15 @@ describe('SearchInterceptor', () => { response.subscribe({ next, error, complete }); response2.subscribe({ next, error, complete }); await timeTravel(10); + expect(fetchMock).toBeCalledTimes(1); - expect(sessionService.trackSearch).toBeCalledTimes(2); - expect(untrack).not.toBeCalled(); + expect(sessionService.trackSearch).toBeCalledTimes(1); + expect(completeSearch).not.toBeCalled(); await timeTravel(300); // Should be called only 2 times (once per partial response) expect(fetchMock).toBeCalledTimes(2); - expect(sessionService.trackSearch).toBeCalledTimes(2); - expect(untrack).toBeCalledTimes(2); + expect(sessionService.trackSearch).toBeCalledTimes(1); + expect(completeSearch).toBeCalledTimes(1); expect(next).toBeCalledTimes(4); expect(error).toBeCalledTimes(0); @@ -1125,8 +1129,15 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); + const completeSearch = jest.fn(); + + sessionService.trackSearch.mockImplementation((params) => ({ + complete: completeSearch, + error: jest.fn(), + beforePoll: jest.fn(() => { + return [{ isSearchStored: false }, () => {}]; + }), + })); const req = { params: { @@ -1149,7 +1160,6 @@ describe('SearchInterceptor', () => { expect(error).toBeCalledTimes(0); expect(complete).toBeCalledTimes(0); expect(sessionService.trackSearch).toBeCalledTimes(1); - expect(untrack).not.toBeCalled(); const next2 = jest.fn(); const error2 = jest.fn(); @@ -1161,9 +1171,9 @@ describe('SearchInterceptor', () => { abortController.abort(); await timeTravel(300); - // Both searches should be tracked and untracked - expect(sessionService.trackSearch).toBeCalledTimes(2); - expect(untrack).toBeCalledTimes(2); + // Only first searches should be tracked and untracked + expect(sessionService.trackSearch).toBeCalledTimes(1); + expect(completeSearch).toBeCalledTimes(1); // First search should error expect(next).toBeCalledTimes(1); @@ -1186,8 +1196,15 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); + const completeSearch = jest.fn(); + + sessionService.trackSearch.mockImplementation((params) => ({ + complete: completeSearch, + error: jest.fn(), + beforePoll: jest.fn(() => { + return [{ isSearchStored: false }, () => {}]; + }), + })); const req = { params: { @@ -1206,7 +1223,7 @@ describe('SearchInterceptor', () => { expect(error).toBeCalledTimes(0); expect(complete).toBeCalledTimes(0); expect(sessionService.trackSearch).toBeCalledTimes(1); - expect(untrack).not.toBeCalled(); + expect(completeSearch).not.toBeCalled(); const next2 = jest.fn(); const error2 = jest.fn(); @@ -1222,8 +1239,8 @@ describe('SearchInterceptor', () => { abortController.abort(); await timeTravel(300); - expect(sessionService.trackSearch).toBeCalledTimes(2); - expect(untrack).toBeCalledTimes(2); + expect(sessionService.trackSearch).toBeCalledTimes(1); + expect(completeSearch).toBeCalledTimes(1); expect(next).toBeCalledTimes(2); expect(error).toBeCalledTimes(0); @@ -1243,7 +1260,6 @@ describe('SearchInterceptor', () => { (_sessionId) => _sessionId === sessionId ); sessionService.getSessionId.mockImplementation(() => sessionId); - sessionService.trackSearch.mockImplementation(() => jest.fn()); const req = { params: { @@ -1282,8 +1298,15 @@ describe('SearchInterceptor', () => { ); sessionService.getSessionId.mockImplementation(() => sessionId); - const untrack = jest.fn(); - sessionService.trackSearch.mockImplementation(() => untrack); + const completeSearch = jest.fn(); + + sessionService.trackSearch.mockImplementation((params) => ({ + complete: completeSearch, + error: jest.fn(), + beforePoll: jest.fn(() => { + return [{ isSearchStored: false }, () => {}]; + }), + })); const req = { params: { diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index ed21e4ef1d1e6..baa5eb30bca4c 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -7,7 +7,16 @@ */ import { memoize, once } from 'lodash'; -import { BehaviorSubject, EMPTY, from, fromEvent, of, Subscription, throwError } from 'rxjs'; +import { + BehaviorSubject, + EMPTY, + from, + fromEvent, + Observable, + of, + Subscription, + throwError, +} from 'rxjs'; import { catchError, filter, @@ -42,6 +51,7 @@ import { IAsyncSearchOptions, IKibanaSearchRequest, IKibanaSearchResponse, + isCompleteResponse, ISearchOptions, ISearchOptionsSerializable, pollSearch, @@ -146,18 +156,24 @@ export class SearchInterceptor { : TimeoutErrorMode.CONTACT; } - private createRequestHash$(request: IKibanaSearchRequest, options: IAsyncSearchOptions) { - const { sessionId, isRestore } = options; + private createRequestHash$( + request: IKibanaSearchRequest, + options: IAsyncSearchOptions + ): Observable { + const { sessionId } = options; // Preference is used to ensure all queries go to the same set of shards and it doesn't need to be hashed // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-shard-routing.html#shard-and-node-preference const { preference, ...params } = request.params || {}; const hashOptions = { ...params, sessionId, - isRestore, }; - return from(sessionId ? createRequestHash(hashOptions) : of(undefined)); + if (!sessionId) return of(undefined); // don't use cache if doesn't belong to a session + const sessionOptions = this.deps.session.getSearchOptions(options.sessionId); + if (sessionOptions?.isRestore) return of(undefined); // don't use cache if restoring a session + + return from(createRequestHash(hashOptions)); } /* @@ -206,6 +222,8 @@ export class SearchInterceptor { serializableOptions.legacyHitsTotal = combined.legacyHitsTotal; if (combined.strategy !== undefined) serializableOptions.strategy = combined.strategy; if (combined.isStored !== undefined) serializableOptions.isStored = combined.isStored; + if (combined.isSearchStored !== undefined) + serializableOptions.isSearchStored = combined.isSearchStored; if (combined.executionContext !== undefined) { serializableOptions.executionContext = combined.executionContext; } @@ -222,16 +240,47 @@ export class SearchInterceptor { options: IAsyncSearchOptions, searchAbortController: SearchAbortController ) { - const search = () => - this.runSearch( - { id, ...request }, - { ...options, abortSignal: searchAbortController.getSignal() } - ); const { sessionId, strategy } = options; + const search = () => { + const [{ isSearchStored }, afterPoll] = searchTracker?.beforePoll() ?? [ + { isSearchStored: false }, + ({ isSearchStored: boolean }) => {}, + ]; + return this.runSearch( + { id, ...request }, + { + ...options, + ...this.deps.session.getSearchOptions(sessionId), + abortSignal: searchAbortController.getSignal(), + isSearchStored, + } + ) + .then((result) => { + afterPoll({ isSearchStored: result.isStored ?? false }); + return result; + }) + .catch((err) => { + afterPoll({ isSearchStored: false }); + throw err; + }); + }; + + const searchTracker = this.deps.session.isCurrentSession(sessionId) + ? this.deps.session.trackSearch({ + abort: () => searchAbortController.abort(), + poll: async () => { + if (id) { + await search(); + } + }, + }) + : undefined; + // track if this search's session will be send to background // if yes, then we don't need to cancel this search when it is aborted - let isSavedToBackground = false; + let isSavedToBackground = + this.deps.session.isCurrentSession(sessionId) && this.deps.session.isStored(); const savedToBackgroundSub = this.deps.session.isCurrentSession(sessionId) && this.deps.session.state$ @@ -256,8 +305,15 @@ export class SearchInterceptor { ...options, abortSignal: searchAbortController.getSignal(), }).pipe( - tap((response) => (id = response.id)), + tap((response) => { + id = response.id; + + if (isCompleteResponse(response)) { + searchTracker?.complete(); + } + }), catchError((e: Error) => { + searchTracker?.error(); cancel(); return throwError(e); }), @@ -378,9 +434,6 @@ export class SearchInterceptor { ); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - const untrackSearch = this.deps.session.isCurrentSession(sessionId) - ? this.deps.session.trackSearch({ abort: () => searchAbortController.abort() }) - : undefined; // Abort the replay if the abortSignal is aborted. // The underlaying search will not abort unless searchAbortController fires. @@ -410,10 +463,6 @@ export class SearchInterceptor { }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); - if (untrackSearch && this.deps.session.isCurrentSession(sessionId)) { - // untrack if this search still belongs to current session - untrackSearch(); - } }) ); }) diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 3fba162d7173c..c88201405d7f0 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -23,7 +23,6 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { ManagementSetup } from '@kbn/management-plugin/public'; import { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import moment from 'moment'; import React from 'react'; import { BehaviorSubject } from 'rxjs'; import { @@ -118,7 +117,8 @@ export class SearchService implements Plugin { this.initializerContext, getStartServices, this.sessionsClient, - nowProvider + nowProvider, + this.usageCollector ); /** * A global object that intercepts all searches and provides convenience methods for cancelling @@ -256,9 +256,6 @@ export class SearchService implements Plugin { application, basePath: http.basePath, storage: new Storage(window.localStorage), - disableSaveAfterSessionCompletesTimeout: moment - .duration(config.search.sessions.notTouchedTimeout) - .asMilliseconds(), usageCollector: this.usageCollector, tourDisabled: screenshotMode.isScreenshotMode(), }) diff --git a/src/plugins/data/public/search/session/mocks.ts b/src/plugins/data/public/search/session/mocks.ts index c6706ff8cf72d..3c64267bd1782 100644 --- a/src/plugins/data/public/search/session/mocks.ts +++ b/src/plugins/data/public/search/session/mocks.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; import { ISessionsClient } from './sessions_client'; import { ISessionService } from './session_service'; import { SearchSessionState } from './search_session_state'; @@ -34,9 +34,17 @@ export function getSessionServiceMock(): jest.Mocked { state$: new BehaviorSubject(SearchSessionState.None).asObservable(), sessionMeta$: new BehaviorSubject({ state: SearchSessionState.None, + isContinued: false, }).asObservable(), + disableSaveAfterSearchesExpire$: of(false), renameCurrentSession: jest.fn(), - trackSearch: jest.fn((searchDescriptor) => () => {}), + trackSearch: jest.fn((searchDescriptor) => ({ + complete: jest.fn(), + error: jest.fn(), + beforePoll: jest.fn(() => { + return [{ isSearchStored: false }, () => {}]; + }), + })), destroy: jest.fn(), cancel: jest.fn(), isStored: jest.fn(), diff --git a/src/plugins/data/public/search/session/search_session_state.test.ts b/src/plugins/data/public/search/session/search_session_state.test.ts index 1137ceddb0da6..c85c635b7fc1a 100644 --- a/src/plugins/data/public/search/session/search_session_state.test.ts +++ b/src/plugins/data/public/search/session/search_session_state.test.ts @@ -8,7 +8,6 @@ import { createSessionStateContainer, SearchSessionState } from './search_session_state'; import type { SearchSessionSavedObject } from './sessions_client'; -import { SearchSessionStatus } from '../../../common'; const mockSavedObject: SearchSessionSavedObject = { id: 'd7170a35-7e2c-48d6-8dec-9a056721b489', @@ -19,11 +18,8 @@ const mockSavedObject: SearchSessionSavedObject = { locatorId: 'my_url_generator_id', idMapping: {}, sessionId: 'session_id', - touched: new Date().toISOString(), created: new Date().toISOString(), expires: new Date().toISOString(), - status: SearchSessionStatus.COMPLETE, - persisted: true, version: '8.0.0', }, references: [], @@ -46,7 +42,7 @@ describe('Session state container', () => { expect(state.get().appName).toBe(appName); }); - test('track', () => { + test('trackSearch', () => { expect(() => state.transitions.trackSearch({})).toThrowError(); state.transitions.start({ appName }); @@ -55,12 +51,12 @@ describe('Session state container', () => { expect(state.selectors.getState()).toBe(SearchSessionState.Loading); }); - test('untrack', () => { + test('removeSearch', () => { state.transitions.start({ appName }); const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Loading); - state.transitions.unTrackSearch(search); + state.transitions.removeSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Completed); }); @@ -95,7 +91,7 @@ describe('Session state container', () => { expect(state.selectors.getState()).toBe(SearchSessionState.Loading); state.transitions.store(mockSavedObject); expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundLoading); - state.transitions.unTrackSearch(search); + state.transitions.removeSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundCompleted); state.transitions.clear(); expect(state.selectors.getState()).toBe(SearchSessionState.None); @@ -124,7 +120,7 @@ describe('Session state container', () => { const search = {}; state.transitions.trackSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundLoading); - state.transitions.unTrackSearch(search); + state.transitions.removeSearch(search); expect(state.selectors.getState()).toBe(SearchSessionState.Restored); expect(() => state.transitions.store(mockSavedObject)).toThrowError(); diff --git a/src/plugins/data/public/search/session/search_session_state.ts b/src/plugins/data/public/search/session/search_session_state.ts index 329d40da89b60..9b4c5d3098cc0 100644 --- a/src/plugins/data/public/search/session/search_session_state.ts +++ b/src/plugins/data/public/search/session/search_session_state.ts @@ -57,13 +57,28 @@ export enum SearchSessionState { Canceled = 'canceled', } +/** + * State of the tracked search + */ +export enum TrackedSearchState { + InProgress = 'inProgress', + Completed = 'completed', + Errored = 'errored', +} + +export interface TrackedSearch { + state: TrackedSearchState; + searchDescriptor: SearchDescriptor; + searchMeta: SearchMeta; +} + /** * Internal state of SessionService * {@link SearchSessionState} is inferred from this state * * @private */ -export interface SessionStateInternal { +export interface SessionStateInternal { /** * Current session Id * Empty means there is no current active session. @@ -92,10 +107,9 @@ export interface SessionStateInternal { isRestore: boolean; /** - * Set of currently running searches - * within a session and any info associated with them + * Set of all searches within a session and any info associated with them */ - pendingSearches: SearchDescriptor[]; + trackedSearches: Array>; /** * There was at least a single search in this session @@ -107,6 +121,17 @@ export interface SessionStateInternal { */ isCanceled: boolean; + /** + * If session was continued from a different app, + * If session continued from a different app, then it is very likely that `trackedSearches` + * doesn't have all the search that were included into the session. + * Session that was continued can't be saved because we can't guarantee all the searches saved. + * This limitation should be fixed in https://github.com/elastic/kibana/issues/121543 + * + * @deprecated - https://github.com/elastic/kibana/issues/121543 + */ + isContinued: boolean; + /** * Start time of the current session (from browser perspective) */ @@ -124,27 +149,34 @@ export interface SessionStateInternal { } const createSessionDefaultState: < - SearchDescriptor = unknown ->() => SessionStateInternal = () => ({ + SearchDescriptor = unknown, + SearchMeta extends {} = {} +>() => SessionStateInternal = () => ({ sessionId: undefined, appName: undefined, isStored: false, isRestore: false, isCanceled: false, + isContinued: false, isStarted: false, - pendingSearches: [], + trackedSearches: [], }); export interface SessionPureTransitions< SearchDescriptor = unknown, - S = SessionStateInternal + SearchMeta extends {} = {}, + S = SessionStateInternal > { start: (state: S) => ({ appName }: { appName: string }) => S; restore: (state: S) => (sessionId: string) => S; clear: (state: S) => () => S; store: (state: S) => (searchSessionSavedObject: SearchSessionSavedObject) => S; - trackSearch: (state: S) => (search: SearchDescriptor) => S; - unTrackSearch: (state: S) => (search: SearchDescriptor) => S; + trackSearch: (state: S) => (search: SearchDescriptor, meta?: SearchMeta) => S; + removeSearch: (state: S) => (search: SearchDescriptor) => S; + completeSearch: (state: S) => (search: SearchDescriptor) => S; + errorSearch: (state: S) => (search: SearchDescriptor) => S; + updateSearchMeta: (state: S) => (search: SearchDescriptor, meta: Partial) => S; + cancel: (state: S) => () => S; setSearchSessionSavedObject: ( state: S @@ -177,21 +209,78 @@ export const sessionPureTransitions: SessionPureTransitions = { searchSessionSavedObject, }; }, - trackSearch: (state) => (search) => { - if (!state.sessionId) throw new Error("Can't track search. Missing sessionId"); + trackSearch: + (state) => + (search, meta = {}) => { + if (!state.sessionId) throw new Error("Can't track search. Missing sessionId"); + return { + ...state, + isStarted: true, + trackedSearches: state.trackedSearches.concat({ + state: TrackedSearchState.InProgress, + searchDescriptor: search, + searchMeta: meta, + }), + completedTime: undefined, + }; + }, + removeSearch: (state) => (search) => { + const trackedSearches = state.trackedSearches.filter((s) => s.searchDescriptor !== search); return { ...state, - isStarted: true, - pendingSearches: state.pendingSearches.concat(search), - completedTime: undefined, + trackedSearches, + completedTime: + trackedSearches.filter((s) => s.state !== TrackedSearchState.InProgress).length === 0 + ? new Date() + : state.completedTime, }; }, - unTrackSearch: (state) => (search) => { - const pendingSearches = state.pendingSearches.filter((s) => s !== search); + completeSearch: (state) => (search) => { return { ...state, - pendingSearches, - completedTime: pendingSearches.length === 0 ? new Date() : state.completedTime, + trackedSearches: state.trackedSearches.map((s) => { + if (s.searchDescriptor === search) { + return { + ...s, + state: TrackedSearchState.Completed, + }; + } + + return s; + }), + }; + }, + errorSearch: (state) => (search) => { + return { + ...state, + trackedSearches: state.trackedSearches.map((s) => { + if (s.searchDescriptor === search) { + return { + ...s, + state: TrackedSearchState.Errored, + }; + } + + return s; + }), + }; + }, + updateSearchMeta: (state) => (search, meta) => { + return { + ...state, + trackedSearches: state.trackedSearches.map((s) => { + if (s.searchDescriptor === search) { + return { + ...s, + searchMeta: { + ...s.searchMeta, + ...meta, + }, + }; + } + + return s; + }), }; }, cancel: (state) => () => { @@ -232,14 +321,23 @@ export interface SessionMeta { startTime?: Date; canceledTime?: Date; completedTime?: Date; + + /** + * @deprecated - see remarks in {@link SessionStateInternal} + */ + isContinued: boolean; } export interface SessionPureSelectors< SearchDescriptor = unknown, - S = SessionStateInternal + SearchMeta extends {} = {}, + S = SessionStateInternal > { getState: (state: S) => () => SearchSessionState; getMeta: (state: S) => () => SessionMeta; + getSearch: ( + state: S + ) => (search: SearchDescriptor) => TrackedSearch | null; } export const sessionPureSelectors: SessionPureSelectors = { @@ -247,17 +345,22 @@ export const sessionPureSelectors: SessionPureSelectors = { if (!state.sessionId) return SearchSessionState.None; if (!state.isStarted) return SearchSessionState.None; if (state.isCanceled) return SearchSessionState.Canceled; + + const pendingSearches = state.trackedSearches.filter( + (s) => s.state === TrackedSearchState.InProgress + ); + switch (true) { case state.isRestore: - return state.pendingSearches.length > 0 + return pendingSearches.length > 0 ? SearchSessionState.BackgroundLoading : SearchSessionState.Restored; case state.isStored: - return state.pendingSearches.length > 0 + return pendingSearches.length > 0 ? SearchSessionState.BackgroundLoading : SearchSessionState.BackgroundCompleted; default: - return state.pendingSearches.length > 0 + return pendingSearches.length > 0 ? SearchSessionState.Loading : SearchSessionState.Completed; } @@ -272,24 +375,31 @@ export const sessionPureSelectors: SessionPureSelectors = { startTime: state.searchSessionSavedObject?.attributes.created ? new Date(state.searchSessionSavedObject?.attributes.created) : state.startTime, - completedTime: state.searchSessionSavedObject?.attributes.completed - ? new Date(state.searchSessionSavedObject?.attributes.completed) - : state.completedTime, + completedTime: state.completedTime, canceledTime: state.canceledTime, + isContinued: state.isContinued, }); }, + getSearch(state) { + return (search) => { + return state.trackedSearches.find((s) => s.searchDescriptor === search) ?? null; + }; + }, }; -export type SessionStateContainer = StateContainer< - SessionStateInternal, - SessionPureTransitions, - SessionPureSelectors +export type SessionStateContainer< + SearchDescriptor = unknown, + SearchMeta extends {} = {} +> = StateContainer< + SessionStateInternal, + SessionPureTransitions, + SessionPureSelectors >; -export const createSessionStateContainer = ( +export const createSessionStateContainer = ( { freeze = true }: { freeze: boolean } = { freeze: true } ): { - stateContainer: SessionStateContainer; + stateContainer: SessionStateContainer; sessionState$: Observable; sessionMeta$: Observable; } => { @@ -298,7 +408,7 @@ export const createSessionStateContainer = ( sessionPureTransitions, sessionPureSelectors, freeze ? undefined : { freeze: (s) => s } - ) as SessionStateContainer; + ) as unknown as SessionStateContainer; const sessionMeta$: Observable = stateContainer.state$.pipe( map(() => stateContainer.selectors.getMeta()), diff --git a/src/plugins/data/public/search/session/session_helpers.test.ts b/src/plugins/data/public/search/session/session_helpers.test.ts index c06185d438401..bc092a4f6ac3f 100644 --- a/src/plugins/data/public/search/session/session_helpers.test.ts +++ b/src/plugins/data/public/search/session/session_helpers.test.ts @@ -23,7 +23,13 @@ let nowProvider: jest.Mocked; let currentAppId$: BehaviorSubject; beforeEach(() => { - const initializerContext = coreMock.createPluginInitializerContext(); + const initializerContext = coreMock.createPluginInitializerContext({ + search: { + sessions: { + notTouchedTimeout: '5m', + }, + }, + }); const startService = coreMock.createSetup().getStartServices; nowProvider = createNowProviderMock(); currentAppId$ = new BehaviorSubject('app'); @@ -50,6 +56,7 @@ beforeEach(() => { ]), getSessionsClientMock(), nowProvider, + undefined, { freezeState: false } // needed to use mocks inside state container ); state$ = new BehaviorSubject(SearchSessionState.None); @@ -67,8 +74,13 @@ describe('waitUntilNextSessionCompletes$', () => { 'emits when next session starts', fakeSchedulers((advance) => { sessionService.start(); - let untrackSearch = sessionService.trackSearch({ abort: () => {} }); - untrackSearch(); + + let { complete: completeSearch } = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }); + + completeSearch(); const next = jest.fn(); const complete = jest.fn(); @@ -78,8 +90,12 @@ describe('waitUntilNextSessionCompletes$', () => { sessionService.start(); expect(next).not.toBeCalled(); - untrackSearch = sessionService.trackSearch({ abort: () => {} }); - untrackSearch(); + completeSearch = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; + + completeSearch(); expect(next).not.toBeCalled(); advance(500); diff --git a/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.test.tsx b/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.test.tsx index d791cf51ddbe6..9f29e2866fb61 100644 --- a/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.test.tsx +++ b/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.test.tsx @@ -40,7 +40,6 @@ const timeFilter = dataStart.query.timefilter.timefilter as jest.Mocked refreshInterval$.pipe(map(() => {}))); timeFilter.getRefreshInterval.mockImplementation(() => refreshInterval$.getValue()); -const disableSaveAfterSessionCompletesTimeout = 5 * 60 * 1000; const tourDisabled = false; function Container({ children }: { children?: ReactNode }) { @@ -64,7 +63,6 @@ test("shouldn't show indicator in case no active search session", async () => { sessionService, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -93,7 +91,6 @@ test("shouldn't show indicator in case app hasn't opt-in", async () => { sessionService, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -124,7 +121,6 @@ test('should show indicator in case there is an active search session', async () sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -150,7 +146,6 @@ test('should be disabled in case uiConfig says so ', async () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -175,7 +170,6 @@ test('should be disabled in case not enough permissions', async () => { sessionService: { ...sessionService, state$, hasAccess: () => false }, application, storage, - disableSaveAfterSessionCompletesTimeout, basePath, tourDisabled, }); @@ -195,20 +189,15 @@ test('should be disabled in case not enough permissions', async () => { }); describe('Completed inactivity', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - afterEach(() => { - jest.useRealTimers(); - }); test('save should be disabled after completed and timeout', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); + const disableSaveAfterSearchesExpire$ = new BehaviorSubject(false); + const SearchSessionIndicator = createConnectedSearchSessionIndicator({ - sessionService: { ...sessionService, state$ }, + sessionService: { ...sessionService, state$, disableSaveAfterSearchesExpire$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -227,30 +216,10 @@ describe('Completed inactivity', () => { expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled(); act(() => { - jest.advanceTimersByTime(5 * 60 * 1000); - }); - - expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled(); - - act(() => { - state$.next(SearchSessionState.Completed); - }); - - expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled(); - - act(() => { - jest.advanceTimersByTime(2.5 * 60 * 1000); - }); - - expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled(); - expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(0); - - act(() => { - jest.advanceTimersByTime(2.5 * 60 * 1000); + disableSaveAfterSearchesExpire$.next(true); }); expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled(); - expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(1); }); }); @@ -270,7 +239,6 @@ describe('tour steps', () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -312,7 +280,6 @@ describe('tour steps', () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -347,7 +314,6 @@ describe('tour steps', () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled: true, @@ -393,7 +359,6 @@ describe('tour steps', () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -421,7 +386,6 @@ describe('tour steps', () => { sessionService: { ...sessionService, state$ }, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, diff --git a/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.tsx b/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.tsx index 727a188fc2c00..9a23342d58c62 100644 --- a/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.tsx +++ b/src/plugins/data/public/search/session/session_indicator/connected_search_session_indicator/connected_search_session_indicator.tsx @@ -7,8 +7,8 @@ */ import React, { useCallback, useEffect, useState } from 'react'; -import { debounce, distinctUntilChanged, mapTo, switchMap, tap } from 'rxjs/operators'; -import { merge, of, timer } from 'rxjs'; +import { debounce } from 'rxjs/operators'; +import { timer } from 'rxjs'; import useObservable from 'react-use/lib/useObservable'; import { i18n } from '@kbn/i18n'; import { RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; @@ -25,11 +25,6 @@ export interface SearchSessionIndicatorDeps { application: ApplicationStart; basePath: IBasePath; storage: IStorageWrapper; - /** - * Controls for how long we allow to save a session, - * after the last search in the session has completed - */ - disableSaveAfterSessionCompletesTimeout: number; tourDisabled: boolean; usageCollector?: SearchUsageCollector; } @@ -38,7 +33,6 @@ export const createConnectedSearchSessionIndicator = ({ sessionService, application, storage, - disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, tourDisabled, @@ -49,25 +43,14 @@ export const createConnectedSearchSessionIndicator = ({ debounce((_state) => timer(_state === SearchSessionState.None ? 50 : 300)) // switch to None faster to quickly remove indicator when navigating away ); - const disableSaveAfterSessionCompleteTimedOut$ = sessionService.state$.pipe( - switchMap((_state) => - _state === SearchSessionState.Completed - ? merge(of(false), timer(disableSaveAfterSessionCompletesTimeout).pipe(mapTo(true))) - : of(false) - ), - distinctUntilChanged(), - tap((value) => { - if (value) usageCollector?.trackSessionIndicatorSaveDisabled(); - }) - ); - return () => { const state = useObservable(debouncedSessionServiceState$, SearchSessionState.None); const isSaveDisabledByApp = sessionService.getSearchSessionIndicatorUiConfig().isDisabled(); - const disableSaveAfterSessionCompleteTimedOut = useObservable( - disableSaveAfterSessionCompleteTimedOut$, + const disableSaveAfterSearchesExpire = useObservable( + sessionService.disableSaveAfterSearchesExpire$, false ); + const [searchSessionIndicator, setSearchSessionIndicator] = useState(null); const searchSessionIndicatorRef = useCallback((ref: SearchSessionIndicatorRef) => { @@ -82,7 +65,7 @@ export const createConnectedSearchSessionIndicator = ({ let managementDisabled = false; let managementDisabledReasonText: string = ''; - if (disableSaveAfterSessionCompleteTimedOut) { + if (disableSaveAfterSearchesExpire) { saveDisabled = true; saveDisabledReasonText = i18n.translate( 'data.searchSessionIndicator.disabledDueToTimeoutMessage', @@ -160,7 +143,7 @@ export const createConnectedSearchSessionIndicator = ({ startTime, completedTime, canceledTime, - } = useObservable(sessionService.sessionMeta$, { state }); + } = useObservable(sessionService.sessionMeta$, { state, isContinued: false }); const saveSearchSessionNameFn = useCallback(async (newName: string) => { await sessionService.renameCurrentSession(newName); }, []); diff --git a/src/plugins/data/public/search/session/session_indicator/search_session_indicator/search_session_indicator.tsx b/src/plugins/data/public/search/session/session_indicator/search_session_indicator/search_session_indicator.tsx index 239a4ea4c2aa7..465c447039e5e 100644 --- a/src/plugins/data/public/search/session/session_indicator/search_session_indicator/search_session_indicator.tsx +++ b/src/plugins/data/public/search/session/session_indicator/search_session_indicator/search_session_indicator.tsx @@ -75,7 +75,7 @@ const ContinueInBackgroundButton = ({ diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index d377cca07a738..7517174698d61 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -6,18 +6,19 @@ * Side Public License, v 1. */ -import { SessionService, ISessionService } from './session_service'; +import { ISessionService, SessionService } from './session_service'; import { coreMock } from '@kbn/core/public/mocks'; -import { take, toArray } from 'rxjs/operators'; +import { first, take, toArray } from 'rxjs/operators'; import { getSessionsClientMock } from './mocks'; import { BehaviorSubject } from 'rxjs'; import { SearchSessionState } from './search_session_state'; import { createNowProviderMock } from '../../now_provider/mocks'; import { NowProviderInternalContract } from '../../now_provider'; import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants'; -import type { SearchSessionSavedObject, ISessionsClient } from './sessions_client'; -import { SearchSessionStatus } from '../../../common'; +import type { ISessionsClient, SearchSessionSavedObject } from './sessions_client'; import { CoreStart } from '@kbn/core/public'; +import { SearchUsageCollector } from '../..'; +import { createSearchUsageCollectorMock } from '../collectors/mocks'; const mockSavedObject: SearchSessionSavedObject = { id: 'd7170a35-7e2c-48d6-8dec-9a056721b489', @@ -28,11 +29,8 @@ const mockSavedObject: SearchSessionSavedObject = { locatorId: 'my_locator_id', idMapping: {}, sessionId: 'session_id', - touched: new Date().toISOString(), created: new Date().toISOString(), expires: new Date().toISOString(), - status: SearchSessionStatus.COMPLETE, - persisted: true, version: '8.0.0', }, references: [], @@ -46,9 +44,16 @@ describe('Session service', () => { let currentAppId$: BehaviorSubject; let toastService: jest.Mocked; let sessionsClient: jest.Mocked; + let usageCollector: jest.Mocked; beforeEach(() => { - const initializerContext = coreMock.createPluginInitializerContext(); + const initializerContext = coreMock.createPluginInitializerContext({ + search: { + sessions: { + notTouchedTimeout: 5 * 60 * 1000, + }, + }, + }); const startService = coreMock.createSetup().getStartServices; const startServicesMock = coreMock.createStart(); toastService = startServicesMock.notifications.toasts; @@ -60,6 +65,7 @@ describe('Session service', () => { id, attributes: { ...mockSavedObject.attributes, sessionId: id }, })); + usageCollector = createSearchUsageCollectorMock(); sessionService = new SessionService( initializerContext, () => @@ -83,6 +89,7 @@ describe('Session service', () => { ]), sessionsClient, nowProvider, + usageCollector, { freezeState: false } // needed to use mocks inside state container ); state$ = new BehaviorSubject(SearchSessionState.None); @@ -132,34 +139,86 @@ describe('Session service', () => { }); it('Tracks searches for current session', () => { - expect(() => sessionService.trackSearch({ abort: () => {} })).toThrowError(); + expect(() => + sessionService.trackSearch({ abort: () => {}, poll: async () => {} }) + ).toThrowError(); expect(state$.getValue()).toBe(SearchSessionState.None); sessionService.start(); - const untrack1 = sessionService.trackSearch({ abort: () => {} }); + const complete1 = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; expect(state$.getValue()).toBe(SearchSessionState.Loading); - const untrack2 = sessionService.trackSearch({ abort: () => {} }); + const complete2 = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; + expect(state$.getValue()).toBe(SearchSessionState.Loading); - untrack1(); + complete1(); expect(state$.getValue()).toBe(SearchSessionState.Loading); - untrack2(); + complete2(); expect(state$.getValue()).toBe(SearchSessionState.Completed); }); it('Cancels all tracked searches within current session', async () => { const abort = jest.fn(); + const poll = jest.fn(); sessionService.start(); - sessionService.trackSearch({ abort }); - sessionService.trackSearch({ abort }); - sessionService.trackSearch({ abort }); - const untrack = sessionService.trackSearch({ abort }); + sessionService.trackSearch({ abort, poll }); + sessionService.trackSearch({ abort, poll }); + sessionService.trackSearch({ abort, poll }); + const complete = sessionService.trackSearch({ abort, poll }).complete; + complete(); - untrack(); await sessionService.cancel(); expect(abort).toBeCalledTimes(3); }); + + describe('Keeping searches alive', () => { + let dateNowSpy: jest.SpyInstance; + let now = Date.now(); + const advanceTimersBy = (by: number) => { + now = now + by; + jest.advanceTimersByTime(by); + }; + beforeEach(() => { + dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => now); + now = Date.now(); + jest.useFakeTimers(); + }); + afterEach(() => { + dateNowSpy.mockRestore(); + jest.useRealTimers(); + }); + + it('Polls all completed searches to keep them alive', async () => { + const abort = jest.fn(); + const poll = jest.fn(() => Promise.resolve()); + + sessionService.enableStorage({ + getName: async () => 'Name', + getLocatorData: async () => ({ + id: 'id', + initialState: {}, + restoreState: {}, + }), + }); + sessionService.start(); + + const searchTracker = sessionService.trackSearch({ abort, poll }); + searchTracker.complete(); + + expect(poll).toHaveBeenCalledTimes(0); + + advanceTimersBy(30000); + + expect(poll).toHaveBeenCalledTimes(1); + }); + }); }); it('Can continue previous session from another app', async () => { @@ -171,6 +230,7 @@ describe('Session service', () => { sessionService.continue(sessionId!); expect(sessionService.getSessionId()).toBe(sessionId); + expect((await sessionService.sessionMeta$.pipe(first()).toPromise())!.isContinued).toBe(true); }); it('Calling clear() more than once still allows previous session from another app to continue', async () => { @@ -213,7 +273,7 @@ describe('Session service', () => { it('Continue drops client side loading state', async () => { const sessionId = sessionService.start(); - sessionService.trackSearch({ abort: () => {} }); + sessionService.trackSearch({ abort: () => {}, poll: async () => {} }); expect(state$.getValue()).toBe(SearchSessionState.Loading); sessionService.clear(); // even allow to call clear multiple times @@ -389,4 +449,96 @@ describe('Session service', () => { }) ); }); + + describe('disableSaveAfterSearchesExpire$', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + }); + + test('disables save after session completes on timeout', async () => { + const emitResult: boolean[] = []; + sessionService.disableSaveAfterSearchesExpire$.subscribe((result) => { + emitResult.push(result); + }); + + sessionService.start(); + const complete = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; + + complete(); + + expect(emitResult).toEqual([false]); + + jest.advanceTimersByTime(2 * 60 * 1000); // 2 minutes + + expect(emitResult).toEqual([false]); + + jest.advanceTimersByTime(3 * 60 * 1000); // 3 minutes + + expect(emitResult).toEqual([false, true]); + + sessionService.start(); + + expect(emitResult).toEqual([false, true, false]); + }); + + test('disables save for continued from different app sessions', async () => { + const emitResult: boolean[] = []; + sessionService.disableSaveAfterSearchesExpire$.subscribe((result) => { + emitResult.push(result); + }); + + const sessionId = sessionService.start(); + + const complete = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; + + complete(); + + expect(emitResult).toEqual([false]); + + sessionService.clear(); + + sessionService.continue(sessionId); + + expect(emitResult).toEqual([false, true]); + + sessionService.start(); + + expect(emitResult).toEqual([false, true, false]); + }); + + test('emits usage once', async () => { + const emitResult: boolean[] = []; + sessionService.disableSaveAfterSearchesExpire$.subscribe((result) => { + emitResult.push(result); + }); + sessionService.disableSaveAfterSearchesExpire$.subscribe(); // testing that source is shared + + sessionService.start(); + const complete = sessionService.trackSearch({ + abort: () => {}, + poll: async () => {}, + }).complete; + + expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(0); + + complete(); + + jest.advanceTimersByTime(5 * 60 * 1000); // 5 minutes + + expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(1); + + sessionService.start(); + + expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/plugins/data/public/search/session/session_service.ts b/src/plugins/data/public/search/session/session_service.ts index 4b5d4da33132d..e71b8ed321825 100644 --- a/src/plugins/data/public/search/session/session_service.ts +++ b/src/plugins/data/public/search/session/session_service.ts @@ -7,32 +7,116 @@ */ import { PublicContract, SerializableRecord } from '@kbn/utility-types'; -import { distinctUntilChanged, map, startWith } from 'rxjs/operators'; -import { Observable, Subscription } from 'rxjs'; +import { + distinctUntilChanged, + filter, + map, + mapTo, + mergeMap, + repeat, + startWith, + switchMap, + takeUntil, + tap, +} from 'rxjs/operators'; +import { + BehaviorSubject, + combineLatest, + EMPTY, + from, + merge, + Observable, + of, + Subscription, + timer, +} from 'rxjs'; import { PluginInitializerContext, StartServicesAccessor, ToastsStart as ToastService, } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { SearchUsageCollector } from '../..'; import { ConfigSchema } from '../../../config'; -import { createSessionStateContainer } from './search_session_state'; import type { - SearchSessionState, SessionMeta, SessionStateContainer, SessionStateInternal, } from './search_session_state'; +import { + createSessionStateContainer, + SearchSessionState, + TrackedSearchState, +} from './search_session_state'; import { ISessionsClient } from './sessions_client'; import { ISearchOptions } from '../../../common'; import { NowProviderInternalContract } from '../../now_provider'; import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants'; import { formatSessionName } from './lib/session_name_formatter'; +/** + * Polling interval for keeping completed searches alive + * until the user saves the session + */ +const KEEP_ALIVE_COMPLETED_SEARCHES_INTERVAL = 30000; + export type ISessionService = PublicContract; interface TrackSearchDescriptor { + /** + * Cancel the search + */ abort: () => void; + + /** + * Keep polling the search to keep it alive + */ + poll: () => Promise; + + /** + * Notify search that session is being saved, could be used to restart the search with different params + * @deprecated - this is used as an escape hatch for TSVB/Timelion to restart a search with different params + */ + onSavingSession?: ( + options: Required> + ) => Promise; +} + +interface TrackSearchMeta { + /** + * Time that indicates when last time this search was polled + */ + lastPollingTime: Date; + + /** + * If the keep_alive of this search was extended up to saved session keep_alive + */ + isStored: boolean; +} + +/** + * Api to manage tracked search + */ +interface TrackSearchHandler { + /** + * Transition search into "complete" status + */ + complete(): void; + + /** + * Transition search into "error" status + */ + error(): void; + + /** + * Call to notify when search is about to be polled to get current search state to build `searchOptions` from (mainly isSearchStored), + * When poll completes or errors, call `afterPoll` callback and confirm is search was successfully stored + */ + beforePoll(): [ + currentSearchState: { isSearchStored: boolean }, + afterPoll: (newSearchState: { isSearchStored: boolean }) => void + ]; } /** @@ -82,9 +166,26 @@ interface SearchSessionIndicatorUiConfig { */ export class SessionService { public readonly state$: Observable; - private readonly state: SessionStateContainer; + private readonly state: SessionStateContainer; public readonly sessionMeta$: Observable; + + /** + * Emits `true` when session completes and `config.search.sessions.notTouchedTimeout` duration has passed. + * Used to stop keeping searches alive after some times and disabled "save session" button + * + * or when failed to extend searches after session completes + */ + private readonly _disableSaveAfterSearchesExpire$ = new BehaviorSubject(false); + + /** + * Emits `true` when it is no longer possible to save a session: + * - Failed to keep searches alive after they completed + * - `config.search.sessions.notTouchedTimeout` after searches completed hit + * - Continued session from a different app and lost information about previous searches (https://github.com/elastic/kibana/issues/121543) + */ + public readonly disableSaveAfterSearchesExpire$: Observable; + private searchSessionInfoProvider?: SearchSessionInfoProvider; private searchSessionIndicatorUiConfig?: Partial; private subscription = new Subscription(); @@ -105,16 +206,50 @@ export class SessionService { getStartServices: StartServicesAccessor, private readonly sessionsClient: ISessionsClient, private readonly nowProvider: NowProviderInternalContract, + private readonly usageCollector?: SearchUsageCollector, { freezeState = true }: { freezeState: boolean } = { freezeState: true } ) { - const { stateContainer, sessionState$, sessionMeta$ } = - createSessionStateContainer({ - freeze: freezeState, - }); + const { stateContainer, sessionState$, sessionMeta$ } = createSessionStateContainer< + TrackSearchDescriptor, + TrackSearchMeta + >({ + freeze: freezeState, + }); this.state$ = sessionState$; this.state = stateContainer; this.sessionMeta$ = sessionMeta$; + this.disableSaveAfterSearchesExpire$ = combineLatest([ + this._disableSaveAfterSearchesExpire$, + this.sessionMeta$.pipe(map((meta) => meta.isContinued)), + ]).pipe( + map( + ([_disableSaveAfterSearchesExpire, isSessionContinued]) => + _disableSaveAfterSearchesExpire || isSessionContinued + ), + distinctUntilChanged() + ); + + const notTouchedTimeout = moment + .duration(initializerContext.config.get().search.sessions.notTouchedTimeout) + .asMilliseconds(); + + this.subscription.add( + this.state$ + .pipe( + switchMap((_state) => + _state === SearchSessionState.Completed + ? merge(of(false), timer(notTouchedTimeout).pipe(mapTo(true))) + : of(false) + ), + distinctUntilChanged(), + tap((value) => { + if (value) this.usageCollector?.trackSessionIndicatorSaveDisabled(); + }) + ) + .subscribe(this._disableSaveAfterSearchesExpire$) + ); + this.subscription.add( sessionMeta$ .pipe( @@ -155,6 +290,54 @@ export class SessionService { }) ); }); + + // keep completed searches alive until user explicitly saves the session + this.subscription.add( + this.getSession$() + .pipe( + switchMap((sessionId) => { + if (!sessionId) return EMPTY; + if (this.isStored()) return EMPTY; // no need to keep searches alive because session and searches are already stored + if (!this.hasAccess()) return EMPTY; // don't need to keep searches alive if the user can't save session + if (!this.isSessionStorageReady()) return EMPTY; // don't need to keep searches alive if app doesn't allow saving session + + const schedulePollSearches = () => { + return timer(KEEP_ALIVE_COMPLETED_SEARCHES_INTERVAL).pipe( + mergeMap(() => { + const searchesToKeepAlive = this.state.get().trackedSearches.filter( + (s) => + !s.searchMeta.isStored && + s.state === TrackedSearchState.Completed && + s.searchMeta.lastPollingTime.getTime() < Date.now() - 5000 // don't poll if was very recently polled + ); + + return from( + Promise.all( + searchesToKeepAlive.map((s) => + s.searchDescriptor.poll().catch((e) => { + // eslint-disable-next-line no-console + console.warn( + `Error while polling search to keep it alive. Considering that it is no longer possible to extend a session.`, + e + ); + if (this.isCurrentSession(sessionId)) { + this._disableSaveAfterSearchesExpire$.next(true); + } + }) + ) + ) + ); + }), + repeat(), + takeUntil(this.disableSaveAfterSearchesExpire$.pipe(filter((disable) => disable))) + ); + }; + + return schedulePollSearches(); + }) + ) + .subscribe(() => {}) + ); } /** @@ -167,15 +350,51 @@ export class SessionService { } /** - * Used to track pending searches within current session + * Used to track searches within current session * - * @param searchDescriptor - uniq object that will be used to untrack the search - * @returns untrack function + * @param searchDescriptor - uniq object that will be used to as search identifier + * @returns {@link TrackSearchHandler} */ - public trackSearch(searchDescriptor: TrackSearchDescriptor): () => void { - this.state.transitions.trackSearch(searchDescriptor); - return () => { - this.state.transitions.unTrackSearch(searchDescriptor); + public trackSearch(searchDescriptor: TrackSearchDescriptor): TrackSearchHandler { + this.state.transitions.trackSearch(searchDescriptor, { + lastPollingTime: new Date(), + isStored: false, + }); + + return { + complete: () => { + this.state.transitions.completeSearch(searchDescriptor); + + // when search completes and session has just been saved, + // trigger polling once again to save search into a session and extend its keep_alive + if (this.isStored()) { + const search = this.state.selectors.getSearch(searchDescriptor); + if (search && !search.searchMeta.isStored) { + search.searchDescriptor.poll().catch((e) => { + // eslint-disable-next-line no-console + console.warn(`Failed to extend search after it was completed`, e); + }); + } + } + }, + error: () => { + this.state.transitions.errorSearch(searchDescriptor); + }, + beforePoll: () => { + const search = this.state.selectors.getSearch(searchDescriptor); + this.state.transitions.updateSearchMeta(searchDescriptor, { + lastPollingTime: new Date(), + }); + + return [ + { isSearchStored: search?.searchMeta?.isStored ?? false }, + ({ isSearchStored }) => { + this.state.transitions.updateSearchMeta(searchDescriptor, { + isStored: isSearchStored, + }); + }, + ]; + }, }; } @@ -244,6 +463,12 @@ export class SessionService { * * This is different from {@link restore} as it reuses search session state and search results held in client memory instead of restoring search results from elasticsearch * @param sessionId + * + * TODO: remove this functionality in favor of separate architecture for client side search cache + * that won't interfere with saving search sessions + * https://github.com/elastic/kibana/issues/121543 + * + * @deprecated */ public continue(sessionId: string) { if (this.lastSessionSnapshot?.sessionId === sessionId) { @@ -254,7 +479,8 @@ export class SessionService { // also have to drop all pending searches which are used to derive client side state of search session indicator, // if we weren't dropping this searches, then we would get into "infinite loading" state when continuing a session that was cleared with pending searches // possible solution to this problem is to refactor session service to support multiple sessions - pendingSearches: [], + trackedSearches: [], + isContinued: true, }); this.lastSessionSnapshot = undefined; } else { @@ -293,10 +519,13 @@ export class SessionService { * Request a cancellation of on-going search requests within current session */ public async cancel(): Promise { - const isStoredSession = this.state.get().isStored; - this.state.get().pendingSearches.forEach((s) => { - s.abort(); - }); + const isStoredSession = this.isStored(); + this.state + .get() + .trackedSearches.filter((s) => s.state === TrackedSearchState.InProgress) + .forEach((s) => { + s.searchDescriptor.abort(); + }); this.state.transitions.cancel(); if (isStoredSession) { await this.sessionsClient.delete(this.state.get().sessionId!); @@ -335,8 +564,41 @@ export class SessionService { }); // if we are still interested in this result - if (this.getSessionId() === sessionId) { + if (this.isCurrentSession(sessionId)) { this.state.transitions.store(searchSessionSavedObject); + + // trigger new poll for all completed searches that are not stored to propogate them into newly creates search session saved object and extend their keepAlive + const completedSearches = this.state + .get() + .trackedSearches.filter( + (s) => s.state === TrackedSearchState.Completed && !s.searchMeta.isStored + ); + const pollCompletedSearchesPromise = Promise.all( + completedSearches.map((s) => + s.searchDescriptor.poll().catch((e) => { + // eslint-disable-next-line no-console + console.warn('Failed to extend search after session was saved', e); + }) + ) + ); + + // notify all the searches with onSavingSession that session has been saved and saved object has been created + // don't wait for the result + const searchesWithSavingHandler = this.state + .get() + .trackedSearches.filter((s) => s.searchDescriptor.onSavingSession); + searchesWithSavingHandler.forEach((s) => + s.searchDescriptor.onSavingSession!({ + sessionId, + isRestore: this.isRestore(), + isStored: this.isStored(), + }).catch((e) => { + // eslint-disable-next-line no-console + console.warn('Failed to execute "onSavingSession" handler after session was saved', e); + }) + ); + + await pollCompletedSearchesPromise; } } diff --git a/src/plugins/data/public/search/session/sessions_client.ts b/src/plugins/data/public/search/session/sessions_client.ts index 12fd424e57dd8..80fe88c22a951 100644 --- a/src/plugins/data/public/search/session/sessions_client.ts +++ b/src/plugins/data/public/search/session/sessions_client.ts @@ -14,7 +14,10 @@ import type { SavedObjectsUpdateResponse, SavedObjectsFindOptions, } from '@kbn/core/server'; -import type { SearchSessionSavedObjectAttributes } from '../../../common'; +import type { + SearchSessionSavedObjectAttributes, + SearchSessionsFindResponse, +} from '../../../common'; export type SearchSessionSavedObject = SavedObject; export type ISessionsClient = PublicContract; export interface SessionsClientDeps { @@ -62,7 +65,7 @@ export class SessionsClient { }); } - public find(options: Omit): Promise { + public find(options: Omit): Promise { return this.http!.post(`/internal/session/_find`, { body: JSON.stringify(options), }); diff --git a/src/plugins/data/public/search/session/sessions_mgmt/components/table/app_filter.tsx b/src/plugins/data/public/search/session/sessions_mgmt/components/table/app_filter.tsx index 48cfda93e1f91..05070814d9a5d 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/components/table/app_filter.tsx +++ b/src/plugins/data/public/search/session/sessions_mgmt/components/table/app_filter.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FieldValueOptionType, SearchFilterConfig } from '@elastic/eui'; +import { SearchFilterConfig } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { capitalize } from 'lodash'; import { UISession } from '../../types'; @@ -18,12 +18,7 @@ export const getAppFilter: (tableData: UISession[]) => SearchFilterConfig = (tab }), field: 'appId', multiSelect: 'or', - options: tableData.reduce((options: FieldValueOptionType[], { appId }) => { - const existingOption = options.find((o) => o.value === appId); - if (!existingOption) { - return [...options, { value: appId, view: capitalize(appId) }]; - } - - return options; - }, []), + options: [...new Set(tableData.map((data) => data.appId ?? 'unknown'))] + .sort() + .map((appId) => ({ value: appId, view: capitalize(appId) })), }); diff --git a/src/plugins/data/public/search/session/sessions_mgmt/components/table/status_filter.tsx b/src/plugins/data/public/search/session/sessions_mgmt/components/table/status_filter.tsx index 25fc7dc092ab8..bf34b72c7971f 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/components/table/status_filter.tsx +++ b/src/plugins/data/public/search/session/sessions_mgmt/components/table/status_filter.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FieldValueOptionType, SearchFilterConfig } from '@elastic/eui'; +import { SearchFilterConfig } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { TableText } from '..'; @@ -20,14 +20,7 @@ export const getStatusFilter: (tableData: UISession[]) => SearchFilterConfig = ( }), field: 'status', multiSelect: 'or', - options: tableData.reduce((options: FieldValueOptionType[], session) => { - const { status: statusType } = session; - const existingOption = options.find((o) => o.value === statusType); - if (!existingOption) { - const view = {getStatusText(session.status)}; - return [...options, { value: statusType, view }]; - } - - return options; - }, []), + options: [...new Set(tableData.map((data) => data.status ?? 'unknown'))] + .sort() + .map((status) => ({ value: status, view: {getStatusText(status)} })), }); diff --git a/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.test.tsx b/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.test.tsx index 1fe4dbe0468ee..ac554af701d04 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.test.tsx +++ b/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.test.tsx @@ -68,13 +68,15 @@ describe('Background Search Session Management Table', () => { id: 'wtywp9u2802hahgp-flps', url: '/app/great-app-url/#48', appId: 'canvas', - status: SearchSessionStatus.IN_PROGRESS, created: '2020-12-02T00:19:32Z', expires: '2020-12-07T00:19:32Z', idMapping: {}, }, }, ], + statuses: { + 'wtywp9u2802hahgp-flps': { status: SearchSessionStatus.EXPIRED }, + }, }; }; diff --git a/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.tsx b/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.tsx index b887d9af43f53..833ad3ec7e75e 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.tsx +++ b/src/plugins/data/public/search/session/sessions_mgmt/components/table/table.tsx @@ -12,7 +12,6 @@ import { CoreStart } from '@kbn/core/public'; import moment from 'moment'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; -import useInterval from 'react-use/lib/useInterval'; import { TableText } from '..'; import { SEARCH_SESSIONS_TABLE_ID } from '../../../../../../common'; import { SearchSessionsMgmtAPI } from '../../lib/api'; @@ -47,6 +46,7 @@ export function SearchSessionsMgmtTable({ const [debouncedIsLoading, setDebouncedIsLoading] = useState(false); const [pagination, setPagination] = useState({ pageIndex: 0 }); const showLatestResultsHandler = useRef(); + const refreshTimeoutRef = useRef(null); const refreshInterval = useMemo( () => moment.duration(config.management.refreshInterval).asMilliseconds(), [config.management.refreshInterval] @@ -63,30 +63,44 @@ export function SearchSessionsMgmtTable({ // refresh behavior const doRefresh = useCallback(async () => { + if (refreshTimeoutRef.current) { + clearTimeout(refreshTimeoutRef.current); + refreshTimeoutRef.current = null; + } + setIsLoading(true); const renderResults = (results: UISession[]) => { setTableData(results); }; showLatestResultsHandler.current = renderResults; - let results: UISession[] = []; - try { - results = await api.fetchTableData(); - } catch (e) {} // eslint-disable-line no-empty - if (showLatestResultsHandler.current === renderResults) { - renderResults(results); - setIsLoading(false); + if (document.visibilityState !== 'hidden') { + let results: UISession[] = []; + try { + results = await api.fetchTableData(); + } catch (e) {} // eslint-disable-line no-empty + + if (showLatestResultsHandler.current === renderResults) { + renderResults(results); + setIsLoading(false); + } } - }, [api]); + + if (showLatestResultsHandler.current === renderResults && refreshInterval > 0) { + if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current); + refreshTimeoutRef.current = window.setTimeout(doRefresh, refreshInterval); + } + }, [api, refreshInterval]); // initial data load useEffect(() => { doRefresh(); searchUsageCollector.trackSessionsListLoaded(); + return () => { + if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current); + }; }, [doRefresh, searchUsageCollector]); - useInterval(doRefresh, refreshInterval); - const onActionComplete: OnActionComplete = () => { doRefresh(); }; diff --git a/src/plugins/data/public/search/session/sessions_mgmt/lib/api.test.ts b/src/plugins/data/public/search/session/sessions_mgmt/lib/api.test.ts index 29a925d066905..73e1e8dc08668 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/lib/api.test.ts +++ b/src/plugins/data/public/search/session/sessions_mgmt/lib/api.test.ts @@ -52,14 +52,16 @@ describe('Search Sessions Management API', () => { attributes: { name: 'Veggie', appId: 'pizza', - status: 'complete', initialState: {}, restoreState: {}, idMapping: [], }, }, ], - } as SavedObjectsFindResponse; + statuses: { + 'hello-pizza-123': { status: 'complete' }, + }, + }; }); const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { @@ -93,7 +95,7 @@ describe('Search Sessions Management API', () => { `); }); - test('completed session with expired time is showed as expired', async () => { + test('expired session is showed as expired', async () => { sessionsClient.find = jest.fn().mockImplementation(async () => { return { saved_objects: [ @@ -102,7 +104,6 @@ describe('Search Sessions Management API', () => { attributes: { name: 'Veggie', appId: 'pizza', - status: 'complete', expires: moment().subtract(3, 'days'), initialState: {}, restoreState: {}, @@ -110,7 +111,10 @@ describe('Search Sessions Management API', () => { }, }, ], - } as SavedObjectsFindResponse; + statuses: { + 'hello-pizza-123': { status: 'expired' }, + }, + }; }); const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { diff --git a/src/plugins/data/public/search/session/sessions_mgmt/lib/api.ts b/src/plugins/data/public/search/session/sessions_mgmt/lib/api.ts index 9830cb436f7df..4fac2d36203c4 100644 --- a/src/plugins/data/public/search/session/sessions_mgmt/lib/api.ts +++ b/src/plugins/data/public/search/session/sessions_mgmt/lib/api.ts @@ -21,7 +21,7 @@ import { } from '../types'; import { ISessionsClient } from '../../sessions_client'; import { SearchUsageCollector } from '../../../collectors'; -import { SearchSessionStatus } from '../../../../../common'; +import { SearchSessionsFindResponse, SearchSessionStatus } from '../../../../../common'; import { SearchSessionsConfigSchema } from '../../../../../config'; type LocatorsStart = SharePluginStart['url']['locators']; @@ -42,25 +42,6 @@ function getActions(status: UISearchSessionState) { return actions; } -/** - * Status we display on mgtm UI might be different from the one inside the saved object - * @param status - */ -function getUIStatus(session: PersistedSearchSessionSavedObjectAttributes): UISearchSessionState { - const isSessionExpired = () => { - const curTime = moment(); - return curTime.diff(moment(session.expires), 'ms') > 0; - }; - - switch (session.status) { - case SearchSessionStatus.COMPLETE: - case SearchSessionStatus.IN_PROGRESS: - return isSessionExpired() ? SearchSessionStatus.EXPIRED : session.status; - } - - return session.status; -} - function getUrlFromState(locators: LocatorsStart, locatorId: string, state: SerializableRecord) { try { const locator = locators.get(locatorId); @@ -75,7 +56,11 @@ function getUrlFromState(locators: LocatorsStart, locatorId: string, state: Seri // Helper: factory for a function to map server objects to UI objects const mapToUISession = - (locators: LocatorsStart, config: SearchSessionsConfigSchema) => + ( + locators: LocatorsStart, + config: SearchSessionsConfigSchema, + sessionStatuses: SearchSessionsFindResponse['statuses'] + ) => async ( savedObject: SavedObject ): Promise => { @@ -91,7 +76,7 @@ const mapToUISession = version, } = savedObject.attributes; - const status = getUIStatus(savedObject.attributes); + const status = sessionStatuses[savedObject.id]?.status; const actions = getActions(status); // TODO: initialState should be saved without the searchSessionID @@ -141,9 +126,7 @@ export class SearchSessionsMgmtAPI { page: 1, perPage: mgmtConfig.maxSessions, sortField: 'created', - sortOrder: 'asc', - searchFields: ['persisted'], - search: 'true', + sortOrder: 'desc', }) ); const timeout$ = timer(refreshTimeout.asMilliseconds()).pipe( @@ -165,7 +148,9 @@ export class SearchSessionsMgmtAPI { const savedObjects = result.saved_objects as Array< SavedObject >; - return await Promise.all(savedObjects.map(mapToUISession(this.deps.locators, this.config))); + return await Promise.all( + savedObjects.map(mapToUISession(this.deps.locators, this.config, result.statuses)) + ); } } catch (err) { // eslint-disable-next-line no-console diff --git a/src/plugins/data/server/config_deprecations.test.ts b/src/plugins/data/server/config_deprecations.test.ts index 9c54c793f661a..674e1e9a6435b 100644 --- a/src/plugins/data/server/config_deprecations.test.ts +++ b/src/plugins/data/server/config_deprecations.test.ts @@ -64,4 +64,87 @@ describe('Config Deprecations', () => { ] `); }); + + test('reports about old, no longer used configs', () => { + const config = { + data: { + search: { + sessions: { + enabled: false, + pageSize: 1000, + trackingInterval: '30s', + cleanupInterval: '30s', + expireInterval: '30s', + monitoringTaskTimeout: '30s', + notTouchedInProgressTimeout: '30s', + }, + }, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated).toMatchInlineSnapshot(` + Object { + "data": Object { + "search": Object { + "sessions": Object { + "enabled": false, + }, + }, + }, + } + `); + expect(messages).toMatchInlineSnapshot(` + Array [ + "You no longer need to configure \\"data.search.sessions.pageSize\\".", + "You no longer need to configure \\"data.search.sessions.trackingInterval\\".", + "You no longer need to configure \\"data.search.sessions.cleanupInterval\\".", + "You no longer need to configure \\"data.search.sessions.expireInterval\\".", + "You no longer need to configure \\"data.search.sessions.monitoringTaskTimeout\\".", + "You no longer need to configure \\"data.search.sessions.notTouchedInProgressTimeout\\".", + ] + `); + }); + + test('reports about old, no longer used configs from xpack.data_enhanced', () => { + const config = { + xpack: { + data_enhanced: { + search: { + sessions: { + enabled: false, + pageSize: 1000, + trackingInterval: '30s', + cleanupInterval: '30s', + expireInterval: '30s', + monitoringTaskTimeout: '30s', + notTouchedInProgressTimeout: '30s', + }, + }, + }, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated).toMatchInlineSnapshot(` + Object { + "data": Object { + "search": Object { + "sessions": Object { + "enabled": false, + }, + }, + }, + } + `); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"xpack.data_enhanced.search.sessions\\" has been replaced by \\"data.search.sessions\\"", + "You no longer need to configure \\"data.search.sessions.pageSize\\".", + "You no longer need to configure \\"data.search.sessions.trackingInterval\\".", + "You no longer need to configure \\"data.search.sessions.cleanupInterval\\".", + "You no longer need to configure \\"data.search.sessions.expireInterval\\".", + "You no longer need to configure \\"data.search.sessions.monitoringTaskTimeout\\".", + "You no longer need to configure \\"data.search.sessions.notTouchedInProgressTimeout\\".", + ] + `); + }); }); diff --git a/src/plugins/data/server/config_deprecations.ts b/src/plugins/data/server/config_deprecations.ts index dcc168f306e1e..3adbcf1c2178d 100644 --- a/src/plugins/data/server/config_deprecations.ts +++ b/src/plugins/data/server/config_deprecations.ts @@ -8,8 +8,17 @@ import type { ConfigDeprecationProvider } from '@kbn/core/server'; -export const configDeprecationProvider: ConfigDeprecationProvider = ({ renameFromRoot }) => [ +export const configDeprecationProvider: ConfigDeprecationProvider = ({ + renameFromRoot, + unusedFromRoot, +}) => [ renameFromRoot('xpack.data_enhanced.search.sessions', 'data.search.sessions', { level: 'warning', }), + unusedFromRoot('data.search.sessions.pageSize', { level: 'warning' }), + unusedFromRoot('data.search.sessions.trackingInterval', { level: 'warning' }), + unusedFromRoot('data.search.sessions.cleanupInterval', { level: 'warning' }), + unusedFromRoot('data.search.sessions.expireInterval', { level: 'warning' }), + unusedFromRoot('data.search.sessions.monitoringTaskTimeout', { level: 'warning' }), + unusedFromRoot('data.search.sessions.notTouchedInProgressTimeout', { level: 'warning' }), ]; diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 31944922c7b44..8630f2be1585c 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -42,5 +42,6 @@ export function createSearchRequestHandlerContext() { extendSession: jest.fn(), cancelSession: jest.fn(), deleteSession: jest.fn(), + getSessionStatus: jest.fn(), }; } diff --git a/src/plugins/data/server/search/routes/session.test.ts b/src/plugins/data/server/search/routes/session.test.ts index dbc12e44b7a2b..2fdf02c86ce6b 100644 --- a/src/plugins/data/server/search/routes/session.test.ts +++ b/src/plugins/data/server/search/routes/session.test.ts @@ -65,6 +65,21 @@ describe('registerSessionRoutes', () => { expect(mockContext.search!.getSession).toHaveBeenCalledWith(id); }); + it('status calls getSessionStatus with sessionId', async () => { + const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const params = { id }; + + const mockRequest = httpServerMock.createKibanaRequest({ params }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [[], [, statusHandler]] = mockRouter.get.mock.calls; + + await statusHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.getSessionStatus).toHaveBeenCalledWith(id); + }); + it('find calls findSession with options', async () => { const page = 1; const perPage = 5; diff --git a/src/plugins/data/server/search/routes/session.ts b/src/plugins/data/server/search/routes/session.ts index 6106a69df207a..c654fcb53adbd 100644 --- a/src/plugins/data/server/search/routes/session.ts +++ b/src/plugins/data/server/search/routes/session.ts @@ -86,6 +86,35 @@ export function registerSessionRoutes(router: DataPluginRouter, logger: Logger): } ); + router.get( + { + path: '/internal/session/{id}/status', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + options: { + tags: [STORE_SEARCH_SESSIONS_ROLE_TAG], + }, + }, + async (context, request, res) => { + const { id } = request.params; + try { + const searchContext = await context.search; + const response = await searchContext!.getSessionStatus(id); + + return res.ok({ + body: response, + }); + } catch (e) { + const err = e.output?.payload || e; + logger.error(err); + return reportServerError(res, err); + } + } + ); + router.post( { path: '/internal/session/_find', diff --git a/src/plugins/data/server/search/saved_objects/search_session.ts b/src/plugins/data/server/search/saved_objects/search_session.ts index c77c8379af365..3eb1c6190dd6e 100644 --- a/src/plugins/data/server/search/saved_objects/search_session.ts +++ b/src/plugins/data/server/search/saved_objects/search_session.ts @@ -16,9 +16,6 @@ export const searchSessionSavedObjectType: SavedObjectsType = { hidden: true, mappings: { properties: { - persisted: { - type: 'boolean', - }, sessionId: { type: 'keyword', }, @@ -31,15 +28,6 @@ export const searchSessionSavedObjectType: SavedObjectsType = { expires: { type: 'date', }, - touched: { - type: 'date', - }, - completed: { - type: 'date', - }, - status: { - type: 'keyword', - }, appId: { type: 'keyword', }, @@ -70,6 +58,9 @@ export const searchSessionSavedObjectType: SavedObjectsType = { version: { type: 'keyword', }, + isCanceled: { + type: 'boolean', + }, }, }, migrations: searchSessionSavedObjectMigrations, diff --git a/src/plugins/data/server/search/saved_objects/search_session_migration.test.ts b/src/plugins/data/server/search/saved_objects/search_session_migration.test.ts index 251d57e884ac1..f18ac70bc4f43 100644 --- a/src/plugins/data/server/search/saved_objects/search_session_migration.test.ts +++ b/src/plugins/data/server/search/saved_objects/search_session_migration.test.ts @@ -11,9 +11,10 @@ import { SearchSessionSavedObjectAttributesPre$7$13$0, SearchSessionSavedObjectAttributesPre$7$14$0, SearchSessionSavedObjectAttributesPre$8$0$0, + SearchSessionSavedObjectAttributesPre$8$6$0, } from './search_session_migration'; import { SavedObject } from '@kbn/core/types'; -import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../common'; +import { SEARCH_SESSION_TYPE, SearchSessionStatus, SearchStatus } from '../../../common'; import { SavedObjectMigrationContext } from '@kbn/core/server'; describe('7.12.0 -> 7.13.0', () => { @@ -356,3 +357,98 @@ describe('7.14.0 -> 8.0.0', () => { ); }); }); + +describe('8.0.0 -> 8.6.0', () => { + const migration = searchSessionSavedObjectMigrations['8.6.0']; + + const mockSessionSavedObject: SavedObject = { + id: 'id', + type: SEARCH_SESSION_TYPE, + attributes: { + appId: 'my_app_id', + completed: '2021-03-29T00:00:00.000Z', + created: '2021-03-26T00:00:00.000Z', + expires: '2021-03-30T00:00:00.000Z', + idMapping: { + search1: { id: 'id1', strategy: 'ese', status: SearchStatus.COMPLETE }, + search2: { + id: 'id2', + strategy: 'sql', + status: SearchStatus.ERROR, + error: 'error', + }, + search3: { id: 'id3', strategy: 'es', status: SearchStatus.COMPLETE }, + }, + initialState: {}, + locatorId: undefined, + name: 'my_name', + persisted: true, + realmName: 'realmName', + realmType: 'realmType', + restoreState: {}, + sessionId: 'sessionId', + status: SearchSessionStatus.COMPLETE, + touched: '2021-03-29T00:00:00.000Z', + username: 'username', + version: '7.14.0', + }, + references: [], + }; + + test('migrates object', () => { + const migratedSession = migration(mockSessionSavedObject, {} as SavedObjectMigrationContext); + + expect(migratedSession.attributes).not.toHaveProperty('status'); + expect(migratedSession.attributes).not.toHaveProperty('touched'); + expect(migratedSession.attributes).not.toHaveProperty('completed'); + expect(migratedSession.attributes).not.toHaveProperty('persisted'); + expect(migratedSession.attributes.idMapping.search1).not.toHaveProperty('status'); + expect(migratedSession.attributes.idMapping.search2).not.toHaveProperty('error'); + + expect(migratedSession.attributes).toMatchInlineSnapshot(` + Object { + "appId": "my_app_id", + "created": "2021-03-26T00:00:00.000Z", + "expires": "2021-03-30T00:00:00.000Z", + "idMapping": Object { + "search1": Object { + "id": "id1", + "strategy": "ese", + }, + "search2": Object { + "id": "id2", + "strategy": "sql", + }, + "search3": Object { + "id": "id3", + "strategy": "es", + }, + }, + "initialState": Object {}, + "locatorId": undefined, + "name": "my_name", + "realmName": "realmName", + "realmType": "realmType", + "restoreState": Object {}, + "sessionId": "sessionId", + "username": "username", + "version": "7.14.0", + } + `); + }); + + test('status:canceled -> isCanceled', () => { + const migratedSession = migration( + { + ...mockSessionSavedObject, + attributes: { + ...mockSessionSavedObject.attributes, + status: SearchSessionStatus.CANCELLED, + }, + }, + {} as SavedObjectMigrationContext + ); + + expect(migratedSession.attributes.isCanceled).toBe(true); + }); +}); diff --git a/src/plugins/data/server/search/saved_objects/search_session_migration.ts b/src/plugins/data/server/search/saved_objects/search_session_migration.ts index 314db0fb8b58f..fec4e3c33a93b 100644 --- a/src/plugins/data/server/search/saved_objects/search_session_migration.ts +++ b/src/plugins/data/server/search/saved_objects/search_session_migration.ts @@ -39,12 +39,39 @@ export type SearchSessionSavedObjectAttributesPre$7$14$0 = Omit< * from using `urlGeneratorId` to `locatorId`. */ export type SearchSessionSavedObjectAttributesPre$8$0$0 = Omit< - SearchSessionSavedObjectAttributesLatest, + SearchSessionSavedObjectAttributesPre$8$6$0, 'locatorId' > & { urlGeneratorId?: string; }; +/** + * In 8.6.0 with search session refactoring and moving away from using task manager we are no longer track of: + * - `completed` - when session was completed + * - `persisted` - if session was saved + * - `touched` - when session was last updated (touched by the user) + * - `status` - status is no longer persisted. Except 'canceled' which was moved to `isCanceled` + * - `status` and `error` in idMapping (search info) + */ +export type SearchSessionSavedObjectAttributesPre$8$6$0 = Omit< + SearchSessionSavedObjectAttributesLatest, + 'idMapping' | 'isCanceled' +> & { + completed?: string | null; + persisted: boolean; + touched: string; + status: SearchSessionStatus; + idMapping: Record< + string, + { + id: string; + strategy: string; + status: string; + error?: string; + } + >; +}; + function getLocatorId(urlGeneratorId?: string) { if (!urlGeneratorId) return; if (urlGeneratorId === 'DISCOVER_APP_URL_GENERATOR') return 'DISCOVER_APP_LOCATOR'; @@ -89,4 +116,27 @@ export const searchSessionSavedObjectMigrations: SavedObjectMigrationMap = { const attributes = { ...otherAttrs, locatorId }; return { ...doc, attributes }; }, + '8.6.0': ( + doc: SavedObjectUnsanitizedDoc + ): SavedObjectUnsanitizedDoc => { + const { + attributes: { touched, completed, persisted, idMapping, status, ...otherAttrs }, + } = doc; + + const attributes: SearchSessionSavedObjectAttributesLatest = { + ...otherAttrs, + idMapping: Object.entries(idMapping).reduce< + SearchSessionSavedObjectAttributesLatest['idMapping'] + >((res, [searchHash, { status: searchStatus, error, ...otherSearchAttrs }]) => { + res[searchHash] = otherSearchAttrs; + return res; + }, {}), + }; + + if (status === SearchSessionStatus.CANCELLED) { + attributes.isCanceled = true; + } + + return { ...doc, attributes }; + }, }; diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts index 56733b6871ec8..cb3575caec510 100644 --- a/src/plugins/data/server/search/search_service.test.ts +++ b/src/plugins/data/server/search/search_service.test.ts @@ -33,6 +33,8 @@ import { ENHANCED_ES_SEARCH_STRATEGY } from '../../common'; let mockSessionClient: jest.Mocked; jest.mock('./session', () => { class SearchSessionService { + setup() {} + start() {} asScopedProvider = () => (request: any) => mockSessionClient; } return { @@ -193,7 +195,7 @@ describe('Search service', () => { it('does not fail if `trackId` throws', async () => { const searchRequest = { params: {} }; - const options = { sessionId, isStored: false, isRestore: false }; + const options = { sessionId, isStored: true, isRestore: false }; mockSessionClient.trackId = jest.fn().mockRejectedValue(undefined); mockStrategy.search.mockReturnValue( @@ -203,14 +205,32 @@ describe('Search service', () => { }) ); - await mockScopedClient.search(searchRequest, options).toPromise(); + const result = await mockScopedClient.search(searchRequest, options).toPromise(); expect(mockSessionClient.trackId).toBeCalledTimes(1); + expect(result?.isStored).toBeUndefined(); }); - it('calls `trackId` for every response, if the response contains an `id` and not restoring', async () => { + it("doesn't call trackId if session is not stored", async () => { const searchRequest = { params: {} }; - const options = { sessionId, isStored: false, isRestore: false }; + const options = { sessionId }; + mockSessionClient.trackId = jest.fn(); + + mockStrategy.search.mockReturnValue( + of({ + id: 'my_id', + rawResponse: {} as any, + }) + ); + + await mockScopedClient.search(searchRequest, options).toPromise(); + + expect(mockSessionClient.trackId).toBeCalledTimes(0); + }); + + it('calls `trackId` once, if the response contains an `id`, session is stored and not restoring', async () => { + const searchRequest = { params: {} }; + const options = { sessionId, isStored: true, isRestore: false }; mockSessionClient.trackId = jest.fn().mockResolvedValue(undefined); mockStrategy.search.mockReturnValue( @@ -228,10 +248,20 @@ describe('Search service', () => { await mockScopedClient.search(searchRequest, options).toPromise(); - expect(mockSessionClient.trackId).toBeCalledTimes(2); + expect(mockSessionClient.trackId).toBeCalledTimes(1); expect(mockSessionClient.trackId.mock.calls[0]).toEqual([searchRequest, 'my_id', options]); - expect(mockSessionClient.trackId.mock.calls[1]).toEqual([searchRequest, 'my_id', options]); + }); + + it('does not call `trackId` if search is already tracked', async () => { + const searchRequest = { params: {} }; + const options = { sessionId, isStored: true, isRestore: false, isSearchStored: true }; + mockSessionClient.getId = jest.fn().mockResolvedValueOnce('my_id'); + mockSessionClient.trackId = jest.fn().mockResolvedValue(undefined); + + await mockScopedClient.search(searchRequest, options).toPromise(); + + expect(mockSessionClient.trackId).not.toBeCalled(); }); it('does not call `trackId` if restoring', async () => { diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index b5d140c36a0f5..7523da61752b4 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { firstValueFrom, from, Observable, throwError } from 'rxjs'; +import { concatMap, firstValueFrom, from, Observable, of, throwError } from 'rxjs'; import { pick } from 'lodash'; import moment from 'moment'; import { @@ -19,7 +19,7 @@ import { SharedGlobalConfig, StartServicesAccessor, } from '@kbn/core/server'; -import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators'; +import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { BfetchServerSetup } from '@kbn/bfetch-plugin/server'; import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/server'; @@ -156,13 +156,7 @@ export class SearchService implements Plugin { registerSearchRoute(router); registerSessionRoutes(router, this.logger); - if (taskManager) { - this.sessionService.setup(core, { taskManager, security }); - } else { - // this should never happen in real world, but - // taskManager and security are optional deps because they are in x-pack - this.logger.debug('Skipping sessionService setup because taskManager is not available'); - } + this.sessionService.setup(core, { security }); core.http.registerRouteHandlerContext( 'search', @@ -273,9 +267,7 @@ export class SearchService implements Plugin { ): ISearchStart { const { elasticsearch, savedObjects, uiSettings } = core; - if (taskManager) { - this.sessionService.start(core, { taskManager }); - } + this.sessionService.start(core, {}); const aggs = this.aggsService.start({ fieldFormats, @@ -384,22 +376,55 @@ export class SearchService implements Plugin { }; const searchRequest$ = from(getSearchRequest()); + let isInternalSearchStored = false; // used to prevent tracking current search more than once const search$ = searchRequest$.pipe( - switchMap((searchRequest) => strategy.search(searchRequest, options, deps)), - withLatestFrom(searchRequest$), - tap(([response, requestWithId]) => { - if (!options.sessionId || !response.id || (options.isRestore && requestWithId.id)) return; - // intentionally swallow tracking error, as it shouldn't fail the search - deps.searchSessionsClient.trackId(request, response.id, options).catch((trackErr) => { - this.logger.error(trackErr); - }); - }), - map(([response, requestWithId]) => { - return { - ...response, - isRestored: !!requestWithId.id, - }; - }) + switchMap((searchRequest) => + strategy.search(searchRequest, options, deps).pipe( + concatMap((response) => { + response = { + ...response, + isRestored: !!searchRequest.id, + }; + + if ( + options.sessionId && // if within search session + options.isStored && // and search session was saved (saved object exists) + response.id && // and async search has started + !(options.isRestore && searchRequest.id) // and not restoring already tracked search + ) { + // then track this search inside the search-session saved object + + // check if search was already tracked and extended, don't track again in this case + if (options.isSearchStored || isInternalSearchStored) { + return of({ + ...response, + isStored: true, + }); + } else { + return from( + deps.searchSessionsClient.trackId(request, response.id, options) + ).pipe( + tap(() => { + isInternalSearchStored = true; + }), + map(() => ({ + ...response, + isStored: true, + })), + catchError((e) => { + this.logger.error( + `Error while trying to track search id: ${e?.message}. This might lead to untracked long-running search.` + ); + return of(response); + }) + ); + } + } else { + return of(response); + } + }) + ) + ) ); return search$; @@ -521,6 +546,7 @@ export class SearchService implements Plugin { extendSession: this.extendSession.bind(this, deps), cancelSession: this.cancelSession.bind(this, deps), deleteSession: this.deleteSession.bind(this, deps), + getSessionStatus: searchSessionsClient.status, }; }; }; diff --git a/src/plugins/data/server/search/session/check_non_persisted_sessions.test.ts b/src/plugins/data/server/search/session/check_non_persisted_sessions.test.ts deleted file mode 100644 index edbef1938a82f..0000000000000 --- a/src/plugins/data/server/search/session/check_non_persisted_sessions.test.ts +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { checkNonPersistedSessions as checkNonPersistedSessions$ } from './check_non_persisted_sessions'; -import { - SearchSessionStatus, - SearchSessionSavedObjectAttributes, - ENHANCED_ES_SEARCH_STRATEGY, - EQL_SEARCH_STRATEGY, -} from '../../../common'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { CheckSearchSessionsDeps, SearchStatus } from './types'; -import moment from 'moment'; -import { - SavedObjectsBulkUpdateObject, - SavedObjectsDeleteOptions, - SavedObjectsClientContract, -} from '@kbn/core/server'; -import { SearchSessionsConfigSchema } from '../../../config'; - -jest.useFakeTimers(); - -const checkNonPersistedSessions = ( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema -) => checkNonPersistedSessions$(deps, config).toPromise(); - -describe('checkNonPersistedSessions', () => { - let mockClient: any; - let savedObjectsClient: jest.Mocked; - const config: SearchSessionsConfigSchema = { - enabled: true, - pageSize: 5, - notTouchedInProgressTimeout: moment.duration(1, 'm'), - notTouchedTimeout: moment.duration(5, 'm'), - maxUpdateRetries: 3, - defaultExpiration: moment.duration(7, 'd'), - trackingInterval: moment.duration(10, 's'), - expireInterval: moment.duration(10, 'm'), - monitoringTaskTimeout: moment.duration(5, 'm'), - cleanupInterval: moment.duration(10, 's'), - management: {} as any, - }; - const mockLogger: any = { - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }; - - beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - mockClient = { - asyncSearch: { - status: jest.fn(), - delete: jest.fn(), - }, - eql: { - status: jest.fn(), - delete: jest.fn(), - }, - }; - }); - - test('does nothing if there are no open sessions', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [], - total: 0, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - describe('delete', () => { - test('doesnt delete a non persisted, recently touched session', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - expires: moment().add(moment.duration(3, 'm')), - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(10, 's')), - idMapping: {}, - }, - }, - ], - total: 1, - } as any); - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test('doesnt delete a non persisted, completed session, within on screen time frame', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.COMPLETE, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(1, 'm')), - expires: moment().add(moment.duration(3, 'm')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.COMPLETE, - }, - }, - }, - }, - ], - total: 1, - } as any); - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test('deletes in space', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - namespaces: ['awesome'], - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - expires: moment().add(moment.duration(3, 'm')), - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(2, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - }, - }, - }, - }, - ], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.delete).toBeCalled(); - - const [, id, opts] = savedObjectsClient.delete.mock.calls[0]; - expect(id).toBe('123'); - expect((opts as SavedObjectsDeleteOptions).namespace).toBe('awesome'); - }); - - test('deletes a non persisted, abandoned session', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(2, 'm')), - expires: moment().add(moment.duration(3, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - }, - }, - }, - }, - ], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).toBeCalled(); - - expect(mockClient.asyncSearch.delete).toBeCalled(); - - const { id } = mockClient.asyncSearch.delete.mock.calls[0][0]; - expect(id).toBe('async-id'); - }); - - test('deletes a completed, not persisted session', async () => { - mockClient.asyncSearch.delete = jest.fn().mockResolvedValue(true); - - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.COMPLETE, - expires: moment().add(moment.duration(3, 'm')), - created: moment().subtract(moment.duration(30, 'm')), - touched: moment().subtract(moment.duration(6, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - status: SearchStatus.COMPLETE, - }, - 'eql-map-key': { - strategy: EQL_SEARCH_STRATEGY, - id: 'eql-async-id', - status: SearchStatus.COMPLETE, - }, - }, - }, - }, - ], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).toBeCalled(); - - expect(mockClient.asyncSearch.delete).toBeCalled(); - expect(mockClient.eql.delete).not.toBeCalled(); - - const { id } = mockClient.asyncSearch.delete.mock.calls[0][0]; - expect(id).toBe('async-id'); - }); - - test('ignores errors thrown while deleting async searches', async () => { - mockClient.asyncSearch.delete = jest.fn().mockRejectedValueOnce(false); - - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.COMPLETE, - expires: moment().add(moment.duration(3, 'm')), - created: moment().subtract(moment.duration(30, 'm')), - touched: moment().subtract(moment.duration(6, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - status: SearchStatus.COMPLETE, - }, - }, - }, - }, - ], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).toBeCalled(); - - expect(mockClient.asyncSearch.delete).toBeCalled(); - - const { id } = mockClient.asyncSearch.delete.mock.calls[0][0]; - expect(id).toBe('async-id'); - }); - - test("doesn't attempt to delete errored out async search", async () => { - mockClient.asyncSearch.delete = jest.fn(); - - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [ - { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.ERROR, - expires: moment().add(moment.duration(3, 'm')), - created: moment().subtract(moment.duration(30, 'm')), - touched: moment().subtract(moment.duration(6, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - status: SearchStatus.ERROR, - }, - }, - }, - }, - ], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).toBeCalled(); - expect(mockClient.asyncSearch.delete).not.toBeCalled(); - }); - }); - - describe('update', () => { - test('does nothing if the search is still running', async () => { - const so = { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(10, 's')), - expires: moment().add(moment.duration(3, 'm')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - }; - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [so], - total: 1, - } as any); - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: true, - is_running: true, - }, - }); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test("doesn't re-check completed or errored searches", async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - savedObjectsClient.delete = jest.fn(); - const so = { - id: '123', - attributes: { - status: SearchSessionStatus.ERROR, - expires: moment().add(moment.duration(3, 'm')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.COMPLETE, - }, - 'another-search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.ERROR, - }, - }, - }, - }; - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [so], - total: 1, - } as any); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(mockClient.asyncSearch.status).not.toBeCalled(); - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test('updates in space', async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - const so = { - namespaces: ['awesome'], - attributes: { - status: SearchSessionStatus.IN_PROGRESS, - expires: moment().add(moment.duration(3, 'm')), - touched: '123', - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - }; - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [so], - total: 1, - } as any); - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: false, - is_running: false, - completion_status: 200, - }, - }); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(mockClient.asyncSearch.status).toBeCalledWith({ id: 'search-id' }, { meta: true }); - const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; - const updatedAttributes = updateInput[0] as SavedObjectsBulkUpdateObject; - expect(updatedAttributes.namespace).toBe('awesome'); - }); - - test('updates to complete if the search is done', async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - const so = { - attributes: { - status: SearchSessionStatus.IN_PROGRESS, - expires: moment().add(moment.duration(3, 'm')), - touched: '123', - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - }; - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [so], - total: 1, - } as any); - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: false, - is_running: false, - completion_status: 200, - }, - }); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - expect(mockClient.asyncSearch.status).toBeCalledWith({ id: 'search-id' }, { meta: true }); - const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; - const updatedAttributes = updateInput[0].attributes as SearchSessionSavedObjectAttributes; - expect(updatedAttributes.status).toBe(SearchSessionStatus.COMPLETE); - expect(updatedAttributes.touched).not.toBe('123'); - expect(updatedAttributes.completed).not.toBeUndefined(); - expect(updatedAttributes.idMapping['search-hash'].status).toBe(SearchStatus.COMPLETE); - expect(updatedAttributes.idMapping['search-hash'].error).toBeUndefined(); - - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test('updates to error if the search is errored', async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - const so = { - attributes: { - expires: moment().add(moment.duration(3, 'm')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - }; - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [so], - total: 1, - } as any); - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: false, - is_running: false, - completion_status: 500, - }, - }); - - await checkNonPersistedSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; - const updatedAttributes = updateInput[0].attributes as SearchSessionSavedObjectAttributes; - expect(updatedAttributes.status).toBe(SearchSessionStatus.ERROR); - expect(updatedAttributes.idMapping['search-hash'].status).toBe(SearchStatus.ERROR); - expect(updatedAttributes.idMapping['search-hash'].error).toBe( - 'Search completed with a 500 status' - ); - }); - }); -}); diff --git a/src/plugins/data/server/search/session/check_non_persisted_sessions.ts b/src/plugins/data/server/search/session/check_non_persisted_sessions.ts deleted file mode 100644 index f6314611afdbc..0000000000000 --- a/src/plugins/data/server/search/session/check_non_persisted_sessions.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SavedObjectsFindResult } from '@kbn/core/server'; -import moment from 'moment'; -import { EMPTY } from 'rxjs'; -import { catchError, concatMap } from 'rxjs/operators'; -import { nodeBuilder, KueryNode } from '@kbn/es-query'; -import { - ENHANCED_ES_SEARCH_STRATEGY, - SEARCH_SESSION_TYPE, - SearchSessionSavedObjectAttributes, - SearchSessionStatus, -} from '../../../common'; -import { checkSearchSessionsByPage, getSearchSessionsPage$ } from './get_search_session_page'; -import { CheckSearchSessionsDeps, SearchStatus } from './types'; -import { bulkUpdateSessions, getAllSessionsStatusUpdates } from './update_session_status'; -import { SearchSessionsConfigSchema } from '../../../config'; - -export const SEARCH_SESSIONS_CLEANUP_TASK_TYPE = 'search_sessions_cleanup'; -export const SEARCH_SESSIONS_CLEANUP_TASK_ID = `data_enhanced_${SEARCH_SESSIONS_CLEANUP_TASK_TYPE}`; - -function isSessionStale( - session: SavedObjectsFindResult, - config: SearchSessionsConfigSchema -) { - const curTime = moment(); - // Delete cancelled sessions immediately - if (session.attributes.status === SearchSessionStatus.CANCELLED) return true; - // Delete if a running session wasn't polled for in the last notTouchedInProgressTimeout OR - // if a completed \ errored \ canceled session wasn't saved for within notTouchedTimeout - return ( - (session.attributes.status === SearchSessionStatus.IN_PROGRESS && - curTime.diff(moment(session.attributes.touched), 'ms') > - config.notTouchedInProgressTimeout.asMilliseconds()) || - (session.attributes.status !== SearchSessionStatus.IN_PROGRESS && - curTime.diff(moment(session.attributes.touched), 'ms') > - config.notTouchedTimeout.asMilliseconds()) - ); -} - -function checkNonPersistedSessionsPage( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - filter: KueryNode, - page: number -) { - const { logger, client, savedObjectsClient } = deps; - logger.debug(`${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Fetching sessions from page ${page}`); - return getSearchSessionsPage$(deps, filter, config.pageSize, page).pipe( - concatMap(async (nonPersistedSearchSessions) => { - if (!nonPersistedSearchSessions.total) return nonPersistedSearchSessions; - - logger.debug( - `${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Found ${nonPersistedSearchSessions.total} sessions, processing ${nonPersistedSearchSessions.saved_objects.length}` - ); - - const updatedSessions = await getAllSessionsStatusUpdates( - deps, - config, - nonPersistedSearchSessions - ); - const deletedSessionIds: string[] = []; - - await Promise.all( - nonPersistedSearchSessions.saved_objects.map(async (session) => { - if (isSessionStale(session, config)) { - // delete saved object to free up memory - // TODO: there's a potential rare edge case of deleting an object and then receiving a new trackId for that same session! - // Maybe we want to change state to deleted and cleanup later? - logger.debug(`Deleting stale session | ${session.id}`); - try { - deletedSessionIds.push(session.id); - await savedObjectsClient.delete(SEARCH_SESSION_TYPE, session.id, { - namespace: session.namespaces?.[0], - }); - } catch (e) { - logger.error( - `${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Error while deleting session ${session.id}: ${e.message}` - ); - } - - // Send a delete request for each async search to ES - Object.keys(session.attributes.idMapping).map(async (searchKey: string) => { - const searchInfo = session.attributes.idMapping[searchKey]; - if (searchInfo.status === SearchStatus.ERROR) return; // skip attempting to delete async search in case we know it has errored out - - if (searchInfo.strategy === ENHANCED_ES_SEARCH_STRATEGY) { - try { - await client.asyncSearch.delete({ id: searchInfo.id }); - } catch (e) { - if (e.message !== 'resource_not_found_exception') { - logger.error( - `${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Error while deleting async_search ${searchInfo.id}: ${e.message}` - ); - } - } - } - }); - } - }) - ); - - const nonDeletedSessions = updatedSessions.filter((updateSession) => { - return deletedSessionIds.indexOf(updateSession.id) === -1; - }); - - await bulkUpdateSessions(deps, nonDeletedSessions); - - return nonPersistedSearchSessions; - }) - ); -} - -export function checkNonPersistedSessions( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema -) { - const { logger } = deps; - - const filters = nodeBuilder.is(`${SEARCH_SESSION_TYPE}.attributes.persisted`, 'false'); - - return checkSearchSessionsByPage(checkNonPersistedSessionsPage, deps, config, filters).pipe( - catchError((e) => { - logger.error( - `${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Error while processing sessions: ${e?.message}` - ); - return EMPTY; - }) - ); -} diff --git a/src/plugins/data/server/search/session/check_persisted_sessions.test.ts b/src/plugins/data/server/search/session/check_persisted_sessions.test.ts deleted file mode 100644 index 1e6de567a0d76..0000000000000 --- a/src/plugins/data/server/search/session/check_persisted_sessions.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { checkPersistedSessionsProgress } from './check_persisted_sessions'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import moment from 'moment'; -import { SavedObjectsClientContract } from '@kbn/core/server'; -import { SearchSessionsConfigSchema } from '../../../config'; - -describe('checkPersistedSessionsProgress', () => { - let mockClient: any; - let savedObjectsClient: jest.Mocked; - const config: SearchSessionsConfigSchema = { - enabled: true, - pageSize: 5, - notTouchedInProgressTimeout: moment.duration(1, 'm'), - notTouchedTimeout: moment.duration(5, 'm'), - maxUpdateRetries: 3, - defaultExpiration: moment.duration(7, 'd'), - trackingInterval: moment.duration(10, 's'), - cleanupInterval: moment.duration(10, 's'), - expireInterval: moment.duration(10, 'm'), - monitoringTaskTimeout: moment.duration(5, 'm'), - management: {} as any, - }; - const mockLogger: any = { - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }; - - beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - mockClient = { - asyncSearch: { - status: jest.fn(), - delete: jest.fn(), - }, - eql: { - status: jest.fn(), - delete: jest.fn(), - }, - }; - }); - - test('fetches only running persisted sessions', async () => { - savedObjectsClient.find.mockResolvedValue({ - saved_objects: [], - total: 0, - } as any); - - await checkPersistedSessionsProgress( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config - ); - - const [findInput] = savedObjectsClient.find.mock.calls[0]; - - expect(findInput.filter.arguments[0].arguments[0].value).toBe( - 'search-session.attributes.persisted' - ); - expect(findInput.filter.arguments[0].arguments[1].value).toBe('true'); - expect(findInput.filter.arguments[1].arguments[0].value).toBe( - 'search-session.attributes.status' - ); - expect(findInput.filter.arguments[1].arguments[1].value).toBe('in_progress'); - }); -}); diff --git a/src/plugins/data/server/search/session/check_persisted_sessions.ts b/src/plugins/data/server/search/session/check_persisted_sessions.ts deleted file mode 100644 index ace921a56a052..0000000000000 --- a/src/plugins/data/server/search/session/check_persisted_sessions.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { EMPTY, Observable } from 'rxjs'; -import { catchError, concatMap } from 'rxjs/operators'; -import { nodeBuilder, KueryNode } from '@kbn/es-query'; -import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../common'; -import { checkSearchSessionsByPage, getSearchSessionsPage$ } from './get_search_session_page'; -import { CheckSearchSessionsDeps, SearchSessionsResponse } from './types'; -import { bulkUpdateSessions, getAllSessionsStatusUpdates } from './update_session_status'; -import { SearchSessionsConfigSchema } from '../../../config'; - -export const SEARCH_SESSIONS_TASK_TYPE = 'search_sessions_monitor'; -export const SEARCH_SESSIONS_TASK_ID = `data_enhanced_${SEARCH_SESSIONS_TASK_TYPE}`; - -function checkPersistedSessionsPage( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - filter: KueryNode, - page: number -): Observable { - const { logger } = deps; - logger.debug(`${SEARCH_SESSIONS_TASK_TYPE} Fetching sessions from page ${page}`); - return getSearchSessionsPage$(deps, filter, config.pageSize, page).pipe( - concatMap(async (persistedSearchSessions) => { - if (!persistedSearchSessions.total) return persistedSearchSessions; - - logger.debug( - `${SEARCH_SESSIONS_TASK_TYPE} Found ${persistedSearchSessions.total} sessions, processing ${persistedSearchSessions.saved_objects.length}` - ); - - const updatedSessions = await getAllSessionsStatusUpdates( - deps, - config, - persistedSearchSessions - ); - await bulkUpdateSessions(deps, updatedSessions); - - return persistedSearchSessions; - }) - ); -} - -export function checkPersistedSessionsProgress( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema -) { - const { logger } = deps; - - const persistedSessionsFilter = nodeBuilder.and([ - nodeBuilder.is(`${SEARCH_SESSION_TYPE}.attributes.persisted`, 'true'), - nodeBuilder.is( - `${SEARCH_SESSION_TYPE}.attributes.status`, - SearchSessionStatus.IN_PROGRESS.toString() - ), - ]); - - return checkSearchSessionsByPage( - checkPersistedSessionsPage, - deps, - config, - persistedSessionsFilter - ).pipe( - catchError((e) => { - logger.error(`${SEARCH_SESSIONS_TASK_TYPE} Error while processing sessions: ${e?.message}`); - return EMPTY; - }) - ); -} diff --git a/src/plugins/data/server/search/session/expire_persisted_sessions.ts b/src/plugins/data/server/search/session/expire_persisted_sessions.ts deleted file mode 100644 index 1c7ad8ff3e346..0000000000000 --- a/src/plugins/data/server/search/session/expire_persisted_sessions.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { EMPTY, Observable } from 'rxjs'; -import { catchError, concatMap } from 'rxjs/operators'; -import { nodeBuilder, KueryNode } from '@kbn/es-query'; -import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../common'; -import { checkSearchSessionsByPage, getSearchSessionsPage$ } from './get_search_session_page'; -import { CheckSearchSessionsDeps, SearchSessionsResponse } from './types'; -import { bulkUpdateSessions, getAllSessionsStatusUpdates } from './update_session_status'; -import { SearchSessionsConfigSchema } from '../../../config'; - -export const SEARCH_SESSIONS_EXPIRE_TASK_TYPE = 'search_sessions_expire'; -export const SEARCH_SESSIONS_EXPIRE_TASK_ID = `data_enhanced_${SEARCH_SESSIONS_EXPIRE_TASK_TYPE}`; - -function checkSessionExpirationPage( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - filter: KueryNode, - page: number -): Observable { - const { logger } = deps; - logger.debug(`${SEARCH_SESSIONS_EXPIRE_TASK_TYPE} Fetching sessions from page ${page}`); - return getSearchSessionsPage$(deps, filter, config.pageSize, page).pipe( - concatMap(async (searchSessions) => { - if (!searchSessions.total) return searchSessions; - - logger.debug( - `${SEARCH_SESSIONS_EXPIRE_TASK_TYPE} Found ${searchSessions.total} sessions, processing ${searchSessions.saved_objects.length}` - ); - - const updatedSessions = await getAllSessionsStatusUpdates(deps, config, searchSessions); - await bulkUpdateSessions(deps, updatedSessions); - - return searchSessions; - }) - ); -} - -export function checkPersistedCompletedSessionExpiration( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema -) { - const { logger } = deps; - - const persistedSessionsFilter = nodeBuilder.and([ - nodeBuilder.is(`${SEARCH_SESSION_TYPE}.attributes.persisted`, 'true'), - nodeBuilder.is( - `${SEARCH_SESSION_TYPE}.attributes.status`, - SearchSessionStatus.COMPLETE.toString() - ), - ]); - - return checkSearchSessionsByPage( - checkSessionExpirationPage, - deps, - config, - persistedSessionsFilter - ).pipe( - catchError((e) => { - logger.error( - `${SEARCH_SESSIONS_EXPIRE_TASK_TYPE} Error while processing sessions: ${e?.message}` - ); - return EMPTY; - }) - ); -} diff --git a/src/plugins/data/server/search/session/get_search_session_page.test.ts b/src/plugins/data/server/search/session/get_search_session_page.test.ts deleted file mode 100644 index 43a8f987dc8fc..0000000000000 --- a/src/plugins/data/server/search/session/get_search_session_page.test.ts +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { checkSearchSessionsByPage, getSearchSessionsPage$ } from './get_search_session_page'; -import { ENHANCED_ES_SEARCH_STRATEGY, SearchSessionStatus } from '../../../common'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { SearchStatus } from './types'; -import moment from 'moment'; -import { SavedObjectsClientContract } from '@kbn/core/server'; -import { of, Subject, throwError } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { SearchSessionsConfigSchema } from '../../../config'; - -jest.useFakeTimers(); - -describe('checkSearchSessionsByPage', () => { - const mockClient = {} as any; - let savedObjectsClient: jest.Mocked; - const config: SearchSessionsConfigSchema = { - enabled: true, - pageSize: 5, - management: {} as any, - } as any; - const mockLogger: any = { - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }; - - const emptySO = { - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(10, 's')), - idMapping: {}, - }, - }; - - beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - }); - - describe('getSearchSessionsPage$', () => { - test('sorting is by "touched"', async () => { - savedObjectsClient.find.mockResolvedValueOnce({ - saved_objects: [], - total: 0, - } as any); - - await getSearchSessionsPage$( - { - savedObjectsClient, - } as any, - { - type: 'literal', - }, - 1, - 1 - ); - - expect(savedObjectsClient.find).toHaveBeenCalledWith( - expect.objectContaining({ sortField: 'touched', sortOrder: 'asc' }) - ); - }); - }); - - describe('pagination', () => { - test('fetches one page if got empty response', async () => { - const checkFn = jest.fn().mockReturnValue(of(undefined)); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ).toPromise(); - - expect(checkFn).toHaveBeenCalledTimes(1); - }); - - test('fetches one page if got response with no saved objects', async () => { - const checkFn = jest.fn().mockReturnValue( - of({ - total: 0, - }) - ); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ).toPromise(); - - expect(checkFn).toHaveBeenCalledTimes(1); - }); - - test('fetches one page if less than page size object are returned', async () => { - const checkFn = jest.fn().mockReturnValue( - of({ - saved_objects: [emptySO, emptySO], - total: 5, - }) - ); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ).toPromise(); - - expect(checkFn).toHaveBeenCalledTimes(1); - }); - - test('fetches two pages if exactly page size objects are returned', async () => { - let i = 0; - - const checkFn = jest.fn().mockImplementation(() => - of({ - saved_objects: i++ === 0 ? [emptySO, emptySO, emptySO, emptySO, emptySO] : [], - total: 5, - page: i, - }) - ); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ).toPromise(); - - expect(checkFn).toHaveBeenCalledTimes(2); - - // validate that page number increases - const page1 = checkFn.mock.calls[0][3]; - const page2 = checkFn.mock.calls[1][3]; - expect(page1).toBe(1); - expect(page2).toBe(2); - }); - - test('fetches two pages if page size +1 objects are returned', async () => { - let i = 0; - - const checkFn = jest.fn().mockImplementation(() => - of({ - saved_objects: i++ === 0 ? [emptySO, emptySO, emptySO, emptySO, emptySO] : [emptySO], - total: i === 0 ? 5 : 1, - page: i, - }) - ); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ).toPromise(); - - expect(checkFn).toHaveBeenCalledTimes(2); - }); - - test('sessions fetched in the beginning are processed even if sessions in the end fail', async () => { - let i = 0; - - const checkFn = jest.fn().mockImplementation(() => { - if (++i === 2) { - return throwError('Fake find error...'); - } - return of({ - saved_objects: - i <= 5 - ? [ - i === 1 - ? { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(2, 'm')), - idMapping: { - 'map-key': { - strategy: ENHANCED_ES_SEARCH_STRATEGY, - id: 'async-id', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } - : emptySO, - emptySO, - emptySO, - emptySO, - emptySO, - ] - : [], - total: 25, - page: i, - }); - }); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ) - .toPromise() - .catch(() => {}); - - expect(checkFn).toHaveBeenCalledTimes(2); - }); - - test('fetching is abortable', async () => { - let i = 0; - const abort$ = new Subject(); - - const checkFn = jest.fn().mockImplementation(() => { - if (++i === 2) { - abort$.next(); - } - - return of({ - saved_objects: i <= 5 ? [emptySO, emptySO, emptySO, emptySO, emptySO] : [], - total: 25, - page: i, - }); - }); - - await checkSearchSessionsByPage( - checkFn, - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - config, - [] - ) - .pipe(takeUntil(abort$)) - .toPromise() - .catch(() => {}); - - jest.runAllTimers(); - - // if not for `abort$` then this would be called 6 times! - expect(checkFn).toHaveBeenCalledTimes(2); - }); - }); -}); diff --git a/src/plugins/data/server/search/session/get_search_session_page.ts b/src/plugins/data/server/search/session/get_search_session_page.ts deleted file mode 100644 index d98d513c9e73f..0000000000000 --- a/src/plugins/data/server/search/session/get_search_session_page.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SavedObjectsClientContract, Logger } from '@kbn/core/server'; -import { from, Observable, EMPTY } from 'rxjs'; -import { concatMap } from 'rxjs/operators'; -import type { KueryNode } from '@kbn/es-query'; -import { SearchSessionSavedObjectAttributes, SEARCH_SESSION_TYPE } from '../../../common'; -import { CheckSearchSessionsDeps, CheckSearchSessionsFn } from './types'; -import { SearchSessionsConfigSchema } from '../../../config'; - -export interface GetSessionsDeps { - savedObjectsClient: SavedObjectsClientContract; - logger: Logger; -} - -export function getSearchSessionsPage$( - { savedObjectsClient }: GetSessionsDeps, - filter: KueryNode, - pageSize: number, - page: number -) { - return from( - savedObjectsClient.find({ - page, - perPage: pageSize, - type: SEARCH_SESSION_TYPE, - namespaces: ['*'], - // process older sessions first - sortField: 'touched', - sortOrder: 'asc', - filter, - }) - ); -} - -export const checkSearchSessionsByPage = ( - checkFn: CheckSearchSessionsFn, - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - filters: any, - nextPage = 1 -): Observable => - checkFn(deps, config, filters, nextPage).pipe( - concatMap((result) => { - if (!result || !result.saved_objects || result.saved_objects.length < config.pageSize) { - return EMPTY; - } else { - // TODO: while processing previous page session list might have been changed and we might skip a session, - // because it would appear now on a different "page". - // This isn't critical, as we would pick it up on a next task iteration, but maybe we could improve this somehow - return checkSearchSessionsByPage(checkFn, deps, config, filters, result.page + 1); - } - }) - ); diff --git a/src/plugins/data/server/search/session/get_search_status.ts b/src/plugins/data/server/search/session/get_search_status.ts index 3ded923376b18..fb34fff1c1d43 100644 --- a/src/plugins/data/server/search/session/get_search_status.ts +++ b/src/plugins/data/server/search/session/get_search_status.ts @@ -9,24 +9,25 @@ import { i18n } from '@kbn/i18n'; import type { TransportResult } from '@elastic/elasticsearch'; import { ElasticsearchClient } from '@kbn/core/server'; -import { SearchSessionRequestInfo } from '../../../common'; -import { AsyncSearchStatusResponse } from '../..'; +import { SearchSessionRequestStatus } from '../../../common'; import { SearchStatus } from './types'; +import { AsyncSearchStatusResponse } from '../..'; export async function getSearchStatus( - client: ElasticsearchClient, + internalClient: ElasticsearchClient, asyncId: string -): Promise> { +): Promise { // TODO: Handle strategies other than the default one // https://github.com/elastic/kibana/issues/127880 try { // @ts-expect-error start_time_in_millis: EpochMillis is string | number - const apiResponse: TransportResult = await client.asyncSearch.status( - { - id: asyncId, - }, - { meta: true } - ); + const apiResponse: TransportResult = + await internalClient.asyncSearch.status( + { + id: asyncId, + }, + { meta: true } + ); const response = apiResponse.body; if ((response.is_partial && !response.is_running) || response.completion_status >= 400) { return { diff --git a/src/plugins/data/server/search/session/get_session_status.test.ts b/src/plugins/data/server/search/session/get_session_status.test.ts index db75e322edda5..b7e323a15f065 100644 --- a/src/plugins/data/server/search/session/get_session_status.test.ts +++ b/src/plugins/data/server/search/session/get_session_status.test.ts @@ -6,72 +6,141 @@ * Side Public License, v 1. */ -import { SearchStatus } from './types'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { getSessionStatus } from './get_session_status'; -import { SearchSessionStatus } from '../../../common'; +import { SearchSessionSavedObjectAttributes, SearchSessionStatus } from '../../../common'; import moment from 'moment'; import { SearchSessionsConfigSchema } from '../../../config'; +const mockInProgressSearchResponse = { + body: { + is_partial: true, + is_running: true, + }, +}; + +const mockErrorSearchResponse = { + body: { + is_partial: false, + is_running: false, + completion_status: 500, + }, +}; + +const mockCompletedSearchResponse = { + body: { + is_partial: false, + is_running: false, + completion_status: 200, + }, +}; + describe('getSessionStatus', () => { - const mockConfig = { - notTouchedInProgressTimeout: moment.duration(1, 'm'), - } as unknown as SearchSessionsConfigSchema; - test("returns an in_progress status if there's nothing inside the session", () => { + beforeEach(() => { + deps.internalClient.asyncSearch.status.mockReset(); + }); + + const mockConfig = {} as unknown as SearchSessionsConfigSchema; + const deps = { internalClient: elasticsearchServiceMock.createElasticsearchClient() }; + test("returns an in_progress status if there's nothing inside the session", async () => { const session: any = { idMapping: {}, touched: moment(), }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); - test("returns an error status if there's at least one error", () => { + test("returns an error status if there's at least one error", async () => { + deps.internalClient.asyncSearch.status.mockImplementation(async ({ id }): Promise => { + switch (id) { + case 'a': + return mockInProgressSearchResponse; + case 'b': + return mockErrorSearchResponse; + case 'c': + return mockCompletedSearchResponse; + default: + // eslint-disable-next-line no-console + console.error('Not mocked search id'); + throw new Error('Not mocked search id'); + } + }); const session: any = { idMapping: { - a: { status: SearchStatus.IN_PROGRESS }, - b: { status: SearchStatus.ERROR, error: 'Nope' }, - c: { status: SearchStatus.COMPLETE }, + a: { + id: 'a', + }, + b: { id: 'b' }, + c: { id: 'c' }, }, }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.ERROR); + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.ERROR); }); - test('expires a empty session after a minute', () => { + test('expires a session if expired < now', async () => { const session: any = { idMapping: {}, - touched: moment().subtract(2, 'm'), + expires: moment().subtract(2, 'm'), }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.EXPIRED); + + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.EXPIRED); }); - test('doesnt expire a full session after a minute', () => { + test('doesnt expire if expire > now', async () => { + deps.internalClient.asyncSearch.status.mockResolvedValue(mockInProgressSearchResponse as any); + const session: any = { idMapping: { - a: { status: SearchStatus.IN_PROGRESS }, + a: { id: 'a' }, }, - touched: moment().subtract(2, 'm'), + expires: moment().add(2, 'm'), }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); - test('returns a complete status if all are complete', () => { + test('returns cancelled status if session was cancelled', async () => { + const session: Partial = { + idMapping: { + a: { id: 'a', strategy: 'ese' }, + }, + isCanceled: true, + expires: moment().subtract(2, 'm').toISOString(), + }; + expect( + await getSessionStatus(deps, session as SearchSessionSavedObjectAttributes, mockConfig) + ).toBe(SearchSessionStatus.CANCELLED); + }); + + test('returns a complete status if all are complete', async () => { + deps.internalClient.asyncSearch.status.mockResolvedValue(mockCompletedSearchResponse as any); + const session: any = { idMapping: { - a: { status: SearchStatus.COMPLETE }, - b: { status: SearchStatus.COMPLETE }, - c: { status: SearchStatus.COMPLETE }, + a: { id: 'a' }, + b: { id: 'b' }, + c: { id: 'c' }, }, }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.COMPLETE); + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.COMPLETE); }); - test('returns a running status if some are still running', () => { + test('returns a running status if some are still running', async () => { + deps.internalClient.asyncSearch.status.mockImplementation(async ({ id }): Promise => { + switch (id) { + case 'a': + return mockInProgressSearchResponse; + default: + return mockCompletedSearchResponse; + } + }); + const session: any = { idMapping: { - a: { status: SearchStatus.IN_PROGRESS }, - b: { status: SearchStatus.COMPLETE }, - c: { status: SearchStatus.IN_PROGRESS }, + a: { id: 'a' }, + b: { id: 'b' }, + c: { id: 'c' }, }, }; - expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); + expect(await getSessionStatus(deps, session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); }); diff --git a/src/plugins/data/server/search/session/get_session_status.ts b/src/plugins/data/server/search/session/get_session_status.ts index 4614ea92318e9..b89b4c487c32c 100644 --- a/src/plugins/data/server/search/session/get_session_status.ts +++ b/src/plugins/data/server/search/session/get_session_status.ts @@ -7,25 +7,40 @@ */ import moment from 'moment'; +import { ElasticsearchClient } from '@kbn/core/server'; import { SearchSessionSavedObjectAttributes, SearchSessionStatus } from '../../../common'; import { SearchStatus } from './types'; import { SearchSessionsConfigSchema } from '../../../config'; +import { getSearchStatus } from './get_search_status'; -export function getSessionStatus( +export async function getSessionStatus( + deps: { internalClient: ElasticsearchClient }, session: SearchSessionSavedObjectAttributes, config: SearchSessionsConfigSchema -): SearchSessionStatus { - const searchStatuses = Object.values(session.idMapping); - const curTime = moment(); +): Promise { + if (session.isCanceled === true) { + return SearchSessionStatus.CANCELLED; + } + + const now = moment(); + + if (moment(session.expires).isBefore(now)) { + return SearchSessionStatus.EXPIRED; + } + + const searches = Object.values(session.idMapping); + const searchStatuses = await Promise.all( + searches.map(async (s) => { + const status = await getSearchStatus(deps.internalClient, s.id); + return { + ...s, + ...status, + }; + }) + ); + if (searchStatuses.some((item) => item.status === SearchStatus.ERROR)) { return SearchSessionStatus.ERROR; - } else if ( - searchStatuses.length === 0 && - curTime.diff(moment(session.touched), 'ms') > - moment.duration(config.notTouchedInProgressTimeout).asMilliseconds() - ) { - // Expire empty sessions that weren't touched for a minute - return SearchSessionStatus.EXPIRED; } else if ( searchStatuses.length > 0 && searchStatuses.every((item) => item.status === SearchStatus.COMPLETE) diff --git a/src/plugins/data/server/search/session/mocks.ts b/src/plugins/data/server/search/session/mocks.ts index 33715810060a9..339ef628356db 100644 --- a/src/plugins/data/server/search/session/mocks.ts +++ b/src/plugins/data/server/search/session/mocks.ts @@ -22,6 +22,7 @@ export function createSearchSessionsClientMock(): jest.Mocked ({ diff --git a/src/plugins/data/server/search/session/session_service.test.ts b/src/plugins/data/server/search/session/session_service.test.ts index 6565eaf11d3be..b44b9a08b8d38 100644 --- a/src/plugins/data/server/search/session/session_service.test.ts +++ b/src/plugins/data/server/search/session/session_service.test.ts @@ -11,17 +11,16 @@ import { SavedObjectsClientContract, SavedObjectsErrorHelpers, } from '@kbn/core/server'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { ElasticsearchClientMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { nodeBuilder } from '@kbn/es-query'; import { SearchSessionService } from './session_service'; import { createRequestHash } from './utils'; import moment from 'moment'; import { coreMock } from '@kbn/core/server/mocks'; import { ConfigSchema } from '../../../config'; -import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../common'; -import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; const MAX_UPDATE_RETRIES = 3; @@ -29,8 +28,8 @@ const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); describe('SearchSessionService', () => { let savedObjectsClient: jest.Mocked; + let elasticsearchClient: ElasticsearchClientMock; let service: SearchSessionService; - let mockTaskManager: jest.Mocked; const MOCK_STRATEGY = 'ese'; @@ -67,19 +66,14 @@ describe('SearchSessionService', () => { describe('Feature disabled', () => { beforeEach(async () => { savedObjectsClient = savedObjectsClientMock.create(); + elasticsearchClient = elasticsearchServiceMock.createElasticsearchClient(); const config: ConfigSchema = { search: { sessions: { enabled: false, - pageSize: 10000, - notTouchedInProgressTimeout: moment.duration(1, 'm'), notTouchedTimeout: moment.duration(2, 'm'), maxUpdateRetries: MAX_UPDATE_RETRIES, defaultExpiration: moment.duration(7, 'd'), - monitoringTaskTimeout: moment.duration(5, 'm'), - cleanupInterval: moment.duration(10, 's'), - trackingInterval: moment.duration(10, 's'), - expireInterval: moment.duration(10, 'm'), management: {} as any, }, }, @@ -90,23 +84,14 @@ describe('SearchSessionService', () => { error: jest.fn(), }; service = new SearchSessionService(mockLogger, config, '8.0.0'); - service.setup(coreMock.createSetup(), { taskManager: taskManagerMock.createSetup() }); - const coreStart = coreMock.createStart(); - mockTaskManager = taskManagerMock.createStart(); + service.setup(coreMock.createSetup(), {}); await flushPromises(); - await service.start(coreStart, { - taskManager: mockTaskManager, - }); }); afterEach(() => { service.stop(); }); - it('task is cleared, if exists', async () => { - expect(mockTaskManager.removeIfExists).toHaveBeenCalled(); - }); - it('trackId ignores', async () => { await service.trackId({ savedObjectsClient }, mockUser1, { params: {} }, '123', { sessionId: '321', @@ -148,19 +133,15 @@ describe('SearchSessionService', () => { describe('Feature enabled', () => { beforeEach(async () => { savedObjectsClient = savedObjectsClientMock.create(); + elasticsearchClient = elasticsearchServiceMock.createElasticsearchClient(); const config: ConfigSchema = { search: { sessions: { enabled: true, pageSize: 10000, - notTouchedInProgressTimeout: moment.duration(1, 'm'), notTouchedTimeout: moment.duration(2, 'm'), maxUpdateRetries: MAX_UPDATE_RETRIES, defaultExpiration: moment.duration(7, 'd'), - trackingInterval: moment.duration(10, 's'), - expireInterval: moment.duration(10, 'm'), - monitoringTaskTimeout: moment.duration(5, 'm'), - cleanupInterval: moment.duration(10, 's'), management: {} as any, }, }, @@ -171,24 +152,17 @@ describe('SearchSessionService', () => { error: jest.fn(), }; service = new SearchSessionService(mockLogger, config, '8.0.0'); - service.setup(coreMock.createSetup(), { taskManager: taskManagerMock.createSetup() }); + service.setup(coreMock.createSetup(), {}); const coreStart = coreMock.createStart(); - mockTaskManager = taskManagerMock.createStart(); + await flushPromises(); - await service.start(coreStart, { - taskManager: mockTaskManager, - }); + await service.start(coreStart, {}); }); afterEach(() => { service.stop(); }); - it('task is cleared and re-created', async () => { - expect(mockTaskManager.removeIfExists).toHaveBeenCalled(); - expect(mockTaskManager.ensureScheduled).toHaveBeenCalled(); - }); - describe('save', () => { it('throws if `name` is not provided', () => { expect(() => @@ -198,7 +172,9 @@ describe('SearchSessionService', () => { it('throws if `appId` is not provided', () => { expect( - service.save({ savedObjectsClient }, mockUser1, sessionId, { name: 'banana' }) + service.save({ savedObjectsClient }, mockUser1, sessionId, { + name: 'banana', + }) ).rejects.toMatchInlineSnapshot(`[Error: AppId is required]`); }); @@ -232,8 +208,6 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(id).toBe(sessionId); expect(callAttributes).not.toHaveProperty('idMapping'); - expect(callAttributes).toHaveProperty('touched'); - expect(callAttributes).toHaveProperty('persisted', true); expect(callAttributes).toHaveProperty('name', 'banana'); expect(callAttributes).toHaveProperty('appId', 'nanana'); expect(callAttributes).toHaveProperty('locatorId', 'panama'); @@ -265,10 +239,8 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(options?.id).toBe(sessionId); expect(callAttributes).toHaveProperty('idMapping', {}); - expect(callAttributes).toHaveProperty('touched'); expect(callAttributes).toHaveProperty('expires'); expect(callAttributes).toHaveProperty('created'); - expect(callAttributes).toHaveProperty('persisted', true); expect(callAttributes).toHaveProperty('name', 'banana'); expect(callAttributes).toHaveProperty('appId', 'nanana'); expect(callAttributes).toHaveProperty('locatorId', 'panama'); @@ -343,13 +315,20 @@ describe('SearchSessionService', () => { total: 1, per_page: 1, page: 0, + statuses: { + [mockSavedObject.id]: { status: SearchSessionStatus.IN_PROGRESS }, + }, }; savedObjectsClient.find.mockResolvedValue(mockResponse); const options = { page: 0, perPage: 5 }; - const response = await service.find({ savedObjectsClient }, mockUser1, options); + const response = await service.find( + { savedObjectsClient, internalElasticsearchClient: elasticsearchClient }, + mockUser1, + options + ); - expect(response).toBe(mockResponse); + expect(response).toEqual(mockResponse); const [[findOptions]] = savedObjectsClient.find.mock.calls; expect(findOptions).toMatchInlineSnapshot(` Object { @@ -424,17 +403,28 @@ describe('SearchSessionService', () => { total: 1, per_page: 1, page: 0, + statuses: { + [mockSavedObject.id]: { status: SearchSessionStatus.IN_PROGRESS }, + }, }; savedObjectsClient.find.mockResolvedValue(mockResponse); const options1 = { filter: 'foobar' }; - const response1 = await service.find({ savedObjectsClient }, mockUser1, options1); + const response1 = await service.find( + { savedObjectsClient, internalElasticsearchClient: elasticsearchClient }, + mockUser1, + options1 + ); const options2 = { filter: nodeBuilder.is('foo', 'bar') }; - const response2 = await service.find({ savedObjectsClient }, mockUser1, options2); + const response2 = await service.find( + { savedObjectsClient, internalElasticsearchClient: elasticsearchClient }, + mockUser1, + options2 + ); - expect(response1).toBe(mockResponse); - expect(response2).toBe(mockResponse); + expect(response1).toEqual(mockResponse); + expect(response2).toEqual(mockResponse); const [[findOptions1], [findOptions2]] = savedObjectsClient.find.mock.calls; expect(findOptions1).toMatchInlineSnapshot(` @@ -599,13 +589,20 @@ describe('SearchSessionService', () => { total: 1, per_page: 1, page: 0, + statuses: { + [mockSavedObject.id]: { status: SearchSessionStatus.IN_PROGRESS }, + }, }; savedObjectsClient.find.mockResolvedValue(mockResponse); const options = { page: 0, perPage: 5 }; - const response = await service.find({ savedObjectsClient }, null, options); + const response = await service.find( + { savedObjectsClient, internalElasticsearchClient: elasticsearchClient }, + null, + options + ); - expect(response).toBe(mockResponse); + expect(response).toEqual(mockResponse); const [[findOptions]] = savedObjectsClient.find.mock.calls; expect(findOptions).toMatchInlineSnapshot(` Object { @@ -642,7 +639,6 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(id).toBe(sessionId); expect(callAttributes).toHaveProperty('name', attributes.name); - expect(callAttributes).toHaveProperty('touched'); }); it('throws if user conflicts', () => { @@ -675,7 +671,6 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(id).toBe(sessionId); expect(callAttributes).toHaveProperty('name', 'new_name'); - expect(callAttributes).toHaveProperty('touched'); }); }); @@ -688,8 +683,7 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(id).toBe(sessionId); - expect(callAttributes).toHaveProperty('status', SearchSessionStatus.CANCELLED); - expect(callAttributes).toHaveProperty('touched'); + expect(callAttributes).toHaveProperty('isCanceled', true); }); it('throws if user conflicts', () => { @@ -709,8 +703,7 @@ describe('SearchSessionService', () => { expect(type).toBe(SEARCH_SESSION_TYPE); expect(id).toBe(sessionId); - expect(callAttributes).toHaveProperty('status', SearchSessionStatus.CANCELLED); - expect(callAttributes).toHaveProperty('touched'); + expect(callAttributes).toHaveProperty('isCanceled', true); }); }); @@ -740,11 +733,9 @@ describe('SearchSessionService', () => { expect(callAttributes).toHaveProperty('idMapping', { [requestHash]: { id: searchId, - status: SearchSessionStatus.IN_PROGRESS, strategy: MOCK_STRATEGY, }, }); - expect(callAttributes).toHaveProperty('touched'); }); it('retries updating the saved object if there was a ES conflict 409', async () => { @@ -827,15 +818,12 @@ describe('SearchSessionService', () => { expect(callAttributes).toHaveProperty('idMapping', { [requestHash]: { id: searchId, - status: SearchSessionStatus.IN_PROGRESS, strategy: MOCK_STRATEGY, }, }); expect(callAttributes).toHaveProperty('expires'); expect(callAttributes).toHaveProperty('created'); - expect(callAttributes).toHaveProperty('touched'); expect(callAttributes).toHaveProperty('sessionId', sessionId); - expect(callAttributes).toHaveProperty('persisted', false); }); it('retries updating if update returned 404 and then update returned conflict 409 (first create race condition)', async () => { @@ -939,16 +927,13 @@ describe('SearchSessionService', () => { expect(callAttributes1).toHaveProperty('idMapping', { [requestHash1]: { id: searchId1, - status: SearchSessionStatus.IN_PROGRESS, strategy: MOCK_STRATEGY, }, [requestHash2]: { id: searchId2, - status: SearchSessionStatus.IN_PROGRESS, strategy: MOCK_STRATEGY, }, }); - expect(callAttributes1).toHaveProperty('touched'); const [type2, id2, callAttributes2] = savedObjectsClient.update.mock.calls[1]; expect(type2).toBe(SEARCH_SESSION_TYPE); @@ -956,11 +941,9 @@ describe('SearchSessionService', () => { expect(callAttributes2).toHaveProperty('idMapping', { [requestHash3]: { id: searchId3, - status: SearchSessionStatus.IN_PROGRESS, strategy: MOCK_STRATEGY, }, }); - expect(callAttributes2).toHaveProperty('touched'); }); }); diff --git a/src/plugins/data/server/search/session/session_service.ts b/src/plugins/data/server/search/session/session_service.ts index e46c9bc80a2cc..d82956d19f73f 100644 --- a/src/plugins/data/server/search/session/session_service.ts +++ b/src/plugins/data/server/search/session/session_service.ts @@ -8,67 +8,48 @@ import { notFound } from '@hapi/boom'; import { debounce } from 'lodash'; -import { nodeBuilder, fromKueryExpression } from '@kbn/es-query'; +import { fromKueryExpression, nodeBuilder } from '@kbn/es-query'; import { CoreSetup, CoreStart, KibanaRequest, - SavedObjectsClientContract, Logger, SavedObject, - SavedObjectsFindOptions, + SavedObjectsClientContract, SavedObjectsErrorHelpers, + SavedObjectsFindOptions, + ElasticsearchClient, } from '@kbn/core/server'; import type { AuthenticatedUser, SecurityPluginSetup } from '@kbn/security-plugin/server'; -import type { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; import { + ENHANCED_ES_SEARCH_STRATEGY, IKibanaSearchRequest, ISearchOptions, - ENHANCED_ES_SEARCH_STRATEGY, SEARCH_SESSION_TYPE, SearchSessionRequestInfo, SearchSessionSavedObjectAttributes, - SearchSessionStatus, + SearchSessionsFindResponse, + SearchSessionStatusResponse, } from '../../../common'; import { ISearchSessionService, NoSearchIdInSessionError } from '../..'; import { createRequestHash } from './utils'; import { ConfigSchema, SearchSessionsConfigSchema } from '../../../config'; -import { - registerSearchSessionsTask, - scheduleSearchSessionsTask, - unscheduleSearchSessionsTask, -} from './setup_task'; -import { SearchStatus } from './types'; -import { - checkPersistedSessionsProgress, - SEARCH_SESSIONS_TASK_ID, - SEARCH_SESSIONS_TASK_TYPE, -} from './check_persisted_sessions'; -import { - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - checkNonPersistedSessions, - SEARCH_SESSIONS_CLEANUP_TASK_ID, -} from './check_non_persisted_sessions'; -import { - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - SEARCH_SESSIONS_EXPIRE_TASK_ID, - checkPersistedCompletedSessionExpiration, -} from './expire_persisted_sessions'; +import { getSessionStatus } from './get_session_status'; export interface SearchSessionDependencies { savedObjectsClient: SavedObjectsClientContract; } + +export interface SearchSessionStatusDependencies extends SearchSessionDependencies { + internalElasticsearchClient: ElasticsearchClient; +} + interface SetupDependencies { - taskManager: TaskManagerSetupContract; security?: SecurityPluginSetup; } -interface StartDependencies { - taskManager: TaskManagerStartContract; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface StartDependencies {} const DEBOUNCE_UPDATE_OR_CREATE_WAIT = 1000; const DEBOUNCE_UPDATE_OR_CREATE_MAX_WAIT = 5000; @@ -101,83 +82,17 @@ export class SearchSessionService implements ISearchSessionService { public setup(core: CoreSetup, deps: SetupDependencies) { this.security = deps.security; - const taskDeps = { - config: this.config, - taskManager: deps.taskManager, - logger: this.logger, - }; - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_TASK_TYPE, - 'persisted session progress', - checkPersistedSessionsProgress - ); - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - 'non persisted session cleanup', - checkNonPersistedSessions - ); - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - 'complete session expiration', - checkPersistedCompletedSessionExpiration - ); this.setupCompleted = true; } - public async start(core: CoreStart, deps: StartDependencies) { + public start(core: CoreStart, deps: StartDependencies) { if (!this.setupCompleted) throw new Error('SearchSessionService setup() must be called before start()'); - - return this.setupMonitoring(core, deps); } public stop() {} - private setupMonitoring = async (core: CoreStart, deps: StartDependencies) => { - const taskDeps = { - config: this.config, - taskManager: deps.taskManager, - logger: this.logger, - }; - - if (this.sessionConfig.enabled) { - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_TASK_ID, - SEARCH_SESSIONS_TASK_TYPE, - this.sessionConfig.trackingInterval - ); - - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_CLEANUP_TASK_ID, - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - this.sessionConfig.cleanupInterval - ); - - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_EXPIRE_TASK_ID, - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - this.sessionConfig.expireInterval - ); - } else { - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_TASK_ID); - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_CLEANUP_TASK_ID); - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_EXPIRE_TASK_ID); - } - }; - private processUpdateOrCreateBatchQueue = debounce( () => { const queue = [...this.updateOrCreateBatchQueue]; @@ -304,7 +219,6 @@ export class SearchSessionService implements ISearchSessionService { locatorId, initialState, restoreState, - persisted: true, }); }; @@ -324,14 +238,11 @@ export class SearchSessionService implements ISearchSessionService { SEARCH_SESSION_TYPE, { sessionId, - status: SearchSessionStatus.IN_PROGRESS, expires: new Date( Date.now() + this.sessionConfig.defaultExpiration.asMilliseconds() ).toISOString(), created: new Date().toISOString(), - touched: new Date().toISOString(), idMapping: {}, - persisted: false, version: this.version, realmType, realmName, @@ -353,14 +264,15 @@ export class SearchSessionService implements ISearchSessionService { sessionId ); this.throwOnUserConflict(user, session); + return session; }; - public find = ( - { savedObjectsClient }: SearchSessionDependencies, + public find = async ( + { savedObjectsClient, internalElasticsearchClient }: SearchSessionStatusDependencies, user: AuthenticatedUser | null, options: Omit - ) => { + ): Promise => { const userFilters = user === null ? [] @@ -378,11 +290,31 @@ export class SearchSessionService implements ISearchSessionService { const filterKueryNode = typeof options.filter === 'string' ? fromKueryExpression(options.filter) : options.filter; const filter = nodeBuilder.and(userFilters.concat(filterKueryNode ?? [])); - return savedObjectsClient.find({ + const findResponse = await savedObjectsClient.find({ ...options, filter, type: SEARCH_SESSION_TYPE, }); + + const sessionStatuses = await Promise.all( + findResponse.saved_objects.map(async (so) => { + const sessionStatus = await getSessionStatus( + { internalClient: internalElasticsearchClient }, + so.attributes, + this.sessionConfig + ); + + return sessionStatus; + }) + ); + + return { + ...findResponse, + statuses: sessionStatuses.reduce((res, status, index) => { + res[findResponse.saved_objects[index].id] = { status }; + return res; + }, {} as Record), + }; }; public update = async ( @@ -399,7 +331,6 @@ export class SearchSessionService implements ISearchSessionService { sessionId, { ...attributes, - touched: new Date().toISOString(), } ); }; @@ -419,9 +350,9 @@ export class SearchSessionService implements ISearchSessionService { user: AuthenticatedUser | null, sessionId: string ) => { - this.logger.debug(`delete | ${sessionId}`); + this.logger.debug(`cancel | ${sessionId}`); return this.update(deps, user, sessionId, { - status: SearchSessionStatus.CANCELLED, + isCanceled: true, }); }; @@ -445,8 +376,9 @@ export class SearchSessionService implements ISearchSessionService { user: AuthenticatedUser | null, searchRequest: IKibanaSearchRequest, searchId: string, - { sessionId, strategy = ENHANCED_ES_SEARCH_STRATEGY }: ISearchOptions + options: ISearchOptions ) => { + const { sessionId, strategy = ENHANCED_ES_SEARCH_STRATEGY } = options; if (!this.sessionConfig.enabled || !sessionId || !searchId) return; this.logger.debug(`trackId | ${sessionId} | ${searchId}`); @@ -454,10 +386,9 @@ export class SearchSessionService implements ISearchSessionService { if (searchRequest.params) { const requestHash = createRequestHash(searchRequest.params); - const searchInfo = { + const searchInfo: SearchSessionRequestInfo = { id: searchId, strategy, - status: SearchStatus.IN_PROGRESS, }; idMapping = { [requestHash]: searchInfo }; } @@ -471,6 +402,7 @@ export class SearchSessionService implements ISearchSessionService { sessionId: string ) { const searchSession = await this.get(deps, user, sessionId); + const searchIdMapping = new Map(); Object.values(searchSession.attributes.idMapping).forEach((requestInfo) => { searchIdMapping.set(requestInfo.id, requestInfo.strategy); @@ -478,6 +410,23 @@ export class SearchSessionService implements ISearchSessionService { return searchIdMapping; } + public async status( + deps: SearchSessionStatusDependencies, + user: AuthenticatedUser | null, + sessionId: string + ): Promise { + this.logger.debug(`status | ${sessionId}`); + const session = await this.get(deps, user, sessionId); + + const sessionStatus = await getSessionStatus( + { internalClient: deps.internalElasticsearchClient }, + session.attributes, + this.sessionConfig + ); + + return { status: sessionStatus }; + } + /** * Look up an existing search ID that matches the given request in the given session so that the * request can continue rather than restart. @@ -510,13 +459,15 @@ export class SearchSessionService implements ISearchSessionService { return session.attributes.idMapping[requestHash].id; }; - public asScopedProvider = ({ savedObjects }: CoreStart) => { + public asScopedProvider = ({ savedObjects, elasticsearch }: CoreStart) => { return (request: KibanaRequest) => { const user = this.security?.authc.getCurrentUser(request) ?? null; const savedObjectsClient = savedObjects.getScopedClient(request, { includedHiddenTypes: [SEARCH_SESSION_TYPE], }); - const deps = { savedObjectsClient }; + + const internalElasticsearchClient = elasticsearch.client.asScoped(request).asInternalUser; + const deps = { savedObjectsClient, internalElasticsearchClient }; return { getId: this.getId.bind(this, deps, user), trackId: this.trackId.bind(this, deps, user), @@ -528,6 +479,7 @@ export class SearchSessionService implements ISearchSessionService { extend: this.extend.bind(this, deps, user), cancel: this.cancel.bind(this, deps, user), delete: this.delete.bind(this, deps, user), + status: this.status.bind(this, deps, user), getConfig: () => this.config.search.sessions, }; }; diff --git a/src/plugins/data/server/search/session/setup_task.ts b/src/plugins/data/server/search/session/setup_task.ts deleted file mode 100644 index 5fe44b0901b79..0000000000000 --- a/src/plugins/data/server/search/session/setup_task.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Duration } from 'moment'; -import { filter, takeUntil } from 'rxjs/operators'; -import { BehaviorSubject } from 'rxjs'; -import type { RunContext, TaskRunCreatorFunction } from '@kbn/task-manager-plugin/server'; -import { CoreSetup, SavedObjectsClient } from '@kbn/core/server'; -import { SEARCH_SESSION_TYPE } from '../../../common'; -import { - SearchSessionTaskSetupDeps, - SearchSessionTaskStartDeps, - SearchSessionTaskFn, -} from './types'; - -export function searchSessionTaskRunner( - core: CoreSetup, - deps: SearchSessionTaskSetupDeps, - title: string, - checkFn: SearchSessionTaskFn -): TaskRunCreatorFunction { - const { logger, config } = deps; - return ({ taskInstance }: RunContext) => { - const aborted$ = new BehaviorSubject(false); - return { - async run() { - try { - const sessionConfig = config.search.sessions; - const [coreStart] = await core.getStartServices(); - if (!sessionConfig.enabled) { - logger.debug(`Search sessions are disabled. Skipping task ${title}.`); - return; - } - if (aborted$.getValue()) return; - - const internalRepo = coreStart.savedObjects.createInternalRepository([ - SEARCH_SESSION_TYPE, - ]); - const internalSavedObjectsClient = new SavedObjectsClient(internalRepo); - await checkFn( - { - logger, - client: coreStart.elasticsearch.client.asInternalUser, - savedObjectsClient: internalSavedObjectsClient, - }, - sessionConfig - ) - .pipe(takeUntil(aborted$.pipe(filter((aborted) => aborted)))) - .toPromise(); - - return { - state: {}, - }; - } catch (e) { - logger.error(`An error occurred. Skipping task ${title}.`); - } - }, - cancel: async () => { - aborted$.next(true); - }, - }; - }; -} - -export function registerSearchSessionsTask( - core: CoreSetup, - deps: SearchSessionTaskSetupDeps, - taskType: string, - title: string, - checkFn: SearchSessionTaskFn -) { - deps.taskManager.registerTaskDefinitions({ - [taskType]: { - title, - createTaskRunner: searchSessionTaskRunner(core, deps, title, checkFn), - timeout: `${deps.config.search.sessions.monitoringTaskTimeout.asSeconds()}s`, - }, - }); -} - -export async function unscheduleSearchSessionsTask( - { taskManager, logger }: SearchSessionTaskStartDeps, - taskId: string -) { - try { - await taskManager.removeIfExists(taskId); - logger.debug(`${taskId} cleared`); - } catch (e) { - logger.error(`${taskId} Error clearing task ${e.message}`); - } -} - -export async function scheduleSearchSessionsTask( - { taskManager, logger }: SearchSessionTaskStartDeps, - taskId: string, - taskType: string, - interval: Duration -) { - await taskManager.removeIfExists(taskId); - - try { - await taskManager.ensureScheduled({ - id: taskId, - taskType, - schedule: { - interval: `${interval.asSeconds()}s`, - }, - state: {}, - params: {}, - }); - - logger.debug(`${taskId} scheduled to run`); - } catch (e) { - logger.error(`${taskId} Error scheduling task ${e.message}`); - } -} diff --git a/src/plugins/data/server/search/session/types.ts b/src/plugins/data/server/search/session/types.ts index e39a33774f073..76d5f30028736 100644 --- a/src/plugins/data/server/search/session/types.ts +++ b/src/plugins/data/server/search/session/types.ts @@ -6,26 +6,23 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; import { CoreStart, KibanaRequest, SavedObject, SavedObjectsFindOptions, - SavedObjectsFindResponse, SavedObjectsUpdateResponse, - ElasticsearchClient, - Logger, - SavedObjectsClientContract, } from '@kbn/core/server'; -import type { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; -import { KueryNode } from '@kbn/es-query'; -import { SearchSessionSavedObjectAttributes } from '../../../common'; -import { IKibanaSearchRequest, ISearchOptions } from '../../../common/search'; -import { SearchSessionsConfigSchema, ConfigSchema } from '../../../config'; +import { + IKibanaSearchRequest, + ISearchOptions, + SearchSessionsFindResponse, + SearchSessionSavedObjectAttributes, + SearchSessionStatusResponse, +} from '../../../common/search'; +import { SearchSessionsConfigSchema } from '../../../config'; + +export { SearchStatus } from '../../../common/search'; export interface IScopedSearchSessionsClient { getId: (request: IKibanaSearchRequest, options: ISearchOptions) => Promise; @@ -40,9 +37,7 @@ export interface IScopedSearchSessionsClient { attributes: Partial ) => Promise | undefined>; get: (sessionId: string) => Promise>; - find: ( - options: Omit - ) => Promise>; + find: (options: Omit) => Promise; update: ( sessionId: string, attributes: Partial @@ -53,50 +48,10 @@ export interface IScopedSearchSessionsClient { sessionId: string, expires: Date ) => Promise>; + status: (sessionId: string) => Promise; getConfig: () => SearchSessionsConfigSchema | null; } export interface ISearchSessionService { asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient; } - -export enum SearchStatus { - IN_PROGRESS = 'in_progress', - ERROR = 'error', - COMPLETE = 'complete', -} - -export interface CheckSearchSessionsDeps { - savedObjectsClient: SavedObjectsClientContract; - client: ElasticsearchClient; - logger: Logger; -} - -export interface SearchSessionTaskSetupDeps { - taskManager: TaskManagerSetupContract; - logger: Logger; - config: ConfigSchema; -} - -export interface SearchSessionTaskStartDeps { - taskManager: TaskManagerStartContract; - logger: Logger; - config: ConfigSchema; -} - -export type SearchSessionTaskFn = ( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema -) => Observable; - -export type SearchSessionsResponse = SavedObjectsFindResponse< - SearchSessionSavedObjectAttributes, - unknown ->; - -export type CheckSearchSessionsFn = ( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - filter: KueryNode, - page: number -) => Observable; diff --git a/src/plugins/data/server/search/session/update_session_status.test.ts b/src/plugins/data/server/search/session/update_session_status.test.ts deleted file mode 100644 index 38e8ec6cad150..0000000000000 --- a/src/plugins/data/server/search/session/update_session_status.test.ts +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { bulkUpdateSessions, updateSessionStatus } from './update_session_status'; -import { SearchSessionStatus, SearchSessionSavedObjectAttributes } from '../../../common'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { SearchStatus } from './types'; -import moment from 'moment'; -import { - SavedObjectsBulkUpdateObject, - SavedObjectsClientContract, - SavedObjectsFindResult, -} from '@kbn/core/server'; - -describe('bulkUpdateSessions', () => { - let mockClient: any; - const mockConfig: any = {}; - let savedObjectsClient: jest.Mocked; - const mockLogger: any = { - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }; - - beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - mockClient = { - asyncSearch: { - status: jest.fn(), - delete: jest.fn(), - }, - eql: { - status: jest.fn(), - delete: jest.fn(), - }, - }; - }); - - describe('updateSessionStatus', () => { - test('updates expired session', async () => { - const so: SavedObjectsFindResult = { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - expires: moment().subtract(moment.duration(5, 'd')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - - const updated = await updateSessionStatus( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - mockConfig, - so - ); - - expect(updated).toBeTruthy(); - expect(so.attributes.status).toBe(SearchSessionStatus.EXPIRED); - }); - - test('does nothing if the search is still running', async () => { - const so = { - id: '123', - attributes: { - persisted: false, - status: SearchSessionStatus.IN_PROGRESS, - created: moment().subtract(moment.duration(3, 'm')), - touched: moment().subtract(moment.duration(10, 's')), - expires: moment().add(moment.duration(5, 'd')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: true, - is_running: true, - }, - }); - - const updated = await updateSessionStatus( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - mockConfig, - so - ); - - expect(updated).toBeFalsy(); - expect(so.attributes.status).toBe(SearchSessionStatus.IN_PROGRESS); - }); - - test("doesn't re-check completed or errored searches", async () => { - const so = { - id: '123', - attributes: { - expires: moment().add(moment.duration(5, 'd')), - status: SearchSessionStatus.ERROR, - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.COMPLETE, - }, - 'another-search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.ERROR, - }, - }, - }, - } as any; - - const updated = await updateSessionStatus( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - mockConfig, - so - ); - - expect(updated).toBeFalsy(); - expect(mockClient.asyncSearch.status).not.toBeCalled(); - }); - - test('updates to complete if the search is done', async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - const so = { - attributes: { - status: SearchSessionStatus.IN_PROGRESS, - touched: '123', - expires: moment().add(moment.duration(5, 'd')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: false, - is_running: false, - completion_status: 200, - }, - }); - - const updated = await updateSessionStatus( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - mockConfig, - so - ); - - expect(updated).toBeTruthy(); - - expect(mockClient.asyncSearch.status).toBeCalledWith({ id: 'search-id' }, { meta: true }); - expect(so.attributes.status).toBe(SearchSessionStatus.COMPLETE); - expect(so.attributes.status).toBe(SearchSessionStatus.COMPLETE); - expect(so.attributes.touched).not.toBe('123'); - expect(so.attributes.completed).not.toBeUndefined(); - expect(so.attributes.idMapping['search-hash'].status).toBe(SearchStatus.COMPLETE); - expect(so.attributes.idMapping['search-hash'].error).toBeUndefined(); - }); - - test('updates to error if the search is errored', async () => { - savedObjectsClient.bulkUpdate = jest.fn(); - const so = { - attributes: { - expires: moment().add(moment.duration(5, 'd')), - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - - mockClient.asyncSearch.status.mockResolvedValue({ - body: { - is_partial: false, - is_running: false, - completion_status: 500, - }, - }); - - const updated = await updateSessionStatus( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - mockConfig, - so - ); - - expect(updated).toBeTruthy(); - expect(so.attributes.status).toBe(SearchSessionStatus.ERROR); - expect(so.attributes.touched).not.toBe('123'); - expect(so.attributes.idMapping['search-hash'].status).toBe(SearchStatus.ERROR); - expect(so.attributes.idMapping['search-hash'].error).toBe( - 'Search completed with a 500 status' - ); - }); - }); - - describe('bulkUpdateSessions', () => { - test('does nothing if there are no open sessions', async () => { - await bulkUpdateSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - [] - ); - - expect(savedObjectsClient.bulkUpdate).not.toBeCalled(); - expect(savedObjectsClient.delete).not.toBeCalled(); - }); - - test('updates in space', async () => { - const so = { - namespaces: ['awesome'], - attributes: { - expires: moment().add(moment.duration(5, 'd')), - status: SearchSessionStatus.IN_PROGRESS, - touched: '123', - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - - savedObjectsClient.bulkUpdate = jest.fn().mockResolvedValue({ - saved_objects: [so], - }); - - await bulkUpdateSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - [so] - ); - - const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; - const updatedAttributes = updateInput[0] as SavedObjectsBulkUpdateObject; - expect(updatedAttributes.namespace).toBe('awesome'); - }); - - test('logs failures', async () => { - const so = { - namespaces: ['awesome'], - attributes: { - expires: moment().add(moment.duration(5, 'd')), - status: SearchSessionStatus.IN_PROGRESS, - touched: '123', - idMapping: { - 'search-hash': { - id: 'search-id', - strategy: 'cool', - status: SearchStatus.IN_PROGRESS, - }, - }, - }, - } as any; - - savedObjectsClient.bulkUpdate = jest.fn().mockResolvedValue({ - saved_objects: [ - { - error: 'nope', - }, - ], - }); - - await bulkUpdateSessions( - { - savedObjectsClient, - client: mockClient, - logger: mockLogger, - }, - [so] - ); - - expect(savedObjectsClient.bulkUpdate).toBeCalledTimes(1); - expect(mockLogger.error).toBeCalledTimes(1); - }); - }); -}); diff --git a/src/plugins/data/server/search/session/update_session_status.ts b/src/plugins/data/server/search/session/update_session_status.ts deleted file mode 100644 index e8405eb5427b0..0000000000000 --- a/src/plugins/data/server/search/session/update_session_status.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SavedObjectsFindResult, SavedObjectsUpdateResponse } from '@kbn/core/server'; -import { SearchSessionsConfigSchema } from '../../../config'; -import { - SearchSessionRequestInfo, - SearchSessionSavedObjectAttributes, - SearchSessionStatus, -} from '../../../common'; -import { getSearchStatus } from './get_search_status'; -import { getSessionStatus } from './get_session_status'; -import { CheckSearchSessionsDeps, SearchSessionsResponse, SearchStatus } from './types'; -import { isSearchSessionExpired } from './utils'; - -export async function updateSessionStatus( - { logger, client }: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - session: SavedObjectsFindResult -) { - let sessionUpdated = false; - const isExpired = isSearchSessionExpired(session); - - if (!isExpired) { - // Check statuses of all running searches - await Promise.all( - Object.keys(session.attributes.idMapping).map(async (searchKey: string) => { - const updateSearchRequest = ( - currentStatus: Pick - ) => { - sessionUpdated = true; - session.attributes.idMapping[searchKey] = { - ...session.attributes.idMapping[searchKey], - ...currentStatus, - }; - }; - - const searchInfo = session.attributes.idMapping[searchKey]; - if (searchInfo.status === SearchStatus.IN_PROGRESS) { - try { - const currentStatus = await getSearchStatus(client, searchInfo.id); - - if (currentStatus.status !== searchInfo.status) { - logger.debug(`search ${searchInfo.id} | status changed to ${currentStatus.status}`); - updateSearchRequest(currentStatus); - } - } catch (e) { - logger.error(e); - updateSearchRequest({ - status: SearchStatus.ERROR, - error: e.message || e.meta.error?.caused_by?.reason, - }); - } - } - }) - ); - } - - // And only then derive the session's status - const sessionStatus = isExpired - ? SearchSessionStatus.EXPIRED - : getSessionStatus(session.attributes, config); - if (sessionStatus !== session.attributes.status) { - const now = new Date().toISOString(); - session.attributes.status = sessionStatus; - session.attributes.touched = now; - if (sessionStatus === SearchSessionStatus.COMPLETE) { - session.attributes.completed = now; - } else if (session.attributes.completed) { - session.attributes.completed = null; - } - sessionUpdated = true; - } - - return sessionUpdated; -} - -export async function getAllSessionsStatusUpdates( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfigSchema, - searchSessions: SearchSessionsResponse -) { - const updatedSessions = new Array>(); - - await Promise.all( - searchSessions.saved_objects.map(async (session) => { - const updated = await updateSessionStatus(deps, config, session); - - if (updated) { - updatedSessions.push(session); - } - }) - ); - - return updatedSessions; -} - -export async function bulkUpdateSessions( - { logger, savedObjectsClient }: CheckSearchSessionsDeps, - updatedSessions: Array> -) { - if (updatedSessions.length) { - // If there's an error, we'll try again in the next iteration, so there's no need to check the output. - const updatedResponse = await savedObjectsClient.bulkUpdate( - updatedSessions.map((session) => ({ - ...session, - namespace: session.namespaces?.[0], - })) - ); - - const success: Array> = []; - const fail: Array> = []; - - updatedResponse.saved_objects.forEach((savedObjectResponse) => { - if ('error' in savedObjectResponse) { - fail.push(savedObjectResponse); - logger.error( - `Error while updating search session ${savedObjectResponse?.id}: ${savedObjectResponse.error?.message}` - ); - } else { - success.push(savedObjectResponse); - } - }); - - logger.debug(`Updating search sessions: success: ${success.length}, fail: ${fail.length}`); - } -} diff --git a/src/plugins/data/server/search/strategies/common/async_utils.test.ts b/src/plugins/data/server/search/strategies/common/async_utils.test.ts index 7c90a0fd4c124..9771a042f1872 100644 --- a/src/plugins/data/server/search/strategies/common/async_utils.test.ts +++ b/src/plugins/data/server/search/strategies/common/async_utils.test.ts @@ -21,7 +21,7 @@ const getMockSearchSessionsConfig = ({ describe('request utils', () => { describe('getCommonDefaultAsyncSubmitParams', () => { - test('Uses `keep_alive` from default params if no `sessionId` is provided', async () => { + test('Uses short `keep_alive` if no `sessionId` is provided', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), }); @@ -29,13 +29,24 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Uses `keep_alive` from config if enabled', async () => { + test('Uses short `keep_alive` if sessions enabled but no yet saved', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), }); const params = getCommonDefaultAsyncSubmitParams(mockConfig, { sessionId: 'foo', }); + expect(params).toHaveProperty('keep_alive', '1m'); + }); + + test('Uses `keep_alive` from config if sessions enabled and session is saved', async () => { + const mockConfig = getMockSearchSessionsConfig({ + defaultExpiration: moment.duration(3, 'd'), + }); + const params = getCommonDefaultAsyncSubmitParams(mockConfig, { + sessionId: 'foo', + isStored: true, + }); expect(params).toHaveProperty('keep_alive', '259200000ms'); }); @@ -89,12 +100,51 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Has no `keep_alive` if `sessionId` is provided', async () => { + test('Has short `keep_alive` if `sessionId` is provided', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), enabled: true, }); const params = getCommonDefaultAsyncGetParams(mockConfig, { sessionId: 'foo' }); + expect(params).toHaveProperty('keep_alive', '1m'); + }); + + test('Has `keep_alive` from config if `sessionId` is provided and session is stored', async () => { + const mockConfig = getMockSearchSessionsConfig({ + defaultExpiration: moment.duration(3, 'd'), + enabled: true, + }); + const params = getCommonDefaultAsyncGetParams(mockConfig, { + sessionId: 'foo', + isStored: true, + }); + expect(params).toHaveProperty('keep_alive', '259200000ms'); + }); + + test("Don't extend keepAlive if search has already been extended", async () => { + const mockConfig = getMockSearchSessionsConfig({ + defaultExpiration: moment.duration(3, 'd'), + enabled: true, + }); + const params = getCommonDefaultAsyncGetParams(mockConfig, { + sessionId: 'foo', + isStored: true, + isSearchStored: true, + }); + expect(params).not.toHaveProperty('keep_alive'); + }); + + test("Don't extend keepAlive if search is being restored", async () => { + const mockConfig = getMockSearchSessionsConfig({ + defaultExpiration: moment.duration(3, 'd'), + enabled: true, + }); + const params = getCommonDefaultAsyncGetParams(mockConfig, { + sessionId: 'foo', + isStored: true, + isSearchStored: false, + isRestore: true, + }); expect(params).not.toHaveProperty('keep_alive'); }); diff --git a/src/plugins/data/server/search/strategies/common/async_utils.ts b/src/plugins/data/server/search/strategies/common/async_utils.ts index 46483ca3f3279..c0af68f915bd8 100644 --- a/src/plugins/data/server/search/strategies/common/async_utils.ts +++ b/src/plugins/data/server/search/strategies/common/async_utils.ts @@ -25,9 +25,10 @@ export function getCommonDefaultAsyncSubmitParams( > { const useSearchSessions = searchSessionsConfig?.enabled && !!options.sessionId; - const keepAlive = useSearchSessions - ? `${searchSessionsConfig!.defaultExpiration.asMilliseconds()}ms` - : '1m'; + const keepAlive = + useSearchSessions && options.isStored + ? `${searchSessionsConfig!.defaultExpiration.asMilliseconds()}ms` + : '1m'; return { // Wait up to 100ms for the response to return @@ -51,9 +52,13 @@ export function getCommonDefaultAsyncGetParams( return { // Wait up to 100ms for the response to return wait_for_completion_timeout: '100ms', - ...(useSearchSessions - ? // Don't change the expiration of search requests that are tracked in a search session - undefined + ...(useSearchSessions && options.isStored + ? // Use session's keep_alive if search belongs to a stored session + options.isSearchStored || options.isRestore // if search was already stored and extended, then no need to extend keepAlive + ? {} + : { + keep_alive: `${searchSessionsConfig!.defaultExpiration.asMilliseconds()}ms`, + } : { // We still need to do polling for searches not within the context of a search session or when search session disabled keep_alive: '1m', diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts index 409c84a4638f7..c2c42f1ff8963 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts @@ -205,7 +205,7 @@ describe('ES search strategy', () => { }); describe('with sessionId', () => { - it('makes a POST request with params (long keepalive)', async () => { + it('Submit search with session id that is not saved creates a search with short keep_alive', async () => { mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse); const params = { index: 'logstash-*', body: { query: {} } }; @@ -218,10 +218,26 @@ describe('ES search strategy', () => { expect(request.index).toEqual(params.index); expect(request.body).toEqual(params.body); + expect(request).toHaveProperty('keep_alive', '1m'); + }); + + it('Submit search with session id and session is saved creates a search with long keep_alive', async () => { + mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'logstash-*', body: { query: {} } }; + const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger); + + await esSearch.search({ params }, { sessionId: '1', isStored: true }, mockDeps).toPromise(); + + expect(mockSubmitCaller).toBeCalled(); + const request = mockSubmitCaller.mock.calls[0][0]; + expect(request.index).toEqual(params.index); + expect(request.body).toEqual(params.body); + expect(request).toHaveProperty('keep_alive', '604800000ms'); }); - it('makes a GET request to async search without keepalive', async () => { + it('makes a GET request to async search with short keepalive, if session is not saved', async () => { mockGetCaller.mockResolvedValueOnce(mockAsyncResponse); const params = { index: 'logstash-*', body: { query: {} } }; @@ -229,6 +245,44 @@ describe('ES search strategy', () => { await esSearch.search({ id: 'foo', params }, { sessionId: '1' }, mockDeps).toPromise(); + expect(mockGetCaller).toBeCalled(); + const request = mockGetCaller.mock.calls[0][0]; + expect(request.id).toEqual('foo'); + expect(request).toHaveProperty('wait_for_completion_timeout'); + expect(request).toHaveProperty('keep_alive', '1m'); + }); + + it('makes a GET request to async search with long keepalive, if session is saved', async () => { + mockGetCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'logstash-*', body: { query: {} } }; + const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger); + + await esSearch + .search({ id: 'foo', params }, { sessionId: '1', isStored: true }, mockDeps) + .toPromise(); + + expect(mockGetCaller).toBeCalled(); + const request = mockGetCaller.mock.calls[0][0]; + expect(request.id).toEqual('foo'); + expect(request).toHaveProperty('wait_for_completion_timeout'); + expect(request).toHaveProperty('keep_alive', '604800000ms'); + }); + + it('makes a GET request to async search with no keepalive, if session is session saved and search is stored', async () => { + mockGetCaller.mockResolvedValueOnce(mockAsyncResponse); + + const params = { index: 'logstash-*', body: { query: {} } }; + const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger); + + await esSearch + .search( + { id: 'foo', params }, + { sessionId: '1', isSearchStored: true, isStored: true }, + mockDeps + ) + .toPromise(); + expect(mockGetCaller).toBeCalled(); const request = mockGetCaller.mock.calls[0][0]; expect(request.id).toEqual('foo'); diff --git a/src/plugins/data/server/search/strategies/ese_search/request_utils.test.ts b/src/plugins/data/server/search/strategies/ese_search/request_utils.test.ts index 878806d9b3559..b908a1df4f0ec 100644 --- a/src/plugins/data/server/search/strategies/ese_search/request_utils.test.ts +++ b/src/plugins/data/server/search/strategies/ese_search/request_utils.test.ts @@ -60,7 +60,7 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Uses `keep_alive` from config if enabled', async () => { + test('Uses `keep_alive` from config if enabled and session is stored', async () => { const mockUiSettingsClient = getMockUiSettingsClient({ [UI_SETTINGS.SEARCH_INCLUDE_FROZEN]: false, }); @@ -69,6 +69,7 @@ describe('request utils', () => { }); const params = await getDefaultAsyncSubmitParams(mockUiSettingsClient, mockConfig, { sessionId: 'foo', + isStored: true, }); expect(params).toHaveProperty('keep_alive', '259200000ms'); }); @@ -132,12 +133,16 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Has no `keep_alive` if `sessionId` is provided', async () => { + test('Has no `keep_alive` if `sessionId` is provided and search already stored', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), enabled: true, }); - const params = getDefaultAsyncGetParams(mockConfig, { sessionId: 'foo' }); + const params = getDefaultAsyncGetParams(mockConfig, { + sessionId: 'foo', + isStored: true, + isSearchStored: true, + }); expect(params).not.toHaveProperty('keep_alive'); }); diff --git a/src/plugins/data/server/search/strategies/sql_search/request_utils.test.ts b/src/plugins/data/server/search/strategies/sql_search/request_utils.test.ts index 9944de7be17be..d37bcfb0655f8 100644 --- a/src/plugins/data/server/search/strategies/sql_search/request_utils.test.ts +++ b/src/plugins/data/server/search/strategies/sql_search/request_utils.test.ts @@ -29,12 +29,13 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Uses `keep_alive` from config if enabled', async () => { + test('Uses `keep_alive` from config if enabled and session is stored', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), }); const params = getDefaultAsyncSubmitParams(mockConfig, { sessionId: 'foo', + isStored: true, }); expect(params).toHaveProperty('keep_alive', '259200000ms'); }); @@ -89,12 +90,16 @@ describe('request utils', () => { expect(params).toHaveProperty('keep_alive', '1m'); }); - test('Has no `keep_alive` if `sessionId` is provided', async () => { + test('Has no `keep_alive` if `sessionId` is provided, search and session are stored', async () => { const mockConfig = getMockSearchSessionsConfig({ defaultExpiration: moment.duration(3, 'd'), enabled: true, }); - const params = getDefaultAsyncGetParams(mockConfig, { sessionId: 'foo' }); + const params = getDefaultAsyncGetParams(mockConfig, { + sessionId: 'foo', + isStored: true, + isSearchStored: true, + }); expect(params).not.toHaveProperty('keep_alive'); }); diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 2d2a10300ee69..50fc29334d22c 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -89,6 +89,7 @@ export interface IScopedSearchClient extends ISearchClient { cancelSession: IScopedSearchSessionsClient['cancel']; deleteSession: IScopedSearchSessionsClient['delete']; extendSession: IScopedSearchSessionsClient['extend']; + getSessionStatus: IScopedSearchSessionsClient['status']; } export interface ISearchStart< 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 08a13c7f43d48..98f5f906113b6 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 @@ -7,7 +7,14 @@ */ import React, { useState, useEffect, useCallback, useRef } from 'react'; -import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiLoadingSpinner, + EuiLink, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import memoizeOne from 'memoize-one'; import { @@ -67,6 +74,7 @@ export interface Props { defaultTypeIsRollup?: boolean; requireTimestampField?: boolean; editData?: DataView; + showManagementLink?: boolean; allowAdHoc: boolean; } @@ -85,11 +93,14 @@ const IndexPatternEditorFlyoutContentComponent = ({ requireTimestampField = false, editData, allowAdHoc, + showManagementLink, }: Props) => { const { - services: { http, dataViews, uiSettings, overlays }, + services: { application, http, dataViews, uiSettings, overlays }, } = useKibana(); + const canSave = dataViews.getCanSaveSync(); + const { form } = useForm({ // Prefill with data if editData exists defaultValue: { @@ -97,7 +108,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ isAdHoc: false, ...(editData ? { - title: editData.title, + title: editData.getIndexPattern(), id: editData.id, name: editData.name, ...(editData.timeFieldName @@ -131,7 +142,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ }; } - if (editData && editData.title !== formData.title) { + if (editData && editData.getIndexPattern() !== formData.title) { editDataViewModal({ dataViewName: formData.name || formData.title, overlays, @@ -313,7 +324,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ useEffect(() => { if (editData) { loadSources(); - reloadMatchedIndices(removeSpaces(editData.title)); + reloadMatchedIndices(removeSpaces(editData.getIndexPattern())); } // We use the below eslint-disable as adding 'loadSources' and 'reloadMatchedIndices' as a dependency creates an infinite loop // eslint-disable-next-line react-hooks/exhaustive-deps @@ -376,6 +387,17 @@ const IndexPatternEditorFlyoutContentComponent = ({

{editData ? editorTitleEditMode : editorTitle}

+ {showManagementLink && editData && editData.id && ( + + {i18n.translate('indexPatternEditor.goToManagementPage', { + defaultMessage: 'View on data view management page', + })} + + )}
{indexPatternTypeSelect} @@ -425,7 +447,9 @@ const IndexPatternEditorFlyoutContentComponent = ({ }} submitDisabled={form.isSubmitted && !form.isValid} isEdit={!!editData} + isPersisted={Boolean(editData && editData.isPersisted())} allowAdHoc={allowAdHoc} + canSave={canSave} /> diff --git a/src/plugins/data_view_editor/public/components/data_view_flyout_content_container.tsx b/src/plugins/data_view_editor/public/components/data_view_flyout_content_container.tsx index 35801d0a9dcf3..3aaf56e6daaad 100644 --- a/src/plugins/data_view_editor/public/components/data_view_flyout_content_container.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_flyout_content_container.tsx @@ -20,6 +20,7 @@ const IndexPatternFlyoutContentContainer = ({ requireTimestampField = false, editData, allowAdHocDataView, + showManagementLink, }: DataViewEditorProps) => { const { services: { dataViews, notifications }, @@ -30,7 +31,7 @@ const IndexPatternFlyoutContentContainer = ({ let saveResponse; if (editData) { const { name = '', timeFieldName, title = '' } = dataViewSpec; - editData.title = title; + editData.setIndexPattern(title); editData.name = name; editData.timeFieldName = timeFieldName; saveResponse = editData.isPersisted() @@ -68,6 +69,7 @@ const IndexPatternFlyoutContentContainer = ({ defaultTypeIsRollup={defaultTypeIsRollup} requireTimestampField={requireTimestampField} editData={editData} + showManagementLink={showManagementLink} allowAdHoc={allowAdHocDataView || false} /> ); 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 01d954fb29bdb..024885e91d548 100644 --- a/src/plugins/data_view_editor/public/components/footer/footer.tsx +++ b/src/plugins/data_view_editor/public/components/footer/footer.tsx @@ -22,7 +22,9 @@ interface FooterProps { onSubmit: (isAdHoc?: boolean) => void; submitDisabled: boolean; isEdit: boolean; + isPersisted: boolean; allowAdHoc: boolean; + canSave: boolean; } const closeButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutCloseButtonLabel', { @@ -37,11 +39,26 @@ const editButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutEditButt defaultMessage: 'Save', }); +const editUnpersistedButtonLabel = i18n.translate( + 'indexPatternEditor.editor.flyoutEditUnpersistedButtonLabel', + { + defaultMessage: 'Continue to use without saving', + } +); + const exploreButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutExploreButtonLabel', { defaultMessage: 'Use without saving', }); -export const Footer = ({ onCancel, onSubmit, submitDisabled, isEdit, allowAdHoc }: FooterProps) => { +export const Footer = ({ + onCancel, + onSubmit, + submitDisabled, + isEdit, + allowAdHoc, + isPersisted, + canSave, +}: FooterProps) => { const submitPersisted = () => { onSubmit(false); }; @@ -81,17 +98,23 @@ export const Footer = ({ onCancel, onSubmit, submitDisabled, isEdit, allowAdHoc )} - - - {isEdit ? editButtonLabel : saveButtonLabel} - - + {(canSave || (isEdit && !isPersisted)) && ( + + + {isEdit + ? isPersisted + ? editButtonLabel + : editUnpersistedButtonLabel + : saveButtonLabel} + + + )} diff --git a/src/plugins/data_view_editor/public/open_editor.tsx b/src/plugins/data_view_editor/public/open_editor.tsx index b422de8e40ce2..29ea140daef4b 100644 --- a/src/plugins/data_view_editor/public/open_editor.tsx +++ b/src/plugins/data_view_editor/public/open_editor.tsx @@ -35,6 +35,7 @@ export const getEditorOpener = notifications, application, dataViews, + overlays, searchClient, }); @@ -46,6 +47,7 @@ export const getEditorOpener = defaultTypeIsRollup = false, requireTimestampField = false, allowAdHocDataView = false, + editData, }: DataViewEditorProps): CloseEditor => { const closeEditor = () => { if (overlayRef) { @@ -72,9 +74,11 @@ export const getEditorOpener = closeEditor(); onCancel(); }} + editData={editData} defaultTypeIsRollup={defaultTypeIsRollup} requireTimestampField={requireTimestampField} allowAdHocDataView={allowAdHocDataView} + showManagementLink={Boolean(editData && editData.isPersisted())} /> , diff --git a/src/plugins/data_view_editor/public/plugin.tsx b/src/plugins/data_view_editor/public/plugin.tsx index b0186b838987b..232958fbb2c21 100644 --- a/src/plugins/data_view_editor/public/plugin.tsx +++ b/src/plugins/data_view_editor/public/plugin.tsx @@ -21,7 +21,7 @@ export class DataViewEditorPlugin } public start(core: CoreStart, plugins: StartPlugins) { - const { application, uiSettings, docLinks, http, notifications } = core; + const { application, uiSettings, docLinks, http, notifications, overlays } = core; const { data, dataViews } = plugins; return { @@ -48,6 +48,7 @@ export class DataViewEditorPlugin http, notifications, application, + overlays, dataViews, searchClient: data.search.search, }} diff --git a/src/plugins/data_view_editor/public/types.ts b/src/plugins/data_view_editor/public/types.ts index 4500e522119d5..b5e506db4d3e9 100644 --- a/src/plugins/data_view_editor/public/types.ts +++ b/src/plugins/data_view_editor/public/types.ts @@ -13,6 +13,7 @@ import { NotificationsStart, DocLinksStart, HttpSetup, + OverlayStart, } from '@kbn/core/public'; import { EuiComboBoxOptionOption } from '@elastic/eui'; @@ -31,6 +32,7 @@ export interface DataViewEditorContext { http: HttpSetup; notifications: NotificationsStart; application: ApplicationStart; + overlays: OverlayStart; dataViews: DataViewsPublicPluginStart; searchClient: DataPublicPluginStart['search']['search']; } @@ -62,6 +64,11 @@ export interface DataViewEditorProps { * if set to true user is presented with an option to create ad-hoc dataview without a saved object. */ allowAdHocDataView?: boolean; + + /** + * if set to true a link to the management page is shown + */ + showManagementLink?: boolean; } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index 4f7cc3e57a975..b28d8fdb7d2dd 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -118,6 +118,7 @@ export const WithFieldEditorDependencies = const dependencies: Context = { dataView: { title: indexPatternNameForTest, + getIndexPattern: () => indexPatternNameForTest, name: indexPatternNameForTest, getName: () => indexPatternNameForTest, fields: { getAll: spyIndexPatternGetAllFields }, diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx index 34740187d77d9..8366bcc71cb7f 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -168,7 +168,9 @@ export const FieldEditorFlyoutContentContainer = ({ if (!editedField) { throw new Error( - `Unable to find field named '${updatedField.name}' on index pattern '${dataView.title}'` + `Unable to find field named '${ + updatedField.name + }' on index pattern '${dataView.getIndexPattern()}'` ); } diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index 6d6c38f8dfc61..a971d502643ce 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -217,7 +217,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { const [response, searchError] = await search .search({ params: { - index: dataView.title, + index: dataView.getIndexPattern(), body: { size: limit, }, @@ -269,7 +269,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { const [response, searchError] = await search .search({ params: { - index: dataView.title, + index: dataView.getIndexPattern(), body: { size: 1, query: { diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_header.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_header.tsx index 6d327a7e0cdd8..62d61dbdd4c79 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_header.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_header.tsx @@ -49,7 +49,7 @@ export const FieldPreviewHeader = () => { {i18n.translate('indexPatternFieldEditor.fieldPreview.subTitle', { defaultMessage: 'From: {from}', values: { - from: from.value === 'cluster' ? dataView.title : i18nTexts.customData, + from: from.value === 'cluster' ? dataView.getIndexPattern() : i18nTexts.customData, }, })} diff --git a/src/plugins/data_views/README.mdx b/src/plugins/data_views/README.mdx index 6d2ef4c97bb33..930ee4e76b6a9 100644 --- a/src/plugins/data_views/README.mdx +++ b/src/plugins/data_views/README.mdx @@ -1,17 +1,16 @@ --- -id: kibDataPlugin -slug: /kibana-dev-docs/services/data-plugin -title: Data services -image: https://source.unsplash.com/400x175/?Search -description: The data plugin contains services for searching, querying and filtering. -date: 2020-12-02 -tags: ['kibana', 'dev', 'contributor', 'api docs'] +id: kibDataViewsPlugin +slug: /kibana-dev-docs/services/data-views-plugin +title: Data Views services +description: The data views plugin contains services for dealing with Kibana's Data Views. +date: 2022-10-05 +tags: ['kibana', 'dev', 'contributor', 'api docs', 'data views', 'has data'] --- # Data Views The data views API provides a consistent method of structuring and formatting documents -and field lists across the various Kibana apps. Its typically used in conjunction with +and field lists across the various Kibana apps. It's typically used in conjunction with for composing queries. *Note: Kibana index patterns are currently being renamed to data views. There will be some naming inconsistencies until the transition is complete.* diff --git a/src/plugins/data_views/common/data_views/data_view.test.ts b/src/plugins/data_views/common/data_views/data_view.test.ts index 25966a1d9fc85..11176475f7c95 100644 --- a/src/plugins/data_views/common/data_views/data_view.test.ts +++ b/src/plugins/data_views/common/data_views/data_view.test.ts @@ -406,6 +406,19 @@ describe('IndexPattern', () => { }); }); + describe('getIndexPattern', () => { + test('should return the index pattern, labeled title on the data view spec', () => { + expect(indexPattern.getIndexPattern()).toBe( + stubbedSavedObjectIndexPattern().attributes.title + ); + }); + + test('setIndexPattern', () => { + indexPattern.setIndexPattern('test'); + expect(indexPattern.getIndexPattern()).toBe('test'); + }); + }); + describe('getFormatterForField', () => { test('should return the default one for empty objects', () => { indexPattern.setFieldFormat('scriptedFieldWithEmptyFormatter', {}); @@ -452,7 +465,7 @@ describe('IndexPattern', () => { metaFields: [], }); expect(restoredPattern.id).toEqual(indexPattern.id); - expect(restoredPattern.title).toEqual(indexPattern.title); + expect(restoredPattern.getIndexPattern()).toEqual(indexPattern.getIndexPattern()); expect(restoredPattern.timeFieldName).toEqual(indexPattern.timeFieldName); expect(restoredPattern.fields.length).toEqual(indexPattern.fields.length); }); diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index f7f6451ca0453..428a5c4321c6d 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -75,6 +75,7 @@ export class DataView implements DataViewBase { public id?: string; /** * Title of data view + * @deprecated use getIndexPattern instead */ public title: string = ''; /** @@ -193,6 +194,22 @@ export class DataView implements DataViewBase { */ getName = () => (this.name ? this.name : this.title); + /** + * Get index pattern + * @returns index pattern string + */ + + getIndexPattern = () => this.title; + + /** + * Set index pattern + * @param string index pattern string + */ + + setIndexPattern = (indexPattern: string) => { + this.title = indexPattern; + }; + /** * Get last saved saved object fields */ @@ -298,7 +315,7 @@ export class DataView implements DataViewBase { const spec: DataViewSpec = { id: this.id, version: this.version, - title: this.title, + title: this.getIndexPattern(), timeFieldName: this.timeFieldName, sourceFilters: [...(this.sourceFilters || [])], fields, @@ -412,7 +429,7 @@ export class DataView implements DataViewBase { return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, - title: this.title, + title: this.getIndexPattern(), timeFieldName: this.timeFieldName, sourceFilters: this.sourceFilters ? JSON.stringify(this.sourceFilters) : undefined, fields: JSON.stringify(this.fields?.filter((field) => field.scripted) ?? []), diff --git a/src/plugins/data_views/common/data_views/data_views.test.ts b/src/plugins/data_views/common/data_views/data_views.test.ts index a096cbe07cd53..64e9ee483ef3b 100644 --- a/src/plugins/data_views/common/data_views/data_views.test.ts +++ b/src/plugins/data_views/common/data_views/data_views.test.ts @@ -224,11 +224,11 @@ describe('IndexPatterns', () => { // This will conflict because samePattern did a save (from refreshFields) // but the resave should work fine - pattern.title = 'foo2'; + pattern.setIndexPattern('foo2'); await indexPatterns.updateSavedObject(pattern); // This should not be able to recover - samePattern.title = 'foo3'; + samePattern.setIndexPattern('foo3'); let result; try { @@ -241,18 +241,18 @@ describe('IndexPatterns', () => { }); test('create', async () => { - const title = 'kibana-*'; + const indexPattern = 'kibana-*'; indexPatterns.refreshFields = jest.fn(); - const indexPattern = await indexPatterns.create({ title }, true); - expect(indexPattern).toBeInstanceOf(DataView); - expect(indexPattern.title).toBe(title); + const dataView = await indexPatterns.create({ title: indexPattern }, true); + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.getIndexPattern()).toBe(indexPattern); expect(indexPatterns.refreshFields).not.toBeCalled(); - await indexPatterns.create({ title }); + await indexPatterns.create({ title: indexPattern }); expect(indexPatterns.refreshFields).toBeCalled(); - expect(indexPattern.id).toBeDefined(); - expect(indexPattern.isPersisted()).toBe(false); + expect(dataView.id).toBeDefined(); + expect(dataView.isPersisted()).toBe(false); }); test('createSavedObject', async () => { @@ -267,14 +267,14 @@ describe('IndexPatterns', () => { version, attributes: { ...savedObject.attributes, - title: dataView.title, + title: dataView.getIndexPattern(), }, }); const indexPattern = await indexPatterns.createSavedObject(dataView); expect(indexPattern).toBeInstanceOf(DataView); expect(indexPattern.id).toBe(dataView.id); - expect(indexPattern.title).toBe(title); + expect(indexPattern.getIndexPattern()).toBe(title); expect(indexPattern.isPersisted()).toBe(true); }); diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 892df9b312e86..fa0db477fcd75 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -512,7 +512,7 @@ export class DataViewsService { type: dataView.type, rollupIndex: dataView?.typeMeta?.params?.rollup_index, allowNoIndex: dataView.allowNoIndex, - pattern: dataView.title as string, + pattern: dataView.getIndexPattern(), metaFields, }); }; @@ -556,7 +556,7 @@ export class DataViewsService { if (err instanceof DataViewMissingIndices) { this.onNotification( { title: err.message, color: 'danger', iconType: 'alert' }, - `refreshFields:${indexPattern.title}` + `refreshFields:${indexPattern.getIndexPattern()}` ); } @@ -565,10 +565,10 @@ export class DataViewsService { { title: i18n.translate('dataViews.fetchFieldErrorTitle', { defaultMessage: 'Error fetching fields for data view {title} (ID: {id})', - values: { id: indexPattern.id, title: indexPattern.title }, + values: { id: indexPattern.id, title: indexPattern.getIndexPattern() }, }), }, - indexPattern.title + indexPattern.getIndexPattern() ); } }; @@ -1000,7 +1000,7 @@ export class DataViewsService { this.onNotification( { title, color: 'danger' }, - `updateSavedObject:${indexPattern.title}` + `updateSavedObject:${indexPattern.getIndexPattern()}` ); throw err; } diff --git a/src/plugins/discover/public/application/main/components/chart/discover_chart.tsx b/src/plugins/discover/public/application/main/components/chart/discover_chart.tsx index 4912022e08fdd..e7cc01fb00eaa 100644 --- a/src/plugins/discover/public/application/main/components/chart/discover_chart.tsx +++ b/src/plugins/discover/public/application/main/components/chart/discover_chart.tsx @@ -18,16 +18,18 @@ import { import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { + getVisualizeInformation, + triggerVisualizeActions, +} from '@kbn/unified-field-list-plugin/public'; import { HitsCounter } from '../hits_counter'; import { GetStateReturn } from '../../services/discover_state'; import { DiscoverHistogram } from './histogram'; import { DataCharts$, DataTotalHits$ } from '../../hooks/use_saved_search'; import { useChartPanels } from './use_chart_panels'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { - getVisualizeInformation, - triggerVisualizeActions, -} from '../sidebar/lib/visualize_trigger_utils'; +import { getUiActions } from '../../../../kibana_services'; +import { PLUGIN_ID } from '../../../../../common'; const DiscoverHistogramMemoized = memo(DiscoverHistogram); export const CHART_HIDDEN_KEY = 'discover:chartHidden'; @@ -72,7 +74,13 @@ export function DiscoverChart({ useEffect(() => { if (!timeField) return; - getVisualizeInformation(timeField, dataView, savedSearch.columns || []).then((info) => { + getVisualizeInformation( + getUiActions(), + timeField, + dataView, + savedSearch.columns || [], + [] + ).then((info) => { setCanVisualize(Boolean(info)); }); }, [dataView, savedSearch.columns, timeField]); @@ -81,7 +89,13 @@ export function DiscoverChart({ if (!timeField) { return; } - triggerVisualizeActions(timeField, savedSearch.columns || [], dataView); + triggerVisualizeActions( + getUiActions(), + timeField, + savedSearch.columns || [], + PLUGIN_ID, + dataView + ); }, [dataView, savedSearch.columns, timeField]); const onShowChartOptions = useCallback(() => { diff --git a/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_visualize.stories.tsx b/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_visualize.stories.tsx deleted file mode 100644 index 397ada8da109d..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_visualize.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { DiscoverFieldVisualizeInner } from '../discover_field_visualize_inner'; -import { numericField as field } from './fields'; - -const visualizeInfo = { - href: 'http://localhost:9001/', - field, -}; - -const handleVisualizeLinkClick = () => { - alert('Clicked'); -}; - -storiesOf('components/sidebar/DiscoverFieldVisualizeInner', module).add('default', () => ( - -)); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.scss b/src/plugins/discover/public/application/main/components/sidebar/discover_field.scss index 40bc58cef7023..3e7a7a4d4ac1e 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.scss +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.scss @@ -1,8 +1,3 @@ -.dscSidebarItem__fieldPopoverPanel { - min-width: $euiSizeXXL * 6.5; - max-width: $euiSizeXXL * 7.5; -} - .dscSidebarItem--multi { .kbnFieldButton__button { padding-left: 0; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx index ffb2ec7585ed5..caa8539ab6b32 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx @@ -8,7 +8,6 @@ import { act } from 'react-dom/test-utils'; import { EuiPopover, EuiProgress, EuiButtonIcon } from '@elastic/eui'; -import { ReactWrapper } from 'enzyme'; import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; import { mountWithIntl } from '@kbn/test-jest-helpers'; @@ -85,6 +84,7 @@ async function getComponent({ getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: 2 })), ...(onAddFilterExists && { onAddFilter: jest.fn() }), onAddField: jest.fn(), + onEditField: jest.fn(), onRemoveField: jest.fn(), showFieldStats, selected, @@ -140,6 +140,9 @@ async function getComponent({ ); + // wait for lazy modules + await new Promise((resolve) => setTimeout(resolve, 0)); + await comp.update(); return { comp, props }; } @@ -233,29 +236,100 @@ describe('discover sidebar field', function () { aggregatable: true, searchable: true, }); - let comp: ReactWrapper; + + const { comp } = await getComponent({ showFieldStats: true, field, onAddFilterExists: true }); await act(async () => { - const result = await getComponent({ showFieldStats: true, field, onAddFilterExists: true }); - comp = result.comp; + const fieldItem = findTestSubject(comp, 'field-machine.os.raw-showDetails'); + await fieldItem.simulate('click'); await comp.update(); }); + await comp.update(); + + expect(comp.find(EuiPopover).prop('isOpen')).toBe(true); + expect(findTestSubject(comp, 'dscFieldStats-title').text()).toBe('Top values'); + expect(findTestSubject(comp, 'dscFieldStats-topValues-bucket')).toHaveLength(2); + expect( + findTestSubject(comp, 'dscFieldStats-topValues-formattedFieldValue').first().text() + ).toBe('osx'); + expect(comp.find(EuiProgress)).toHaveLength(2); + expect(findTestSubject(comp, 'dscFieldStats-topValues').find(EuiButtonIcon)).toHaveLength(4); + }); + it('should include popover actions', async function () { + const field = new DataViewField({ + name: 'extension.keyword', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + searchable: true, + }); + + const { comp, props } = await getComponent({ field, onAddFilterExists: true }); + await act(async () => { - const fieldItem = findTestSubject(comp, 'field-machine.os.raw-showDetails'); + const fieldItem = findTestSubject(comp, 'field-extension.keyword-showDetails'); await fieldItem.simulate('click'); await comp.update(); }); - await comp!.update(); + await comp.update(); - expect(comp!.find(EuiPopover).prop('isOpen')).toBe(true); - expect(findTestSubject(comp!, 'dscFieldStats-title').text()).toBe('Top values'); - expect(findTestSubject(comp!, 'dscFieldStats-topValues-bucket')).toHaveLength(2); + expect(comp.find(EuiPopover).prop('isOpen')).toBe(true); expect( - findTestSubject(comp!, 'dscFieldStats-topValues-formattedFieldValue').first().text() - ).toBe('osx'); - expect(comp!.find(EuiProgress)).toHaveLength(2); - expect(findTestSubject(comp!, 'dscFieldStats-topValues').find(EuiButtonIcon)).toHaveLength(4); + comp.find('[data-test-subj="fieldPopoverHeader_addField-extension.keyword"]').exists() + ).toBeTruthy(); + expect( + comp + .find('[data-test-subj="discoverFieldListPanelAddExistFilter-extension.keyword"]') + .exists() + ).toBeTruthy(); + expect( + comp.find('[data-test-subj="discoverFieldListPanelEdit-extension.keyword"]').exists() + ).toBeTruthy(); + expect( + comp.find('[data-test-subj="discoverFieldListPanelDelete-extension.keyword"]').exists() + ).toBeFalsy(); + + await act(async () => { + const fieldItem = findTestSubject(comp, 'fieldPopoverHeader_addField-extension.keyword'); + await fieldItem.simulate('click'); + await comp.update(); + }); + + expect(props.onAddField).toHaveBeenCalledWith('extension.keyword'); + + await comp.update(); + + expect(comp.find(EuiPopover).prop('isOpen')).toBe(false); + }); + + it('should not include + action for selected fields', async function () { + const field = new DataViewField({ + name: 'extension.keyword', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + searchable: true, + }); + + const { comp } = await getComponent({ + field, + onAddFilterExists: true, + selected: true, + }); + + await act(async () => { + const fieldItem = findTestSubject(comp, 'field-extension.keyword-showDetails'); + await fieldItem.simulate('click'); + await comp.update(); + }); + + await comp.update(); + + expect(comp.find(EuiPopover).prop('isOpen')).toBe(true); + expect( + comp.find('[data-test-subj="fieldPopoverHeader_addField-extension.keyword"]').exists() + ).toBeFalsy(); }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index 10138dec8b4cb..591475d949c63 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -9,32 +9,27 @@ import './discover_field.scss'; import React, { useState, useCallback, memo, useMemo } from 'react'; -import { - EuiPopover, - EuiPopoverTitle, - EuiButtonIcon, - EuiToolTip, - EuiTitle, - EuiIcon, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, -} from '@elastic/eui'; +import { EuiButtonIcon, EuiToolTip, EuiTitle, EuiIcon, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { UiCounterMetricType } from '@kbn/analytics'; import classNames from 'classnames'; import { FieldButton, FieldIcon } from '@kbn/react-field'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; -import { FieldStats } from '@kbn/unified-field-list-plugin/public'; -import { getFieldCapabilities } from '../../../../utils/get_field_capabilities'; +import { + FieldStats, + FieldPopover, + FieldPopoverHeader, + FieldPopoverHeaderProps, + FieldPopoverVisualize, +} from '@kbn/unified-field-list-plugin/public'; import { getTypeForFieldIcon } from '../../../../utils/get_type_for_field_icon'; import { DiscoverFieldDetails } from './discover_field_details'; import { FieldDetails } from './types'; import { getFieldTypeName } from '../../../../utils/get_field_type_name'; -import { DiscoverFieldVisualize } from './discover_field_visualize'; import type { AppState } from '../../services/discover_state'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { SHOW_LEGACY_FIELD_TOP_VALUES } from '../../../../../common'; +import { SHOW_LEGACY_FIELD_TOP_VALUES, PLUGIN_ID } from '../../../../../common'; +import { getUiActions } from '../../../../kibana_services'; function wrapOnDot(str?: string) { // u200B is a non-width white-space character, which allows @@ -93,7 +88,7 @@ interface ActionButtonProps { field: DataViewField; isSelected?: boolean; alwaysShow: boolean; - toggleDisplay: (field: DataViewField) => void; + toggleDisplay: (field: DataViewField, isSelected?: boolean) => void; } const ActionButton: React.FC = memo( @@ -121,7 +116,7 @@ const ActionButton: React.FC = memo( } ev.preventDefault(); ev.stopPropagation(); - toggleDisplay(field); + toggleDisplay(field, isSelected); }} data-test-subj={`fieldToggle-${field.name}`} aria-label={i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', { @@ -149,7 +144,7 @@ const ActionButton: React.FC = memo( } ev.preventDefault(); ev.stopPropagation(); - toggleDisplay(field); + toggleDisplay(field, isSelected); }} data-test-subj={`fieldToggle-${field.name}`} aria-label={i18n.translate( @@ -311,23 +306,48 @@ function DiscoverFieldComponent({ [setOpen, onAddFilter] ); - const toggleDisplay = useCallback( - (f: DataViewField) => { - if (selected) { + const togglePopover = useCallback(() => { + setOpen((value) => !value); + }, [setOpen]); + + const closePopover = useCallback(() => { + setOpen(false); + }, [setOpen]); + + const toggleDisplay: ActionButtonProps['toggleDisplay'] = useCallback( + (f, isCurrentlySelected) => { + closePopover(); + if (isCurrentlySelected) { onRemoveField(f.name); } else { onAddField(f.name); } }, - [onAddField, onRemoveField, selected] + [onAddField, onRemoveField, closePopover] ); - const togglePopover = useCallback(() => { - setOpen(!infoIsOpen); - }, [infoIsOpen]); - const rawMultiFields = useMemo(() => multiFields?.map((f) => f.field), [multiFields]); + const customPopoverHeaderProps: Partial = useMemo( + () => ({ + buttonAddFieldToWorkspaceProps: { + 'aria-label': i18n.translate('discover.fieldChooser.discoverField.addFieldTooltip', { + defaultMessage: 'Add field as column', + }), + }, + buttonAddFilterProps: { + 'data-test-subj': `discoverFieldListPanelAddExistFilter-${field.name}`, + }, + buttonEditFieldProps: { + 'data-test-subj': `discoverFieldListPanelEdit-${field.name}`, + }, + buttonDeleteFieldProps: { + 'data-test-subj': `discoverFieldListPanelDelete-${field.name}`, + }, + }), + [field.name] + ); + if (field.type === '_source') { return ( - - -
{field.displayName}
-
- {onAddFilter && !dataView.metaFields.includes(field.name) && !field.scripted && ( - - - { - setOpen(false); - onAddFilter('_exists_', field.name, '+'); - }} - iconType="filter" - data-test-subj={`discoverFieldListPanelAddExistFilter-${field.name}`} - aria-label={addExistFilterTooltip} - /> - - - )} - {canEditField && ( - - - { - if (onEditField) { - togglePopover(); - onEditField(field.name); - } - }} - iconType="pencil" - data-test-subj={`discoverFieldListPanelEdit-${field.name}`} - aria-label={editFieldTooltip} - /> - - - )} - {canDeleteField && ( - - - { - onDeleteField?.(field.name); - }} - iconType="trash" - data-test-subj={`discoverFieldListPanelDelete-${field.name}`} - color="danger" - aria-label={deleteFieldTooltip} - /> - - - )} -
- - ); - const button = ( } /> ); + if (!isDocumentRecord) { return button; } @@ -512,29 +454,38 @@ function DiscoverFieldComponent({ )} - ); }; + return ( - setOpen(false)} + button={button} + closePopover={closePopover} data-test-subj="discoverFieldListPanelPopover" - anchorPosition="rightUp" - panelClassName="dscSidebarItem__fieldPopoverPanel" - > - {popoverTitle} - {infoIsOpen && renderPopover()} - + renderHeader={() => ( + + )} + renderContent={renderPopover} + /> ); } diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx index 666b75a689499..3952dd723fc0e 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx @@ -85,7 +85,7 @@ describe('discover sidebar', function () { let props: DiscoverSidebarProps; let comp: ReactWrapper; - beforeAll(() => { + beforeAll(async () => { props = getCompProps(); mockDiscoverServices.data.dataViews.getIdsWithTitle = jest .fn() @@ -95,11 +95,14 @@ describe('discover sidebar', function () { return { ...dataView, isPersisted: () => true }; }); - comp = mountWithIntl( + comp = await mountWithIntl( ); + // wait for lazy modules + await new Promise((resolve) => setTimeout(resolve, 0)); + await comp.update(); }); it('should have Selected Fields and Available Fields with Popular Fields sections', function () { @@ -126,8 +129,10 @@ describe('discover sidebar', function () { expect(props.editField).toHaveBeenCalledWith(); }); - it('should render "Edit field" button', () => { + it('should render "Edit field" button', async () => { findTestSubject(comp, 'field-bytes').simulate('click'); + await new Promise((resolve) => setTimeout(resolve, 0)); + await comp.update(); const editFieldButton = findTestSubject(comp, 'discoverFieldListPanelEdit-bytes'); expect(editFieldButton.length).toBe(1); editFieldButton.simulate('click'); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index 355512da5c52c..436fdc36f4d32 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -28,10 +28,11 @@ import { isEqual } from 'lodash'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataViewPicker } from '@kbn/unified-search-plugin/public'; import { DataViewField, getFieldSubtypeMulti } from '@kbn/data-views-plugin/public'; +import { triggerVisualizeActionsTextBasedLanguages } from '@kbn/unified-field-list-plugin/public'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DiscoverField } from './discover_field'; import { DiscoverFieldSearch } from './discover_field_search'; -import { FIELDS_LIMIT_SETTING } from '../../../../../common'; +import { FIELDS_LIMIT_SETTING, PLUGIN_ID } from '../../../../../common'; import { groupFields } from './lib/group_fields'; import { getDetails } from './lib/get_details'; import { FieldFilterState, getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; @@ -40,7 +41,7 @@ import { DiscoverSidebarResponsiveProps } from './discover_sidebar_responsive'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { DISCOVER_TOUR_STEP_ANCHOR_IDS } from '../../../../components/discover_tour'; import type { DataTableRecord } from '../../../../types'; -import { triggerVisualizeActionsTextBasedLanguages } from './lib/visualize_trigger_utils'; +import { getUiActions } from '../../../../kibana_services'; /** * Default number of available fields displayed and added on scroll @@ -187,7 +188,8 @@ export function DiscoverSidebarComponent({ // In this case the fieldsPerPage needs to be adapted const fieldsRenderedHeight = availableFieldsContainer.current.clientHeight; const avgHeightPerItem = Math.round(fieldsRenderedHeight / fieldsToRender); - const newFieldsPerPage = Math.round(clientHeight / avgHeightPerItem) + 10; + const newFieldsPerPage = + (avgHeightPerItem > 0 ? Math.round(clientHeight / avgHeightPerItem) : 0) + 10; if (newFieldsPerPage >= FIELDS_PER_PAGE && newFieldsPerPage !== fieldsPerPage) { setFieldsPerPage(newFieldsPerPage); setFieldsToRender(newFieldsPerPage); @@ -314,7 +316,13 @@ export function DiscoverSidebarComponent({ const visualizeAggregateQuery = useCallback(() => { const aggregateQuery = state.query && isOfAggregateQueryType(state.query) ? state.query : undefined; - triggerVisualizeActionsTextBasedLanguages(columns, selectedDataView, aggregateQuery); + triggerVisualizeActionsTextBasedLanguages( + getUiActions(), + columns, + PLUGIN_ID, + selectedDataView, + aggregateQuery + ); }, [columns, selectedDataView, state.query]); if (!selectedDataView) { diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index 3d09302544755..944355c9a9db3 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -190,7 +190,9 @@ describe('discover responsive sidebar', function () { ); - comp.update(); + // wait for lazy modules + await new Promise((resolve) => setTimeout(resolve, 0)); + await comp.update(); }); }); 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/event_annotation/common/event_annotation_group/index.ts b/src/plugins/event_annotation/common/event_annotation_group/index.ts index 7cc71e980eefd..e088d6878de7c 100644 --- a/src/plugins/event_annotation/common/event_annotation_group/index.ts +++ b/src/plugins/event_annotation/common/event_annotation_group/index.ts @@ -60,7 +60,7 @@ export function eventAnnotationGroup(): ExpressionFunctionDefinition< fn: (input, args) => { return { type: 'event_annotation_group', - annotations: args.annotations, + annotations: args.annotations.filter((annotation) => !annotation.isHidden), dataView: args.dataView, }; }, diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 5abed8ba618ba..c5df7045fc4dc 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -19,44 +19,6 @@ describe('Event Annotation Service', () => { expect(eventAnnotationService.toExpression([])).toEqual([]); }); - it('should skip hidden annotations', () => { - expect( - eventAnnotationService.toExpression([ - { - id: 'myEvent', - type: 'manual', - key: { - type: 'point_in_time', - timestamp: '2022', - }, - label: 'Hello', - isHidden: true, - }, - { - id: 'myRangeEvent', - type: 'manual', - key: { - type: 'range', - timestamp: '2021', - endTimestamp: '2022', - }, - label: 'Hello Range', - isHidden: true, - }, - { - id: 'myEvent', - type: 'query', - timeField: '@timestamp', - key: { - type: 'point_in_time', - }, - label: 'Hello Range', - isHidden: true, - filter: { type: 'kibana_query', query: '', language: 'kuery' }, - }, - ]) - ).toEqual([]); - }); it('should process manual point annotations', () => { expect( eventAnnotationService.toExpression([ @@ -79,6 +41,7 @@ describe('Event Annotation Service', () => { function: 'manual_point_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], time: ['2022'], label: ['Hello'], color: ['#f04e98'], @@ -115,6 +78,7 @@ describe('Event Annotation Service', () => { function: 'manual_range_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], time: ['2021'], endTime: ['2022'], label: ['Hello'], @@ -149,6 +113,7 @@ describe('Event Annotation Service', () => { function: 'query_point_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], timeField: ['@timestamp'], label: ['Hello'], color: ['#f04e98'], @@ -221,6 +186,7 @@ describe('Event Annotation Service', () => { function: 'manual_point_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], time: ['2022'], label: ['Hello'], color: ['#f04e98'], @@ -240,6 +206,7 @@ describe('Event Annotation Service', () => { function: 'manual_range_event_annotation', arguments: { id: ['myRangeEvent'], + isHidden: [false], time: ['2021'], endTime: ['2022'], label: ['Hello Range'], @@ -257,6 +224,7 @@ describe('Event Annotation Service', () => { function: 'query_point_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], timeField: ['@timestamp'], label: ['Hello'], color: ['#f04e98'], @@ -320,6 +288,7 @@ describe('Event Annotation Service', () => { function: 'query_point_event_annotation', arguments: { id: ['myEvent'], + isHidden: [false], timeField: ['@timestamp'], label: ['Hello'], color: ['#f04e98'], diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index bb8c8d0d62ebd..3722c4d3557bb 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -25,9 +25,8 @@ export function hasIcon(icon: string | undefined): icon is string { export function getEventAnnotationService(): EventAnnotationServiceType { const annotationsToExpression = (annotations: EventAnnotationConfig[]) => { - const visibleAnnotations = annotations.filter(({ isHidden }) => !isHidden); const [queryBasedAnnotations, manualBasedAnnotations] = partition( - visibleAnnotations, + annotations, isQueryAnnotationConfig ); @@ -50,6 +49,7 @@ export function getEventAnnotationService(): EventAnnotationServiceType { label: [label || defaultAnnotationLabel], color: [color || defaultAnnotationRangeColor], outside: [Boolean(outside)], + isHidden: [Boolean(annotation.isHidden)], }, }, ], @@ -71,6 +71,7 @@ export function getEventAnnotationService(): EventAnnotationServiceType { lineStyle: [lineStyle || 'solid'], icon: hasIcon(icon) ? [icon] : ['triangle'], textVisibility: [textVisibility || false], + isHidden: [Boolean(annotation.isHidden)], }, }, ], @@ -112,6 +113,7 @@ export function getEventAnnotationService(): EventAnnotationServiceType { filter: filter ? [queryToAst(filter)] : [], extraFields: extraFields || [], ignoreGlobalFilters: [Boolean(ignoreGlobalFilters)], + isHidden: [Boolean(annotation.isHidden)], }, }, ], 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/components/guide_panel.test.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx index 3506c15fcba35..5bd846aebbdef 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx @@ -228,5 +228,54 @@ describe('Guided setup', () => { expect(find('activeStepButtonLabel').text()).toEqual('Continue'); }); }); + + describe('Quit guide modal', () => { + beforeEach(async () => { + const { component, find, exists } = testBed; + + await act(async () => { + // Enable the "search" guide + await apiService.updateGuideState(mockActiveSearchGuideState, true); + }); + + component.update(); + + await act(async () => { + find('quitGuideButton').simulate('click'); + }); + + component.update(); + + expect(exists('quitGuideModal')).toBe(true); + }); + + test('quit a guide', async () => { + const { component, find, exists } = testBed; + + await act(async () => { + find('confirmModalConfirmButton').simulate('click'); + }); + + component.update(); + + expect(exists('quitGuideModal')).toBe(false); + // For now, the guide button is disabled once a user quits a guide + // This behavior will change once https://github.com/elastic/kibana/issues/141129 is implemented + expect(exists('disabledGuideButton')).toBe(true); + }); + + test('cancels out of the quit guide confirmation modal', async () => { + const { component, find, exists } = testBed; + + await act(async () => { + find('confirmModalCancelButton').simulate('click'); + }); + + component.update(); + + expect(exists('quitGuideModal')).toBe(false); + expect(exists('guideButton')).toBe(true); + }); + }); }); }); diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.tsx index bf57d502918d2..7c122492d84a1 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.tsx @@ -29,6 +29,7 @@ import { import { ApplicationStart } from '@kbn/core-application-browser'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; + import { guidesConfig } from '../constants/guides_config'; import type { GuideState, GuideStepIds } from '../../common/types'; import type { GuideConfig, StepConfig } from '../types'; @@ -36,6 +37,7 @@ import type { GuideConfig, StepConfig } from '../types'; import type { ApiService } from '../services/api'; import { GuideStep } from './guide_panel_step'; +import { QuitGuideModal } from './quit_guide_modal'; import { getGuidePanelStyles } from './guide_panel.styles'; interface GuidePanelProps { @@ -84,6 +86,7 @@ const getProgress = (state?: GuideState): number => { export const GuidePanel = ({ api, application }: GuidePanelProps) => { const { euiTheme } = useEuiTheme(); const [isGuideOpen, setIsGuideOpen] = useState(false); + const [isQuitGuideModalOpen, setIsQuitGuideModalOpen] = useState(false); const [guideState, setGuideState] = useState(undefined); const styles = getGuidePanelStyles(euiTheme); @@ -108,11 +111,20 @@ export const GuidePanel = ({ api, application }: GuidePanelProps) => { await api.completeGuide(guideState!.guideId); }; + const openQuitGuideModal = () => { + // Close the dropdown panel + setIsGuideOpen(false); + // Open the confirmation modal + setIsQuitGuideModalOpen(true); + }; + + const closeQuitGuideModal = () => { + setIsQuitGuideModalOpen(false); + }; + useEffect(() => { const subscription = api.fetchActiveGuideState$().subscribe((newGuideState) => { - if (newGuideState) { - setGuideState(newGuideState); - } + setGuideState(newGuideState); }); return () => subscription.unsubscribe(); }, [api]); @@ -236,7 +248,7 @@ export const GuidePanel = ({ api, application }: GuidePanelProps) => { - {guideConfig?.steps.map((step, index, steps) => { + {guideConfig?.steps.map((step, index) => { const accordionId = htmlIdGenerator(`accordion${index}`)(); const stepState = guideState?.steps[index]; @@ -271,10 +283,9 @@ export const GuidePanel = ({ api, application }: GuidePanelProps) => { - {/* TODO: Implement exit guide modal - https://github.com/elastic/kibana/issues/139804 */} - {}}> + {i18n.translate('guidedOnboarding.dropdownPanel.footer.exitGuideButtonLabel', { - defaultMessage: 'Exit setup guide', + defaultMessage: 'Quit setup guide', })} @@ -325,6 +336,10 @@ export const GuidePanel = ({ api, application }: GuidePanelProps) => { )} + + {isQuitGuideModalOpen && ( + + )} ); }; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_step.styles.ts b/src/plugins/guided_onboarding/public/components/guide_panel_step.styles.ts index 498059564e6ea..8d34d45b7a53c 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel_step.styles.ts +++ b/src/plugins/guided_onboarding/public/components/guide_panel_step.styles.ts @@ -8,18 +8,25 @@ import { EuiThemeComputed } from '@elastic/eui'; import { css } from '@emotion/react'; +import { StepStatus } from '../../common/types'; -export const getGuidePanelStepStyles = (euiTheme: EuiThemeComputed) => ({ +export const getGuidePanelStepStyles = (euiTheme: EuiThemeComputed, stepStatus: StepStatus) => ({ stepNumber: css` width: 24px; height: 24px; border-radius: 50%; - border: 2px solid ${euiTheme.colors.success}; + border: 2px solid + ${stepStatus === 'inactive' ? euiTheme.colors.lightShade : euiTheme.colors.success}; font-weight: ${euiTheme.font.weight.medium}; text-align: center; line-height: 1.4; + color: ${stepStatus === 'inactive' ? euiTheme.colors.subduedText : euiTheme.colors.text}; `, stepTitle: css` - font-weight: ${euiTheme.font.weight.bold}; + font-weight: ${euiTheme.font.weight.semiBold}; + color: ${stepStatus === 'inactive' ? euiTheme.colors.subduedText : euiTheme.colors.text}; + .euiAccordion-isOpen & { + color: ${euiTheme.colors.title}; + } `, }); diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_step.tsx b/src/plugins/guided_onboarding/public/components/guide_panel_step.tsx index 8a98d87debf1a..c05ad6ec310c7 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel_step.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel_step.tsx @@ -40,10 +40,10 @@ export const GuideStep = ({ navigateToStep, }: GuideStepProps) => { const { euiTheme } = useEuiTheme(); - const styles = getGuidePanelStepStyles(euiTheme); + const styles = getGuidePanelStepStyles(euiTheme, stepStatus); - const buttonContent = ( - + const stepTitleContent = ( + {stepStatus === 'complete' ? ( @@ -61,45 +61,49 @@ export const GuideStep = ({ return (
- - <> - + {stepStatus === 'complete' ? ( + <>{stepTitleContent} + ) : ( + + <> + - -
    - {stepConfig.descriptionList.map((description, index) => { - return
  • {description}
  • ; - })} -
-
+ +
    + {stepConfig.descriptionList.map((description, index) => { + return
  • {description}
  • ; + })} +
+
- - {(stepStatus === 'in_progress' || stepStatus === 'active') && ( - - - navigateToStep(stepConfig.id, stepConfig.location)} - fill - data-test-subj="activeStepButtonLabel" - > - {stepStatus === 'active' - ? i18n.translate('guidedOnboarding.dropdownPanel.startStepButtonLabel', { - defaultMessage: 'Start', - }) - : i18n.translate('guidedOnboarding.dropdownPanel.continueStepButtonLabel', { - defaultMessage: 'Continue', - })} - - - - )} - -
+ + {(stepStatus === 'in_progress' || stepStatus === 'active') && ( + + + navigateToStep(stepConfig.id, stepConfig.location)} + fill + data-test-subj="activeStepButtonLabel" + > + {stepStatus === 'active' + ? i18n.translate('guidedOnboarding.dropdownPanel.startStepButtonLabel', { + defaultMessage: 'Start', + }) + : i18n.translate('guidedOnboarding.dropdownPanel.continueStepButtonLabel', { + defaultMessage: 'Continue', + })} + + + + )} + +
+ )}
diff --git a/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx b/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx new file mode 100644 index 0000000000000..a7a7e34c311b4 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { useState } from 'react'; + +import { EuiText, EuiConfirmModal } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { GuideState } from '../../common/types'; +import { apiService } from '../services/api'; + +interface QuitGuideModalProps { + closeModal: () => void; + currentGuide: GuideState; +} + +export const QuitGuideModal = ({ closeModal, currentGuide }: QuitGuideModalProps) => { + const [isDeleting, setIsDeleting] = useState(false); + + const deleteGuide = async () => { + setIsDeleting(true); + await apiService.deactivateGuide(currentGuide); + closeModal(); + }; + + return ( + + +

+ {i18n.translate('guidedOnboarding.quitGuideModal.modalDescription', { + defaultMessage: + 'You can restart anytime by opening the Setup guide from the Help menu.', + })} +

+
+
+ ); +}; 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 df17d00d7f2d4..8eafa3b51c408 100644 --- a/src/plugins/guided_onboarding/public/constants/guides_config/security.ts +++ b/src/plugins/guided_onboarding/public/constants/guides_config/security.ts @@ -21,6 +21,11 @@ export const securityConfig: GuideConfig = { '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.', ], + integration: 'endpoint', + location: { + appID: 'integrations', + path: '/browse/security', + }, }, { id: 'rules', @@ -32,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 new file mode 100644 index 0000000000000..dcac2cfc1c0fb --- /dev/null +++ b/src/plugins/guided_onboarding/public/mocks.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { BehaviorSubject } from 'rxjs'; +import { GuidedOnboardingPluginStart } from '.'; + +const apiServiceMock: jest.Mocked = { + guidedOnboardingApi: { + setup: jest.fn(), + fetchActiveGuideState$: () => new BehaviorSubject(undefined), + fetchAllGuidesState: jest.fn(), + updateGuideState: jest.fn(), + activateGuide: jest.fn(), + completeGuide: jest.fn(), + isGuideStepActive$: () => new BehaviorSubject(false), + startGuideStep: jest.fn(), + completeGuideStep: jest.fn(), + isGuidedOnboardingActiveForIntegration$: () => new BehaviorSubject(false), + completeGuidedOnboardingForIntegration: jest.fn(), + isGuidePanelOpen$: new BehaviorSubject(false), + }, +}; + +export const guidedOnboardingMock = { + createSetup: () => {}, + createStart: () => apiServiceMock, +}; diff --git a/src/plugins/guided_onboarding/public/services/api.mocks.ts b/src/plugins/guided_onboarding/public/services/api.mocks.ts new file mode 100644 index 0000000000000..19dd67c7d7b1b --- /dev/null +++ b/src/plugins/guided_onboarding/public/services/api.mocks.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { GuideState } from '../../common/types'; + +export const searchAddDataActiveState: GuideState = { + guideId: 'search', + isActive: true, + status: 'in_progress', + steps: [ + { + id: 'add_data', + status: 'active', + }, + { + id: 'browse_docs', + status: 'inactive', + }, + { + id: 'search_experience', + status: 'inactive', + }, + ], +}; + +export const searchAddDataInProgressState: GuideState = { + isActive: true, + status: 'in_progress', + steps: [ + { + id: 'add_data', + status: 'in_progress', + }, + { + id: 'browse_docs', + status: 'inactive', + }, + { + id: 'search_experience', + status: 'inactive', + }, + ], + guideId: 'search', +}; + +export const securityAddDataInProgressState: GuideState = { + guideId: 'security', + status: 'in_progress', + isActive: true, + steps: [ + { + id: 'add_data', + status: 'in_progress', + }, + { + id: 'rules', + status: 'inactive', + }, + ], +}; + +export const securityRulesActivesState: GuideState = { + guideId: 'security', + isActive: true, + status: 'in_progress', + steps: [ + { + id: 'add_data', + status: 'complete', + }, + { + id: 'rules', + status: 'active', + }, + ], +}; + +export const noGuideActiveState: GuideState = { + guideId: 'security', + status: 'in_progress', + isActive: false, + steps: [ + { + id: 'add_data', + status: 'in_progress', + }, + { + id: 'rules', + status: 'inactive', + }, + ], +}; diff --git a/src/plugins/guided_onboarding/public/services/api.test.ts b/src/plugins/guided_onboarding/public/services/api.test.ts index ffe5596bd7e35..5deb3d50987f2 100644 --- a/src/plugins/guided_onboarding/public/services/api.test.ts +++ b/src/plugins/guided_onboarding/public/services/api.test.ts @@ -14,29 +14,17 @@ import { API_BASE_PATH } from '../../common/constants'; import { guidesConfig } from '../constants/guides_config'; import type { GuideState } from '../../common/types'; import { ApiService } from './api'; +import { + noGuideActiveState, + searchAddDataActiveState, + securityAddDataInProgressState, + securityRulesActivesState, +} from './api.mocks'; const searchGuide = 'search'; const firstStep = guidesConfig[searchGuide].steps[0].id; - -const mockActiveSearchGuideState: GuideState = { - guideId: searchGuide, - isActive: true, - status: 'in_progress', - steps: [ - { - id: 'add_data', - status: 'active', - }, - { - id: 'browse_docs', - status: 'inactive', - }, - { - id: 'search_experience', - status: 'inactive', - }, - ], -}; +const endpointIntegration = 'endpoint'; +const kubernetesIntegration = 'kubernetes'; describe('GuidedOnboarding ApiService', () => { let httpClient: jest.Mocked; @@ -46,7 +34,7 @@ describe('GuidedOnboarding ApiService', () => { beforeEach(() => { httpClient = httpServiceMock.createStartContract({ basePath: '/base/path' }); httpClient.get.mockResolvedValue({ - state: { activeGuide: searchGuide, activeStep: firstStep }, + state: [securityAddDataInProgressState], }); apiService = new ApiService(); apiService.setup(httpClient); @@ -72,7 +60,7 @@ describe('GuidedOnboarding ApiService', () => { await apiService.activateGuide(searchGuide); const state = await firstValueFrom(apiService.fetchActiveGuideState$()); - expect(state).toEqual(mockActiveSearchGuideState); + expect(state).toEqual(searchAddDataActiveState); }); }); @@ -84,17 +72,31 @@ describe('GuidedOnboarding ApiService', () => { }); }); + describe('deactivateGuide', () => { + it('deactivates an existing guide', async () => { + await apiService.deactivateGuide(searchAddDataActiveState); + + expect(httpClient.put).toHaveBeenCalledTimes(1); + expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, { + body: JSON.stringify({ + ...searchAddDataActiveState, + isActive: false, + }), + }); + }); + }); + describe('updateGuideState', () => { it('sends a request to the put API', async () => { const updatedState: GuideState = { - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, steps: [ { - id: mockActiveSearchGuideState.steps[0].id, + id: searchAddDataActiveState.steps[0].id, status: 'in_progress', // update the first step status }, - mockActiveSearchGuideState.steps[1], - mockActiveSearchGuideState.steps[2], + searchAddDataActiveState.steps[1], + searchAddDataActiveState.steps[2], ], }; await apiService.updateGuideState(updatedState, false); @@ -108,14 +110,14 @@ describe('GuidedOnboarding ApiService', () => { describe('isGuideStepActive$', () => { it('returns true if the step has been started', async (done) => { const updatedState: GuideState = { - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, steps: [ { - id: mockActiveSearchGuideState.steps[0].id, + id: searchAddDataActiveState.steps[0].id, status: 'in_progress', }, - mockActiveSearchGuideState.steps[1], - mockActiveSearchGuideState.steps[2], + searchAddDataActiveState.steps[1], + searchAddDataActiveState.steps[2], ], }; await apiService.updateGuideState(updatedState, false); @@ -130,7 +132,7 @@ describe('GuidedOnboarding ApiService', () => { }); it('returns false if the step is not been started', async (done) => { - await apiService.updateGuideState(mockActiveSearchGuideState, false); + await apiService.updateGuideState(searchAddDataActiveState, false); subscription = apiService .isGuideStepActive$(searchGuide, firstStep) .subscribe((isStepActive) => { @@ -170,21 +172,18 @@ describe('GuidedOnboarding ApiService', () => { }); it('reactivates a guide that has already been started', async () => { - await apiService.activateGuide(searchGuide, mockActiveSearchGuideState); + await apiService.activateGuide(searchGuide, searchAddDataActiveState); expect(httpClient.put).toHaveBeenCalledTimes(1); expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, { - body: JSON.stringify({ - ...mockActiveSearchGuideState, - isActive: true, - }), + body: JSON.stringify(searchAddDataActiveState), }); }); }); describe('completeGuide', () => { const readyToCompleteGuideState: GuideState = { - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, steps: [ { id: 'add_data', @@ -224,7 +223,7 @@ describe('GuidedOnboarding ApiService', () => { it('returns undefined if the selected guide has uncompleted steps', async () => { const incompleteGuideState: GuideState = { - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, steps: [ { id: 'add_data', @@ -249,7 +248,7 @@ describe('GuidedOnboarding ApiService', () => { describe('startGuideStep', () => { beforeEach(async () => { - await apiService.updateGuideState(mockActiveSearchGuideState, false); + await apiService.updateGuideState(searchAddDataActiveState, false); }); it('updates the selected step and marks it as in_progress', async () => { @@ -257,16 +256,16 @@ describe('GuidedOnboarding ApiService', () => { expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, { body: JSON.stringify({ - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, isActive: true, status: 'in_progress', steps: [ { - id: mockActiveSearchGuideState.steps[0].id, + id: searchAddDataActiveState.steps[0].id, status: 'in_progress', }, - mockActiveSearchGuideState.steps[1], - mockActiveSearchGuideState.steps[2], + searchAddDataActiveState.steps[1], + searchAddDataActiveState.steps[2], ], }), }); @@ -281,14 +280,14 @@ describe('GuidedOnboarding ApiService', () => { describe('completeGuideStep', () => { it(`completes the step when it's in progress`, async () => { const updatedState: GuideState = { - ...mockActiveSearchGuideState, + ...searchAddDataActiveState, steps: [ { - id: mockActiveSearchGuideState.steps[0].id, + id: searchAddDataActiveState.steps[0].id, status: 'in_progress', // Mark a step as in_progress in order to test the "completeGuideStep" behavior }, - mockActiveSearchGuideState.steps[1], - mockActiveSearchGuideState.steps[2], + searchAddDataActiveState.steps[1], + searchAddDataActiveState.steps[2], ], }; await apiService.updateGuideState(updatedState, false); @@ -303,14 +302,14 @@ describe('GuidedOnboarding ApiService', () => { ...updatedState, steps: [ { - id: mockActiveSearchGuideState.steps[0].id, + id: searchAddDataActiveState.steps[0].id, status: 'complete', }, { - id: mockActiveSearchGuideState.steps[1].id, + id: searchAddDataActiveState.steps[1].id, status: 'active', }, - mockActiveSearchGuideState.steps[2], + searchAddDataActiveState.steps[2], ], }), }); @@ -322,11 +321,91 @@ describe('GuidedOnboarding ApiService', () => { }); it('does nothing if the step is not in progress', async () => { - await apiService.updateGuideState(mockActiveSearchGuideState, false); + await apiService.updateGuideState(searchAddDataActiveState, false); await apiService.completeGuideStep(searchGuide, firstStep); // Expect only 1 call from updateGuideState() expect(httpClient.put).toHaveBeenCalledTimes(1); }); }); + + describe('isGuidedOnboardingActiveForIntegration$', () => { + it('returns true if the integration is part of the active step', async (done) => { + httpClient.get.mockResolvedValue({ + state: [securityAddDataInProgressState], + }); + apiService.setup(httpClient); + subscription = apiService + .isGuidedOnboardingActiveForIntegration$(endpointIntegration) + .subscribe((isIntegrationInGuideStep) => { + if (isIntegrationInGuideStep) { + done(); + } + }); + }); + + it('returns false if another integration is part of the active step', async (done) => { + httpClient.get.mockResolvedValue({ + state: [securityAddDataInProgressState], + }); + apiService.setup(httpClient); + subscription = apiService + .isGuidedOnboardingActiveForIntegration$(kubernetesIntegration) + .subscribe((isIntegrationInGuideStep) => { + if (!isIntegrationInGuideStep) { + done(); + } + }); + }); + + it('returns false if no guide is active', async (done) => { + httpClient.get.mockResolvedValue({ + state: [noGuideActiveState], + }); + apiService.setup(httpClient); + subscription = apiService + .isGuidedOnboardingActiveForIntegration$(endpointIntegration) + .subscribe((isIntegrationInGuideStep) => { + if (!isIntegrationInGuideStep) { + done(); + } + }); + }); + }); + + describe('completeGuidedOnboardingForIntegration', () => { + it(`completes the step if it's active for the integration`, async () => { + httpClient.get.mockResolvedValue({ + state: [securityAddDataInProgressState], + }); + apiService.setup(httpClient); + + await apiService.completeGuidedOnboardingForIntegration(endpointIntegration); + expect(httpClient.put).toHaveBeenCalledTimes(1); + // this assertion depends on the guides config + expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, { + body: JSON.stringify(securityRulesActivesState), + }); + }); + + it(`does nothing if the step has a different integration`, async () => { + httpClient.get.mockResolvedValue({ + state: [securityAddDataInProgressState], + }); + apiService.setup(httpClient); + + await apiService.completeGuidedOnboardingForIntegration(kubernetesIntegration); + expect(httpClient.put).not.toHaveBeenCalled(); + }); + + it(`does nothing if no guide is active`, async () => { + httpClient.get.mockResolvedValue({ + state: [noGuideActiveState], + }); + apiService.setup(httpClient); + + await apiService.completeGuidedOnboardingForIntegration(endpointIntegration); + expect(httpClient.put).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/plugins/guided_onboarding/public/services/api.ts b/src/plugins/guided_onboarding/public/services/api.ts index 1adfaa5d8cc23..7c970717be5f1 100644 --- a/src/plugins/guided_onboarding/public/services/api.ts +++ b/src/plugins/guided_onboarding/public/services/api.ts @@ -9,11 +9,17 @@ import { HttpSetup } from '@kbn/core/public'; import { BehaviorSubject, map, from, concatMap, of, Observable, firstValueFrom } from 'rxjs'; +import { GuidedOnboardingApi } from '../types'; +import { + getGuideConfig, + getInProgressStepId, + isIntegrationInGuideStep, + isLastStep, +} from './helpers'; import { API_BASE_PATH } from '../../common/constants'; import type { GuideState, GuideId, GuideStep, GuideStepIds } from '../../common/types'; -import { isLastStep, getGuideConfig } from './helpers'; -export class ApiService { +export class ApiService implements GuidedOnboardingApi { private client: HttpSetup | undefined; private onboardingGuideState$!: BehaviorSubject; public isGuidePanelOpen$: BehaviorSubject = new BehaviorSubject(false); @@ -89,7 +95,8 @@ export class ApiService { const response = await this.client.put<{ state: GuideState }>(`${API_BASE_PATH}/state`, { body: JSON.stringify(newState), }); - this.onboardingGuideState$.next(newState); + // If the guide has been deactivated, we return undefined + this.onboardingGuideState$.next(newState.isActive ? newState : undefined); this.isGuidePanelOpen$.next(panelState); return response; } catch (error) { @@ -102,8 +109,8 @@ export class ApiService { /** * Activates a guide by guideId * This is useful for the onboarding landing page, when a user selects a guide to start or continue - * @param {GuideId} guideID the id of the guide (one of search, observability, security) - * @param {GuideState} guideState (optional) the selected guide state, if it exists (i.e., if a user is continuing a guide) + * @param {GuideId} guideId the id of the guide (one of search, observability, security) + * @param {GuideState} guide (optional) the selected guide state, if it exists (i.e., if a user is continuing a guide) * @return {Promise} a promise with the updated guide state */ public async activateGuide( @@ -146,11 +153,27 @@ export class ApiService { } } + /** + * Marks a guide as inactive + * This is useful for the dropdown panel, when a user quits a guide + * @param {GuideState} guide (optional) the selected guide state, if it exists (i.e., if a user is continuing a guide) + * @return {Promise} a promise with the updated guide state + */ + public async deactivateGuide(guide: GuideState): Promise<{ state: GuideState } | undefined> { + return await this.updateGuideState( + { + ...guide, + isActive: false, + }, + false + ); + } + /** * Completes a guide * Updates the overall guide status to 'complete', and marks it as inactive * This is useful for the dropdown panel, when the user clicks the "Continue using Elastic" button after completing all steps - * @param {GuideId} guideID the id of the guide (one of search, observability, security) + * @param {GuideId} guideId the id of the guide (one of search, observability, security) * @return {Promise} a promise with the updated guide state */ public async completeGuide(guideId: GuideId): Promise<{ state: GuideState } | undefined> { @@ -300,6 +323,38 @@ export class ApiService { return undefined; } + + /** + * An observable with the boolean value if the guided onboarding is currently active for the integration. + * Returns true, if the passed integration is used in the current guide's step. + * Returns false otherwise. + * @param {string} integration the integration (package name) to check for in the guided onboarding config + * @return {Observable} an observable with the boolean value + */ + public isGuidedOnboardingActiveForIntegration$(integration?: string): Observable { + return this.fetchActiveGuideState$().pipe( + map((state) => { + return state ? isIntegrationInGuideStep(state, integration) : false; + }) + ); + } + + public async completeGuidedOnboardingForIntegration( + integration?: string + ): Promise<{ state: GuideState } | undefined> { + if (integration) { + const currentState = await firstValueFrom(this.fetchActiveGuideState$()); + if (currentState) { + const inProgressStepId = getInProgressStepId(currentState); + if (inProgressStepId) { + const isIntegrationStepActive = isIntegrationInGuideStep(currentState, integration); + if (isIntegrationStepActive) { + return await this.completeGuideStep(currentState?.guideId, inProgressStepId); + } + } + } + } + } } export const apiService = new ApiService(); diff --git a/src/plugins/guided_onboarding/public/services/helpers.test.ts b/src/plugins/guided_onboarding/public/services/helpers.test.ts index bc09a9185424c..586566fe9488b 100644 --- a/src/plugins/guided_onboarding/public/services/helpers.test.ts +++ b/src/plugins/guided_onboarding/public/services/helpers.test.ts @@ -7,11 +7,16 @@ */ import { guidesConfig } from '../constants/guides_config'; -import { isLastStep } from './helpers'; +import { isIntegrationInGuideStep, isLastStep } from './helpers'; +import { + noGuideActiveState, + securityAddDataInProgressState, + securityRulesActivesState, +} from './api.mocks'; const searchGuide = 'search'; const firstStep = guidesConfig[searchGuide].steps[0].id; -const lastStep = guidesConfig[searchGuide].steps[2].id; +const lastStep = guidesConfig[searchGuide].steps[guidesConfig[searchGuide].steps.length - 1].id; describe('GuidedOnboarding ApiService helpers', () => { // this test suite depends on the guides config @@ -26,4 +31,27 @@ describe('GuidedOnboarding ApiService helpers', () => { expect(result).toBe(false); }); }); + + describe('isIntegrationInGuideStep', () => { + it('return true if the integration is defined in the guide step config', () => { + const result = isIntegrationInGuideStep(securityAddDataInProgressState, 'endpoint'); + expect(result).toBe(true); + }); + it('returns false if a different integration is defined in the guide step', () => { + const result = isIntegrationInGuideStep(securityAddDataInProgressState, 'kubernetes'); + expect(result).toBe(false); + }); + it('returns false if no integration is defined in the guide step', () => { + const result = isIntegrationInGuideStep(securityRulesActivesState, 'endpoint'); + expect(result).toBe(false); + }); + it('returns false if no guide is active', () => { + const result = isIntegrationInGuideStep(noGuideActiveState, 'endpoint'); + expect(result).toBe(false); + }); + it('returns false if no integration passed', () => { + const result = isIntegrationInGuideStep(securityAddDataInProgressState); + expect(result).toBe(false); + }); + }); }); diff --git a/src/plugins/guided_onboarding/public/services/helpers.ts b/src/plugins/guided_onboarding/public/services/helpers.ts index ea4245be99150..0e738646c558d 100644 --- a/src/plugins/guided_onboarding/public/services/helpers.ts +++ b/src/plugins/guided_onboarding/public/services/helpers.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import type { GuideId } from '../../common/types'; +import type { GuideId, GuideState, GuideStepIds } from '../../common/types'; import { guidesConfig } from '../constants/guides_config'; -import type { GuideConfig, StepConfig } from '../types'; +import { GuideConfig, StepConfig } from '../types'; export const getGuideConfig = (guideID?: string): GuideConfig | undefined => { if (guideID && Object.keys(guidesConfig).includes(guideID)) { @@ -33,3 +33,26 @@ export const isLastStep = (guideID: string, stepID: string): boolean => { } return false; }; + +export const getInProgressStepId = (state: GuideState): GuideStepIds | undefined => { + const inProgressStep = state.steps.find((step) => step.status === 'in_progress'); + return inProgressStep ? inProgressStep.id : undefined; +}; + +const getInProgressStepConfig = (state: GuideState): StepConfig | undefined => { + const inProgressStepId = getInProgressStepId(state); + if (inProgressStepId) { + const config = getGuideConfig(state.guideId); + if (config) { + return config.steps.find((step) => step.id === inProgressStepId); + } + } +}; + +export const isIntegrationInGuideStep = (state: GuideState, integration?: string): boolean => { + if (state.isActive) { + const stepConfig = getInProgressStepConfig(state); + return stepConfig ? stepConfig.integration === integration : false; + } + return false; +}; diff --git a/src/plugins/guided_onboarding/public/types.ts b/src/plugins/guided_onboarding/public/types.ts index 4a16c16336c6b..ba7271756cbb7 100755 --- a/src/plugins/guided_onboarding/public/types.ts +++ b/src/plugins/guided_onboarding/public/types.ts @@ -6,15 +6,16 @@ * Side Public License, v 1. */ +import { Observable } from 'rxjs'; import { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; -import { GuideId, GuideStepIds, StepStatus } from '../common/types'; -import { ApiService } from './services/api'; +import { HttpSetup } from '@kbn/core/public'; +import { GuideId, GuideState, GuideStepIds, StepStatus } from '../common/types'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface GuidedOnboardingPluginSetup {} export interface GuidedOnboardingPluginStart { - guidedOnboardingApi?: ApiService; + guidedOnboardingApi?: GuidedOnboardingApi; } export interface AppPluginStartDependencies { @@ -25,6 +26,35 @@ export interface ClientConfigType { ui: boolean; } +export interface GuidedOnboardingApi { + setup: (httpClient: HttpSetup) => void; + fetchActiveGuideState$: () => Observable; + fetchAllGuidesState: () => Promise<{ state: GuideState[] } | undefined>; + updateGuideState: ( + newState: GuideState, + panelState: boolean + ) => Promise<{ state: GuideState } | undefined>; + activateGuide: ( + guideId: GuideId, + guide?: GuideState + ) => Promise<{ state: GuideState } | undefined>; + completeGuide: (guideId: GuideId) => Promise<{ state: GuideState } | undefined>; + isGuideStepActive$: (guideId: GuideId, stepId: GuideStepIds) => Observable; + startGuideStep: ( + guideId: GuideId, + stepId: GuideStepIds + ) => Promise<{ state: GuideState } | undefined>; + completeGuideStep: ( + guideId: GuideId, + stepId: GuideStepIds + ) => Promise<{ state: GuideState } | undefined>; + isGuidedOnboardingActiveForIntegration$: (integration?: string) => Observable; + completeGuidedOnboardingForIntegration: ( + integration?: string + ) => Promise<{ state: GuideState } | undefined>; + isGuidePanelOpen$: Observable; +} + export interface StepConfig { id: GuideStepIds; title: string; @@ -34,6 +64,7 @@ export interface StepConfig { path: string; }; status?: StepStatus; + integration?: string; } export interface GuideConfig { title: string; diff --git a/src/plugins/guided_onboarding/server/routes/index.ts b/src/plugins/guided_onboarding/server/routes/index.ts index cce5aad08b1e5..adc65d0bf6866 100755 --- a/src/plugins/guided_onboarding/server/routes/index.ts +++ b/src/plugins/guided_onboarding/server/routes/index.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import type { IRouter, SavedObjectsClient } from '@kbn/core/server'; +import { API_BASE_PATH } from '../../common/constants'; import type { GuideState } from '../../common/types'; import { guidedSetupSavedObjectsType } from '../saved_objects'; @@ -35,7 +36,7 @@ export function defineRoutes(router: IRouter) { // Fetch all guides state; optionally pass the query param ?active=true to only return the active guide router.get( { - path: '/api/guided_onboarding/state', + path: `${API_BASE_PATH}/state`, validate: { query: schema.object({ active: schema.maybe(schema.boolean()), @@ -69,7 +70,7 @@ export function defineRoutes(router: IRouter) { // will also check any existing active guides and update them to an "inactive" state router.put( { - path: '/api/guided_onboarding/state', + path: `${API_BASE_PATH}/state`, validate: { body: schema.object({ status: schema.string(), diff --git a/src/plugins/home/kibana.json b/src/plugins/home/kibana.json index 02b33e814e2a1..72b4d6cb8fd0b 100644 --- a/src/plugins/home/kibana.json +++ b/src/plugins/home/kibana.json @@ -8,6 +8,6 @@ "server": true, "ui": true, "requiredPlugins": ["dataViews", "share", "urlForwarding"], - "optionalPlugins": ["usageCollection", "customIntegrations"], + "optionalPlugins": ["usageCollection", "customIntegrations", "cloud"], "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.webp b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.webp index f6720bf0e3e51..a48a4a05318bb 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.webp and b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.webp b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.webp index b9263441d1f22..cccee38594e9c 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.webp and b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.webp b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.webp index f73ae849befc9..2c50c8a1471e9 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.webp and b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.webp b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.webp index 3cea45093ddac..0c3769a3c8268 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.webp and b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.webp b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.webp index c5b2250190ae0..8defebcc5fbbb 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.webp and b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.webp b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.webp index 0c5ff2f6cd9f1..6f4dad0dad1f9 100644 Binary files a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.webp and b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.webp differ diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts index 12243944ef0f0..a6c6012a28ed6 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -11,6 +11,7 @@ import { HomePublicPlugin } from './plugin'; import { coreMock } from '@kbn/core/public/mocks'; import { urlForwardingPluginMock } from '@kbn/url-forwarding-plugin/public/mocks'; import { SharePluginSetup } from '@kbn/share-plugin/public'; +import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; const mockInitializerContext = coreMock.createPluginInitializerContext(); const mockShare = {} as SharePluginSetup; @@ -24,14 +25,11 @@ describe('HomePublicPlugin', () => { }); describe('setup', () => { - test('registers tutorial directory to feature catalogue', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - share: mockShare, - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); + test('registers tutorial directory to feature catalogue', () => { + const setup = new HomePublicPlugin(mockInitializerContext).setup(coreMock.createSetup(), { + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); expect(setup).toHaveProperty('featureCatalogue'); expect(setup.featureCatalogue.register).toHaveBeenCalledTimes(1); expect(setup.featureCatalogue.register).toHaveBeenCalledWith( @@ -44,53 +42,73 @@ describe('HomePublicPlugin', () => { ); }); - test('wires up and returns registry', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - share: mockShare, - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); + test('wires up and returns registry', () => { + const setup = new HomePublicPlugin(mockInitializerContext).setup(coreMock.createSetup(), { + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); expect(setup).toHaveProperty('featureCatalogue'); expect(setup.featureCatalogue).toHaveProperty('register'); }); - test('wires up and returns environment service', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - share: {} as SharePluginSetup, - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); + test('wires up and returns environment service', () => { + const setup = new HomePublicPlugin(mockInitializerContext).setup(coreMock.createSetup(), { + share: {} as SharePluginSetup, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); expect(setup).toHaveProperty('environment'); expect(setup.environment).toHaveProperty('update'); }); - test('wires up and returns tutorial service', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - share: mockShare, - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); + test('wires up and returns tutorial service', () => { + const setup = new HomePublicPlugin(mockInitializerContext).setup(coreMock.createSetup(), { + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); expect(setup).toHaveProperty('tutorials'); expect(setup.tutorials).toHaveProperty('setVariable'); }); - test('wires up and returns welcome service', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - share: mockShare, - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); + test('wires up and returns welcome service', () => { + const setup = new HomePublicPlugin(mockInitializerContext).setup(coreMock.createSetup(), { + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); expect(setup).toHaveProperty('welcomeScreen'); expect(setup.welcomeScreen).toHaveProperty('registerOnRendered'); expect(setup.welcomeScreen).toHaveProperty('registerTelemetryNoticeRenderer'); }); + + test('sets the cloud environment variable when the cloud plugin is present but isCloudEnabled: false', () => { + const cloud = { ...cloudMock.createSetup(), isCloudEnabled: false }; + const plugin = new HomePublicPlugin(mockInitializerContext); + const setup = plugin.setup(coreMock.createSetup(), { + cloud, + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); + expect(setup.environment.update).toHaveBeenCalledTimes(1); + expect(setup.environment.update).toHaveBeenCalledWith({ cloud: false }); + expect(setup.tutorials.setVariable).toHaveBeenCalledTimes(0); + }); + + test('when cloud is enabled, it sets the cloud environment and the tutorials variable "cloud"', () => { + const cloud = { ...cloudMock.createSetup(), isCloudEnabled: true }; + const plugin = new HomePublicPlugin(mockInitializerContext); + const setup = plugin.setup(coreMock.createSetup(), { + cloud, + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + }); + expect(setup.environment.update).toHaveBeenCalledTimes(1); + expect(setup.environment.update).toHaveBeenCalledWith({ cloud: true }); + expect(setup.tutorials.setVariable).toHaveBeenCalledTimes(1); + expect(setup.tutorials.setVariable).toHaveBeenCalledWith('cloud', { + id: 'mock-cloud-id', + baseUrl: 'base-url', + deploymentUrl: 'deployment-url', + profileUrl: 'profile-url', + }); + }); }); }); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 642a8d575e078..e27ddf107a5ee 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -20,6 +20,7 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; import { AppNavLinkStatus } from '@kbn/core/public'; import { SharePluginSetup } from '@kbn/share-plugin/public'; +import type { CloudSetup } from '@kbn/cloud-plugin/public'; import { PLUGIN_ID, HOME_APP_BASE_PATH } from '../common/constants'; import { setServices } from './application/kibana_services'; import { ConfigSchema } from '../config'; @@ -42,6 +43,7 @@ export interface HomePluginStartDependencies { } export interface HomePluginSetupDependencies { + cloud?: CloudSetup; share: SharePluginSetup; usageCollection?: UsageCollectionSetup; urlForwarding: UrlForwardingSetup; @@ -66,7 +68,7 @@ export class HomePublicPlugin public setup( core: CoreSetup, - { share, urlForwarding, usageCollection }: HomePluginSetupDependencies + { cloud, share, urlForwarding, usageCollection }: HomePluginSetupDependencies ): HomePublicPluginSetup { core.application.register({ id: PLUGIN_ID, @@ -127,10 +129,25 @@ export class HomePublicPlugin order: 500, }); + const environment = { ...this.environmentService.setup() }; + const tutorials = { ...this.tutorialService.setup() }; + if (cloud) { + environment.update({ cloud: cloud.isCloudEnabled }); + if (cloud.isCloudEnabled) { + tutorials.setVariable('cloud', { + id: cloud.cloudId, + baseUrl: cloud.baseUrl, + // Cloud's API already provides the full URLs + profileUrl: cloud.profileUrl?.replace(cloud.baseUrl ?? '', ''), + deploymentUrl: cloud.deploymentUrl?.replace(cloud.baseUrl ?? '', ''), + }); + } + } + return { featureCatalogue, - environment: { ...this.environmentService.setup() }, - tutorials: { ...this.tutorialService.setup() }, + environment, + tutorials, addData: { ...this.addDataService.setup() }, welcomeScreen: { ...this.welcomeService.setup() }, }; diff --git a/src/plugins/home/public/services/environment/environment.mock.ts b/src/plugins/home/public/services/environment/environment.mock.ts index 713a59ceac7bf..f2d4747d44d6a 100644 --- a/src/plugins/home/public/services/environment/environment.mock.ts +++ b/src/plugins/home/public/services/environment/environment.mock.ts @@ -18,14 +18,13 @@ const createSetupMock = (): jest.Mocked => { const createMock = (): jest.Mocked> => { const service = { - setup: jest.fn(), + setup: jest.fn(createSetupMock), getEnvironment: jest.fn(() => ({ cloud: false, apmUi: false, ml: false, })), }; - service.setup.mockImplementation(createSetupMock); return service; }; diff --git a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts index ad334470205aa..d90d6855879bb 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts @@ -189,44 +189,6 @@ export const getSavedObjects = (): SavedObject[] => [ updated_at: '2021-08-05T12:43:35.817Z', version: 'WzE3MSwxXQ==', }, - { - attributes: { - description: '', - kibanaSavedObjectMeta: { - searchSourceJSON: '{"query":{"query":"","language":"kuery"},"filter":[]}', - }, - title: '[eCommerce] Controls', - uiStateJSON: '{}', - version: 1, - visState: - '{"title":"[eCommerce] Controls","type":"input_control_vis","params":{"controls":[{"id":"1536977437774","fieldName":"manufacturer.keyword","parent":"","label":"Manufacturer","type":"list","options":{"type":"terms","multiselect":true,"dynamicOptions":true,"size":5,"order":"desc"},"indexPatternRefName":"control_0_index_pattern"},{"id":"1536977465554","fieldName":"category.keyword","parent":"","label":"Category","type":"list","options":{"type":"terms","multiselect":true,"dynamicOptions":true,"size":5,"order":"desc"},"indexPatternRefName":"control_1_index_pattern"},{"id":"1536977596163","fieldName":"total_quantity","parent":"","label":"Quantity","type":"range","options":{"decimalPlaces":0,"step":1},"indexPatternRefName":"control_2_index_pattern"}],"updateFiltersOnChange":false,"useTimeFilter":true,"pinFilters":false},"aggs":[]}', - }, - coreMigrationVersion: '8.0.0', - id: 'c3378480-f5ea-11eb-a78e-83aac3c38a60', - migrationVersion: { - visualization: '7.14.0', - }, - references: [ - { - id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - name: 'control_0_index_pattern', - type: 'index-pattern', - }, - { - id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - name: 'control_1_index_pattern', - type: 'index-pattern', - }, - { - id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - name: 'control_2_index_pattern', - type: 'index-pattern', - }, - ], - type: 'visualization', - updated_at: '2021-08-05T12:43:41.128Z', - version: 'WzE3NiwxXQ==', - }, { attributes: { state: { @@ -1285,113 +1247,132 @@ export const getSavedObjects = (): SavedObject[] => [ version: 'WzIzMywxXQ==', }, { + id: '722b74f0-b882-11e8-a6d9-e546fe2bba5f', + type: 'dashboard', + namespaces: ['default'], + updated_at: '2022-09-26T17:19:19.470Z', + version: 'WzQ1MTgsMV0=', attributes: { + title: i18n.translate('home.sampleData.ecommerceSpec.revenueDashboardTitle', { + defaultMessage: '[eCommerce] Revenue Dashboard', + }), + hits: 0, description: i18n.translate('home.sampleData.ecommerceSpec.revenueDashboardDescription', { defaultMessage: 'Analyze mock eCommerce orders and revenue', }), - hits: 0, - kibanaSavedObjectMeta: { - searchSourceJSON: '{"query":{"language":"kuery","query":""},"filter":[]}', - }, - optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', panelsJSON: - '[{"version":"8.0.0-SNAPSHOT","type":"visualization","gridData":{"x":0,"y":22,"w":24,"h":10,"i":"5"},"panelIndex":"5","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_5"},{"version":"8.0.0-SNAPSHOT","type":"visualization","gridData":{"x":36,"y":15,"w":12,"h":7,"i":"7"},"panelIndex":"7","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_7"},{"version":"8.0.0-SNAPSHOT","type":"search","gridData":{"x":0,"y":55,"w":48,"h":18,"i":"10"},"panelIndex":"10","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_10"},{"version":"8.0.0-SNAPSHOT","type":"map","gridData":{"x":0,"y":32,"w":24,"h":14,"i":"11"},"panelIndex":"11","embeddableConfig":{"isLayerTOCOpen":false,"enhancements":{},"mapCenter":{"lat":45.88578,"lon":-15.07605,"zoom":2.11},"mapBuffer":{"minLon":-135,"minLat":0,"maxLon":90,"maxLat":66.51326},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_11"},{"version":"8.0.0-SNAPSHOT","type":"visualization","gridData":{"x":0,"y":0,"w":18,"h":7,"i":"a71cf076-6895-491c-8878-63592e429ed5"},"panelIndex":"a71cf076-6895-491c-8878-63592e429ed5","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_a71cf076-6895-491c-8878-63592e429ed5"},{"version":"8.0.0-SNAPSHOT","type":"visualization","gridData":{"x":18,"y":0,"w":30,"h":7,"i":"adc0a2f4-481c-45eb-b422-0ea59a3e5163"},"panelIndex":"adc0a2f4-481c-45eb-b422-0ea59a3e5163","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_adc0a2f4-481c-45eb-b422-0ea59a3e5163"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":0,"y":7,"w":24,"h":8,"i":"7077b79f-2a99-4fcb-bbd4-456982843278"},"panelIndex":"7077b79f-2a99-4fcb-bbd4-456982843278","embeddableConfig":{"enhancements":{},"hidePanelTitles":false},"title":"% of target revenue ($10k)","panelRefName":"panel_7077b79f-2a99-4fcb-bbd4-456982843278"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":24,"y":7,"w":12,"h":8,"i":"19a3c101-ad2e-4421-a71b-a4734ec1f03e"},"panelIndex":"19a3c101-ad2e-4421-a71b-a4734ec1f03e","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":36,"y":7,"w":12,"h":8,"i":"491469e7-7d24-4216-aeb3-bca00e5c8c1b"},"panelIndex":"491469e7-7d24-4216-aeb3-bca00e5c8c1b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":0,"y":15,"w":24,"h":7,"i":"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef"},"panelIndex":"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":24,"y":15,"w":12,"h":7,"i":"da51079b-952f-43dc-96e6-6f9415a3708b"},"panelIndex":"da51079b-952f-43dc-96e6-6f9415a3708b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_da51079b-952f-43dc-96e6-6f9415a3708b"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":24,"y":22,"w":24,"h":10,"i":"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b"},"panelIndex":"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":24,"y":32,"w":24,"h":14,"i":"bd330ede-2eef-4e2a-8100-22a21abf5038"},"panelIndex":"bd330ede-2eef-4e2a-8100-22a21abf5038","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_bd330ede-2eef-4e2a-8100-22a21abf5038"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":0,"y":46,"w":24,"h":9,"i":"b897d4be-cf83-46fb-a111-c7fbec9ef403"},"panelIndex":"b897d4be-cf83-46fb-a111-c7fbec9ef403","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"Top products this week","panelRefName":"panel_b897d4be-cf83-46fb-a111-c7fbec9ef403"},{"version":"8.0.0-SNAPSHOT","type":"lens","gridData":{"x":24,"y":46,"w":24,"h":9,"i":"e0f68f93-30f2-4da7-889a-6cd128a68d3f"},"panelIndex":"e0f68f93-30f2-4da7-889a-6cd128a68d3f","embeddableConfig":{"timeRange":{"from":"now-2w","to":"now-1w"},"hidePanelTitles":false,"enhancements":{}},"title":"Top products last week","panelRefName":"panel_e0f68f93-30f2-4da7-889a-6cd128a68d3f"}]', + '[{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":21,"w":24,"h":10,"i":"5"},"panelIndex":"5","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_5"},{"version":"8.6.0","type":"visualization","gridData":{"x":36,"y":7,"w":12,"h":7,"i":"7"},"panelIndex":"7","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_7"},{"version":"8.6.0","type":"search","gridData":{"x":0,"y":54,"w":48,"h":18,"i":"10"},"panelIndex":"10","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_10"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":31,"w":24,"h":14,"i":"11"},"panelIndex":"11","embeddableConfig":{"isLayerTOCOpen":false,"enhancements":{},"mapCenter":{"lat":45.88578,"lon":-15.07605,"zoom":2.11},"mapBuffer":{"minLon":-135,"minLat":0,"maxLon":90,"maxLat":66.51326},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_11"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":0,"w":24,"h":7,"i":"a71cf076-6895-491c-8878-63592e429ed5"},"panelIndex":"a71cf076-6895-491c-8878-63592e429ed5","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_a71cf076-6895-491c-8878-63592e429ed5"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":7,"w":12,"h":7,"i":"da51079b-952f-43dc-96e6-6f9415a3708b"},"panelIndex":"da51079b-952f-43dc-96e6-6f9415a3708b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_da51079b-952f-43dc-96e6-6f9415a3708b"},{"version":"8.6.0","type":"lens","gridData":{"x":36,"y":0,"w":12,"h":7,"i":"491469e7-7d24-4216-aeb3-bca00e5c8c1b"},"panelIndex":"491469e7-7d24-4216-aeb3-bca00e5c8c1b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":7,"w":24,"h":7,"i":"7077b79f-2a99-4fcb-bbd4-456982843278"},"panelIndex":"7077b79f-2a99-4fcb-bbd4-456982843278","embeddableConfig":{"enhancements":{},"hidePanelTitles":false},"title":"% of target revenue ($10k)","panelRefName":"panel_7077b79f-2a99-4fcb-bbd4-456982843278"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":0,"w":12,"h":7,"i":"19a3c101-ad2e-4421-a71b-a4734ec1f03e"},"panelIndex":"19a3c101-ad2e-4421-a71b-a4734ec1f03e","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":14,"w":24,"h":7,"i":"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef"},"panelIndex":"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":14,"w":24,"h":17,"i":"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b"},"panelIndex":"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":31,"w":24,"h":14,"i":"bd330ede-2eef-4e2a-8100-22a21abf5038"},"panelIndex":"bd330ede-2eef-4e2a-8100-22a21abf5038","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_bd330ede-2eef-4e2a-8100-22a21abf5038"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":45,"w":24,"h":9,"i":"b897d4be-cf83-46fb-a111-c7fbec9ef403"},"panelIndex":"b897d4be-cf83-46fb-a111-c7fbec9ef403","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"Top products this week","panelRefName":"panel_b897d4be-cf83-46fb-a111-c7fbec9ef403"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":45,"w":24,"h":9,"i":"e0f68f93-30f2-4da7-889a-6cd128a68d3f"},"panelIndex":"e0f68f93-30f2-4da7-889a-6cd128a68d3f","embeddableConfig":{"timeRange":{"from":"now-2w","to":"now-1w"},"hidePanelTitles":false,"enhancements":{}},"title":"Top products last week","panelRefName":"panel_e0f68f93-30f2-4da7-889a-6cd128a68d3f"}]', + optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', + version: 1, + timeRestore: true, + timeTo: 'now', + timeFrom: 'now-7d', refreshInterval: { pause: true, value: 0, }, - timeFrom: 'now-7d', - timeRestore: true, - timeTo: 'now', - title: i18n.translate('home.sampleData.ecommerceSpec.revenueDashboardTitle', { - defaultMessage: '[eCommerce] Revenue Dashboard', - }), - version: 1, - }, - coreMigrationVersion: '8.0.0', - id: '722b74f0-b882-11e8-a6d9-e546fe2bba5f', - migrationVersion: { - dashboard: '7.14.0', + controlGroupInput: { + controlStyle: 'oneLine', + chainingSystem: 'HIERARCHICAL', + panelsJSON: + '{"1ee1617f-fd8e-45e4-bc6a-d5736710ea20":{"order":0,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"title":"Manufacturer","fieldName":"manufacturer.keyword","parentFieldName":"manufacturer","id":"1ee1617f-fd8e-45e4-bc6a-d5736710ea20","enhancements":{}}},"afa9fa0f-a002-41a5-bab9-b738316d2590":{"order":1,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"title":"Category","fieldName":"category.keyword","parentFieldName":"category","id":"afa9fa0f-a002-41a5-bab9-b738316d2590","enhancements":{}}},"d3f766cb-5f96-4a12-8d3c-034e08be8855":{"order":2,"width":"small","grow":true,"type":"rangeSliderControl","explicitInput":{"title":"Quantity","fieldName":"total_quantity","id":"d3f766cb-5f96-4a12-8d3c-034e08be8855","enhancements":{}}}}', + ignoreParentSettingsJSON: + '{"ignoreFilters":false,"ignoreQuery":false,"ignoreTimerange":false,"ignoreValidations":false}', + }, + kibanaSavedObjectMeta: { + searchSourceJSON: '{"query":{"language":"kuery","query":""},"filter":[]}', + }, }, references: [ { - id: '45e07720-b890-11e8-a6d9-e546fe2bba5f', name: '5:panel_5', type: 'visualization', + id: '45e07720-b890-11e8-a6d9-e546fe2bba5f', }, { - id: 'b80e6540-b891-11e8-a6d9-e546fe2bba5f', name: '7:panel_7', type: 'visualization', + id: 'b80e6540-b891-11e8-a6d9-e546fe2bba5f', }, { - id: '3ba638e0-b894-11e8-a6d9-e546fe2bba5f', name: '10:panel_10', type: 'search', + id: '3ba638e0-b894-11e8-a6d9-e546fe2bba5f', }, { - id: '9c6f83f0-bb4d-11e8-9c84-77068524bcab', name: '11:panel_11', type: 'visualization', + id: '9c6f83f0-bb4d-11e8-9c84-77068524bcab', }, { - id: 'c00d1f90-f5ea-11eb-a78e-83aac3c38a60', name: 'a71cf076-6895-491c-8878-63592e429ed5:panel_a71cf076-6895-491c-8878-63592e429ed5', type: 'visualization', + id: 'c00d1f90-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'c3378480-f5ea-11eb-a78e-83aac3c38a60', - name: 'adc0a2f4-481c-45eb-b422-0ea59a3e5163:panel_adc0a2f4-481c-45eb-b422-0ea59a3e5163', - type: 'visualization', - }, - { - id: 'c762b7a0-f5ea-11eb-a78e-83aac3c38a60', - name: '7077b79f-2a99-4fcb-bbd4-456982843278:panel_7077b79f-2a99-4fcb-bbd4-456982843278', + name: 'da51079b-952f-43dc-96e6-6f9415a3708b:panel_da51079b-952f-43dc-96e6-6f9415a3708b', type: 'lens', + id: 'e3902840-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'ce02e260-f5ea-11eb-a78e-83aac3c38a60', - name: '19a3c101-ad2e-4421-a71b-a4734ec1f03e:panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e', + name: '491469e7-7d24-4216-aeb3-bca00e5c8c1b:panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b', type: 'lens', + id: 'd5f90030-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'd5f90030-f5ea-11eb-a78e-83aac3c38a60', - name: '491469e7-7d24-4216-aeb3-bca00e5c8c1b:panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b', + name: '7077b79f-2a99-4fcb-bbd4-456982843278:panel_7077b79f-2a99-4fcb-bbd4-456982843278', type: 'lens', + id: 'c762b7a0-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'dde978b0-f5ea-11eb-a78e-83aac3c38a60', - name: 'a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef:panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef', + name: '19a3c101-ad2e-4421-a71b-a4734ec1f03e:panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e', type: 'lens', + id: 'ce02e260-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'e3902840-f5ea-11eb-a78e-83aac3c38a60', - name: 'da51079b-952f-43dc-96e6-6f9415a3708b:panel_da51079b-952f-43dc-96e6-6f9415a3708b', + name: 'a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef:panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef', type: 'lens', + id: 'dde978b0-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'eddf7850-f5ea-11eb-a78e-83aac3c38a60', name: '64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b:panel_64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b', type: 'lens', + id: 'eddf7850-f5ea-11eb-a78e-83aac3c38a60', }, { - id: 'ff6a21b0-f5ea-11eb-a78e-83aac3c38a60', name: 'bd330ede-2eef-4e2a-8100-22a21abf5038:panel_bd330ede-2eef-4e2a-8100-22a21abf5038', type: 'lens', + id: 'ff6a21b0-f5ea-11eb-a78e-83aac3c38a60', }, { - id: '03071e90-f5eb-11eb-a78e-83aac3c38a60', name: 'b897d4be-cf83-46fb-a111-c7fbec9ef403:panel_b897d4be-cf83-46fb-a111-c7fbec9ef403', type: 'lens', + id: '03071e90-f5eb-11eb-a78e-83aac3c38a60', }, { - id: '06379e00-f5eb-11eb-a78e-83aac3c38a60', name: 'e0f68f93-30f2-4da7-889a-6cd128a68d3f:panel_e0f68f93-30f2-4da7-889a-6cd128a68d3f', type: 'lens', + id: '06379e00-f5eb-11eb-a78e-83aac3c38a60', + }, + { + name: 'controlGroup_1ee1617f-fd8e-45e4-bc6a-d5736710ea20:optionsListDataView', + type: 'index-pattern', + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + }, + { + name: 'controlGroup_afa9fa0f-a002-41a5-bab9-b738316d2590:optionsListDataView', + type: 'index-pattern', + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + }, + { + name: 'controlGroup_d3f766cb-5f96-4a12-8d3c-034e08be8855:rangeSliderDataView', + type: 'index-pattern', + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', }, ], - type: 'dashboard', - updated_at: '2021-08-05T12:45:46.525Z', - version: 'WzIzOSwxXQ==', + migrationVersion: { + dashboard: '8.5.0', + }, + coreMigrationVersion: '8.6.0', }, ]; diff --git a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts index 2e4413dcba0d3..db6d9ce049d23 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts @@ -220,62 +220,81 @@ export const getSavedObjects = (): SavedObject[] => [ { id: '7adfa750-4c81-11e8-b3d7-01146121b73d', type: 'dashboard', - updated_at: '2021-07-07T14:16:23.001Z', - version: '5', + namespaces: ['default'], + updated_at: '2022-09-26T17:13:00.935Z', + version: 'WzMzMTIsMV0=', + attributes: { + title: i18n.translate('home.sampleData.flightsSpec.globalFlightDashboardTitle', { + defaultMessage: '[Flights] Global Flight Dashboard', + }), + hits: 0, + description: i18n.translate('home.sampleData.flightsSpec.globalFlightDashboardDescription', { + defaultMessage: + 'Analyze mock flight data for ES-Air, Logstash Airways, Kibana Airlines and JetBeats', + }), + panelsJSON: + '[{"version":"8.6.0","type":"search","gridData":{"x":0,"y":69,"w":48,"h":15,"i":"4"},"panelIndex":"4","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_4"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":16,"w":24,"h":9,"i":"7"},"panelIndex":"7","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_7"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":58,"w":24,"h":11,"i":"10"},"panelIndex":"10","embeddableConfig":{"vis":{"colors":{"Count":"#1F78C1"},"legendOpen":false},"enhancements":{}},"panelRefName":"panel_10"},{"version":"8.6.0","type":"visualization","gridData":{"x":36,"y":58,"w":12,"h":11,"i":"21"},"panelIndex":"21","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_21"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":36,"w":24,"h":22,"i":"23"},"panelIndex":"23","embeddableConfig":{"isLayerTOCOpen":true,"enhancements":{},"mapCenter":{"lat":34.65823,"lon":-112.44472,"zoom":4.28},"mapBuffer":{"minLon":-135,"minLat":21.94305,"maxLon":-90,"maxLat":48.9225},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_23"},{"version":"8.6.0","type":"visualization","gridData":{"x":24,"y":36,"w":24,"h":22,"i":"31"},"panelIndex":"31","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_31"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":0,"w":24,"h":8,"i":"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9"},"panelIndex":"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9","embeddableConfig":{"savedVis":{"title":"[Flights] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":10,"openLinksInNewTab":true,"markdown":"## Sample Flight data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{}}},"hidePanelTitles":true,"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":0,"w":8,"h":8,"i":"392b4936-f753-47bc-a98d-a4e41a0a4cd4"},"panelIndex":"392b4936-f753-47bc-a98d-a4e41a0a4cd4","embeddableConfig":{"enhancements":{},"attributes":{"title":"[Flights] Total Flights","description":"","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"8fa993db-c147-4954-adf7-4ff264d42576":{"columns":{"81124c45-6ab6-42f4-8859-495d55eb8065":{"label":"Total flights","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true}},"columnOrder":["81124c45-6ab6-42f4-8859-495d55eb8065"],"incompleteColumns":{}}}}},"visualization":{"layerId":"8fa993db-c147-4954-adf7-4ff264d42576","accessor":"81124c45-6ab6-42f4-8859-495d55eb8065","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576","type":"index-pattern"}]},"hidePanelTitles":true}},{"version":"8.6.0","type":"lens","gridData":{"x":32,"y":0,"w":8,"h":4,"i":"9271deff-5a61-4665-83fc-f9fdc6bf0c0b"},"panelIndex":"9271deff-5a61-4665-83fc-f9fdc6bf0c0b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"FlightDelay : true","language":"kuery"},"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"location":{"min":0,"max":41},"text":"count(kql=\'FlightDelay : true\') / count()"}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ec":{"label":"Delayed","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'FlightDelay : true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"customLabel":true}},"columnOrder":["7e8fe9b1-f45c-4f3d-9561-30febcd357ec","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"7e8fe9b1-f45c-4f3d-9561-30febcd357ec","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":40,"y":0,"w":8,"h":4,"i":"aa591c29-1a31-4ee1-a71d-b829c06fd162"},"panelIndex":"aa591c29-1a31-4ee1-a71d-b829c06fd162","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"c7851241-5526-499a-960b-357af8c2ce5bX0":{"label":"Part of Delayed","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX1":{"label":"Part of Delayed","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","timeShift":"1w","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX2":{"label":"Part of Delayed","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"subtract","args":[{"type":"function","name":"divide","args":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"location":{"min":0,"max":28},"text":"count() / count(shift=\'1w\') "},1],"location":{"min":0,"max":31},"text":"count() / count(shift=\'1w\') - 1"}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5b":{"label":"Delayed vs 1 week earlier","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count() / count(shift=\'1w\') - 1","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX2"],"customLabel":true}},"columnOrder":["c7851241-5526-499a-960b-357af8c2ce5b","c7851241-5526-499a-960b-357af8c2ce5bX2","c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"c7851241-5526-499a-960b-357af8c2ce5b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"alias":null,"negate":false,"disabled":false,"type":"phrase","key":"FlightDelay","params":{"query":true},"index":"filter-index-pattern-0"},"query":{"match_phrase":{"FlightDelay":true}},"$state":{"store":"appState"}}]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"filter-index-pattern-0","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":32,"y":4,"w":8,"h":4,"i":"b766e3b8-4544-46ed-99e6-9ecc4847e2a2"},"panelIndex":"b766e3b8-4544-46ed-99e6-9ecc4847e2a2","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0":{"label":"Part of Cancelled","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"Cancelled : true","language":"kuery"},"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1":{"label":"Part of Cancelled","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2":{"label":"Part of Cancelled","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"location":{"min":0,"max":39},"text":"count(kql=\'Cancelled : true\') / count()"}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ec":{"label":"Cancelled","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'Cancelled : true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"customLabel":true}},"columnOrder":["7e8fe9b1-f45c-4f3d-9561-30febcd357ec","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"7e8fe9b1-f45c-4f3d-9561-30febcd357ec","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":40,"y":4,"w":8,"h":4,"i":"2e33ade5-96e5-40b4-b460-493e5d4fa834"},"panelIndex":"2e33ade5-96e5-40b4-b460-493e5d4fa834","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"c7851241-5526-499a-960b-357af8c2ce5bX0":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX1":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","timeShift":"1w","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX2":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"subtract","args":[{"type":"function","name":"divide","args":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"location":{"min":0,"max":28},"text":"count() / count(shift=\'1w\') "},1],"location":{"min":0,"max":31},"text":"count() / count(shift=\'1w\') - 1"}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5b":{"label":"Cancelled vs 1 week earlier","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count() / count(shift=\'1w\') - 1","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX2"],"customLabel":true}},"columnOrder":["c7851241-5526-499a-960b-357af8c2ce5b","c7851241-5526-499a-960b-357af8c2ce5bX2","c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"c7851241-5526-499a-960b-357af8c2ce5b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"alias":null,"negate":false,"disabled":false,"type":"phrase","key":"Cancelled","params":{"query":true},"index":"filter-index-pattern-0"},"query":{"match_phrase":{"Cancelled":true}},"$state":{"store":"appState"}}]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"filter-index-pattern-0","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":8,"w":24,"h":8,"i":"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65"},"panelIndex":"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsXY","state":{"datasourceStates":{"indexpattern":{"layers":{"03c34665-471c-49c7-acf1-5a11f517421c":{"columns":{"a5b94e30-4e77-4b0a-9187-1d8b13de1456":{"label":"timestamp","dataType":"date","operationType":"date_histogram","sourceField":"timestamp","isBucketed":true,"scale":"interval","params":{"interval":"auto","includeEmptyRows":true}},"3e267327-7317-4310-aee3-320e0f7c1e70":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___"}},"columnOrder":["a5b94e30-4e77-4b0a-9187-1d8b13de1456","3e267327-7317-4310-aee3-320e0f7c1e70"],"incompleteColumns":{}}}}},"visualization":{"legend":{"isVisible":true,"position":"right","legendSize":"auto"},"valueLabels":"hide","fittingFunction":"None","yLeftExtent":{"mode":"full"},"yRightExtent":{"mode":"custom","lowerBound":0,"upperBound":1},"axisTitlesVisibilitySettings":{"x":false,"yLeft":false,"yRight":true},"tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"preferredSeriesType":"bar_stacked","layers":[{"layerId":"03c34665-471c-49c7-acf1-5a11f517421c","accessors":["3e267327-7317-4310-aee3-320e0f7c1e70"],"position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"a5b94e30-4e77-4b0a-9187-1d8b13de1456","layerType":"data"}]},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c","type":"index-pattern"}]},"hidePanelTitles":false,"enhancements":{}},"title":"[Flights] Flight count"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":8,"w":24,"h":28,"i":"fb86b32f-fb7a-45cf-9511-f366fef51bbd"},"panelIndex":"fb86b32f-fb7a-45cf-9511-f366fef51bbd","embeddableConfig":{"attributes":{"title":"Cities by delay, cancellation","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"indexpattern":{"layers":{"f26e8f7a-4118-4227-bea0-5c02d8b270f7":{"columns":{"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0":{"label":"Top values of OriginCityName","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"OriginCityName","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"alphabetical","fallback":true},"orderDirection":"asc","otherBucket":true,"missingBucket":false}},"52f6f2e9-6242-4c44-be63-b799150e7e60X0":{"label":"Part of Delay %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"FlightDelay : true ","language":"kuery"},"customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60X1":{"label":"Part of Delay %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60X2":{"label":"Part of Delay %","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1"],"location":{"min":0,"max":42},"text":"count(kql=\'FlightDelay : true \') / count()"}},"references":["52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1"],"customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60":{"label":"Delay %","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'FlightDelay : true \') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":0}}},"references":["52f6f2e9-6242-4c44-be63-b799150e7e60X2"],"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0":{"label":"Part of Cancel %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"Cancelled: true","language":"kuery"},"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1":{"label":"Part of Cancel %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2":{"label":"Part of Cancel %","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1"],"location":{"min":0,"max":38},"text":"count(kql=\'Cancelled: true\') / count()"}},"references":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1"],"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6":{"label":"Cancel %","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'Cancelled: true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":0}}},"references":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2"],"customLabel":true}},"columnOrder":["3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0","52f6f2e9-6242-4c44-be63-b799150e7e60","52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1","52f6f2e9-6242-4c44-be63-b799150e7e60X2","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6"],"incompleteColumns":{}}}}},"visualization":{"columns":[{"isTransposed":false,"columnId":"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0","width":262.75},{"columnId":"52f6f2e9-6242-4c44-be63-b799150e7e60","isTransposed":false,"width":302.5,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#f7e0b8","stop":0.6},{"color":"#e7664c","stop":1}],"name":"custom","colorStops":[{"color":"#f7e0b8","stop":0.2},{"color":"#e7664c","stop":0.6}],"rangeType":"number","rangeMin":0.2,"rangeMax":0.6}},"alignment":"center"},{"columnId":"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6","isTransposed":false,"alignment":"center","colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#f7e0b8","stop":0.6},{"color":"#e7664c","stop":0.6666666666666666}],"rangeType":"number","name":"custom","colorStops":[{"color":"#f7e0b8","stop":0.2},{"color":"#e7664c","stop":0.6}],"rangeMin":0.2,"rangeMax":0.6}}}],"layerId":"f26e8f7a-4118-4227-bea0-5c02d8b270f7","sorting":{"columnId":"52f6f2e9-6242-4c44-be63-b799150e7e60","direction":"desc"},"layerType":"data","rowHeight":"single","rowHeightLines":1},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-f26e8f7a-4118-4227-bea0-5c02d8b270f7","type":"index-pattern"}]},"enhancements":{},"hidePanelTitles":false},"title":"[Flights] Most delayed cities"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":25,"w":24,"h":11,"i":"0cc42484-16f7-42ec-b38c-9bf8be69cde7"},"panelIndex":"0cc42484-16f7-42ec-b38c-9bf8be69cde7","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsXY","state":{"datasourceStates":{"indexpattern":{"layers":{"e80cc05e-c52a-4e5f-ac71-4b37274867f5":{"columns":{"caf7421e-93a3-439e-ab0a-fbdead93c21c":{"label":"Top values of FlightDelayType","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"FlightDelayType","isBucketed":true,"params":{"size":10,"orderBy":{"type":"column","columnId":"0233d302-ec81-4fbe-96cb-7fac84cf035c"},"orderDirection":"desc","otherBucket":true,"missingBucket":false}},"13ec79e3-9d73-4536-9056-3d92802bb30a":{"label":"timestamp","dataType":"date","operationType":"date_histogram","sourceField":"timestamp","isBucketed":true,"scale":"interval","params":{"interval":"auto","includeEmptyRows":true}},"0233d302-ec81-4fbe-96cb-7fac84cf035c":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___"}},"columnOrder":["caf7421e-93a3-439e-ab0a-fbdead93c21c","13ec79e3-9d73-4536-9056-3d92802bb30a","0233d302-ec81-4fbe-96cb-7fac84cf035c"],"incompleteColumns":{}}}}},"visualization":{"legend":{"isVisible":true,"position":"bottom","legendSize":"auto"},"valueLabels":"hide","fittingFunction":"None","yLeftExtent":{"mode":"full"},"yRightExtent":{"mode":"full"},"axisTitlesVisibilitySettings":{"x":true,"yLeft":false,"yRight":true},"tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"preferredSeriesType":"bar_percentage_stacked","layers":[{"layerId":"e80cc05e-c52a-4e5f-ac71-4b37274867f5","accessors":["0233d302-ec81-4fbe-96cb-7fac84cf035c"],"position":"top","seriesType":"bar_percentage_stacked","showGridlines":false,"palette":{"type":"palette","name":"cool"},"xAccessor":"13ec79e3-9d73-4536-9056-3d92802bb30a","splitAccessor":"caf7421e-93a3-439e-ab0a-fbdead93c21c","layerType":"data"}]},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5","type":"index-pattern"}]},"hidePanelTitles":false,"enhancements":{}},"title":"[Flights] Delay Type"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":58,"w":12,"h":11,"i":"5d53db36-2d5a-4adc-af7b-cec4c1a294e0"},"panelIndex":"5d53db36-2d5a-4adc-af7b-cec4c1a294e0","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsPie","state":{"datasourceStates":{"indexpattern":{"layers":{"0c8e136b-a822-4fb3-836d-e06cbea4eea4":{"columns":{"d1cee8bf-34cf-4141-99d7-ff043ee77b56":{"label":"Top values of FlightDelayType","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"FlightDelayType","isBucketed":true,"params":{"size":10,"orderBy":{"type":"column","columnId":"aa152ace-ee2d-447b-b86d-459bef4d7880"},"orderDirection":"desc","otherBucket":true,"missingBucket":false}},"aa152ace-ee2d-447b-b86d-459bef4d7880":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___"}},"columnOrder":["d1cee8bf-34cf-4141-99d7-ff043ee77b56","aa152ace-ee2d-447b-b86d-459bef4d7880"],"incompleteColumns":{}}}}},"visualization":{"shape":"pie","palette":{"type":"palette","name":"cool"},"layers":[{"layerId":"0c8e136b-a822-4fb3-836d-e06cbea4eea4","metric":"aa152ace-ee2d-447b-b86d-459bef4d7880","numberDisplay":"percent","categoryDisplay":"default","legendDisplay":"default","nestedLegend":false,"layerType":"data","legendSize":"auto","primaryGroups":["d1cee8bf-34cf-4141-99d7-ff043ee77b56"]}]},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"type":"phrase","key":"FlightDelayType","params":{"query":"No Delay"},"disabled":false,"negate":true,"alias":null,"index":"filter-index-pattern-0"},"query":{"match_phrase":{"FlightDelayType":"No Delay"}},"$state":{"store":"appState"}}]},"references":[{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4","type":"index-pattern"},{"id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"filter-index-pattern-0","type":"index-pattern"}]},"enhancements":{},"hidePanelTitles":false},"title":"[Flights] Delay Type"}]', + optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', + version: 1, + timeRestore: true, + timeTo: 'now', + timeFrom: 'now-7d', + refreshInterval: { + pause: true, + value: 0, + }, + controlGroupInput: { + controlStyle: 'oneLine', + chainingSystem: 'HIERARCHICAL', + panelsJSON: + '{"85b632c8-3b7b-408d-8223-b0caccf75bd3":{"order":0,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"title":"Origin City","fieldName":"OriginCityName","id":"85b632c8-3b7b-408d-8223-b0caccf75bd3","selectedOptions":[],"enhancements":{}}},"d4dc9d2b-5850-402a-921d-8a2cd0107156":{"order":1,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"title":"Destination City","fieldName":"DestCityName","id":"d4dc9d2b-5850-402a-921d-8a2cd0107156","enhancements":{}}},"bee4a16a-f5c1-40b2-887e-db1b9ad9e15f":{"order":2,"width":"small","grow":true,"type":"rangeSliderControl","explicitInput":{"title":"Average Ticket Price","fieldName":"AvgTicketPrice","id":"bee4a16a-f5c1-40b2-887e-db1b9ad9e15f","enhancements":{}}}}', + ignoreParentSettingsJSON: + '{"ignoreFilters":false,"ignoreQuery":false,"ignoreTimerange":false,"ignoreValidations":false}', + }, + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"query":{"language":"kuery","query":""},"highlightAll":true,"version":true,"filter":[]}', + }, + }, references: [ { - id: '571aaf70-4c88-11e8-b3d7-01146121b73d', name: '4:panel_4', type: 'search', + id: '571aaf70-4c88-11e8-b3d7-01146121b73d', }, { - id: 'bcb63b50-4c89-11e8-b3d7-01146121b73d', name: '7:panel_7', type: 'visualization', + id: 'bcb63b50-4c89-11e8-b3d7-01146121b73d', }, { - id: '9886b410-4c8b-11e8-b3d7-01146121b73d', name: '10:panel_10', type: 'visualization', + id: '9886b410-4c8b-11e8-b3d7-01146121b73d', }, { - id: '293b5a30-4c8f-11e8-b3d7-01146121b73d', name: '21:panel_21', type: 'visualization', + id: '293b5a30-4c8f-11e8-b3d7-01146121b73d', }, { - id: '334084f0-52fd-11e8-a160-89cc2ad9e8e2', name: '23:panel_23', type: 'visualization', + id: '334084f0-52fd-11e8-a160-89cc2ad9e8e2', }, { - id: 'ed78a660-53a0-11e8-acbd-0be0ad9d822b', name: '31:panel_31', type: 'visualization', + id: 'ed78a660-53a0-11e8-acbd-0be0ad9d822b', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: 'aa810aa2-29c9-4a75-b39e-f4f267de1732:control_aa810aa2-29c9-4a75-b39e-f4f267de1732_0_index_pattern', - type: 'index-pattern', - }, - { - id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: 'aa810aa2-29c9-4a75-b39e-f4f267de1732:control_aa810aa2-29c9-4a75-b39e-f4f267de1732_1_index_pattern', - type: 'index-pattern', - }, - { - id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: 'aa810aa2-29c9-4a75-b39e-f4f267de1732:control_aa810aa2-29c9-4a75-b39e-f4f267de1732_2_index_pattern', - type: 'index-pattern', - }, - { - id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '086ac2e9-dd16-4b45-92b8-1e43ff7e3f65:indexpattern-datasource-current-indexpattern', + name: '392b4936-f753-47bc-a98d-a4e41a0a4cd4:indexpattern-datasource-current-indexpattern', type: 'index-pattern', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '086ac2e9-dd16-4b45-92b8-1e43ff7e3f65:indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c', + name: '392b4936-f753-47bc-a98d-a4e41a0a4cd4:indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576', type: 'index-pattern', }, { @@ -328,6 +347,16 @@ export const getSavedObjects = (): SavedObject[] => [ name: '2e33ade5-96e5-40b4-b460-493e5d4fa834:filter-index-pattern-0', type: 'index-pattern', }, + { + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + name: '086ac2e9-dd16-4b45-92b8-1e43ff7e3f65:indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + name: '086ac2e9-dd16-4b45-92b8-1e43ff7e3f65:indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c', + type: 'index-pattern', + }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', name: 'fb86b32f-fb7a-45cf-9511-f366fef51bbd:indexpattern-datasource-current-indexpattern', @@ -340,67 +369,48 @@ export const getSavedObjects = (): SavedObject[] => [ }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:indexpattern-datasource-current-indexpattern', + name: '0cc42484-16f7-42ec-b38c-9bf8be69cde7:indexpattern-datasource-current-indexpattern', type: 'index-pattern', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4', + name: '0cc42484-16f7-42ec-b38c-9bf8be69cde7:indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5', type: 'index-pattern', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:filter-index-pattern-0', + name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:indexpattern-datasource-current-indexpattern', type: 'index-pattern', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '0cc42484-16f7-42ec-b38c-9bf8be69cde7:indexpattern-datasource-current-indexpattern', + name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4', type: 'index-pattern', }, { id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '0cc42484-16f7-42ec-b38c-9bf8be69cde7:indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5', + name: '5d53db36-2d5a-4adc-af7b-cec4c1a294e0:filter-index-pattern-0', type: 'index-pattern', }, { - id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '392b4936-f753-47bc-a98d-a4e41a0a4cd4:indexpattern-datasource-current-indexpattern', + name: 'controlGroup_85b632c8-3b7b-408d-8223-b0caccf75bd3:optionsListDataView', type: 'index-pattern', + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', }, { + name: 'controlGroup_d4dc9d2b-5850-402a-921d-8a2cd0107156:optionsListDataView', + type: 'index-pattern', id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', - name: '392b4936-f753-47bc-a98d-a4e41a0a4cd4:indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576', + }, + { + name: 'controlGroup_bee4a16a-f5c1-40b2-887e-db1b9ad9e15f:rangeSliderDataView', type: 'index-pattern', + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', }, ], migrationVersion: { - dashboard: '7.14.0', - }, - attributes: { - title: i18n.translate('home.sampleData.flightsSpec.globalFlightDashboardTitle', { - defaultMessage: '[Flights] Global Flight Dashboard', - }), - hits: 0, - description: i18n.translate('home.sampleData.flightsSpec.globalFlightDashboardDescription', { - defaultMessage: - 'Analyze mock flight data for ES-Air, Logstash Airways, Kibana Airlines and JetBeats', - }), - panelsJSON: - '[{"version":"7.14.0","type":"search","gridData":{"x":0,"y":68,"w":48,"h":15,"i":"4"},"panelIndex":"4","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_4"},{"version":"7.14.0","type":"visualization","gridData":{"x":0,"y":15,"w":24,"h":9,"i":"7"},"panelIndex":"7","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_7"},{"version":"7.14.0","type":"visualization","gridData":{"x":0,"y":57,"w":24,"h":11,"i":"10"},"panelIndex":"10","embeddableConfig":{"vis":{"colors":{"Count":"#1F78C1"},"legendOpen":false},"enhancements":{}},"panelRefName":"panel_10"},{"version":"7.14.0","type":"visualization","gridData":{"x":36,"y":57,"w":12,"h":11,"i":"21"},"panelIndex":"21","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_21"},{"version":"7.14.0","type":"map","gridData":{"x":0,"y":35,"w":24,"h":22,"i":"23"},"panelIndex":"23","embeddableConfig":{"isLayerTOCOpen":true,"enhancements":{},"mapCenter":{"lat":34.65823,"lon":-112.44472,"zoom":4.28},"mapBuffer":{"minLon":-135,"minLat":21.94305,"maxLon":-90,"maxLat":48.9225},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_23"},{"version":"7.14.0","type":"visualization","gridData":{"x":24,"y":35,"w":24,"h":22,"i":"31"},"panelIndex":"31","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_31"},{"version":"7.14.0","type":"visualization","gridData":{"x":0,"y":0,"w":32,"h":7,"i":"aa810aa2-29c9-4a75-b39e-f4f267de1732"},"panelIndex":"aa810aa2-29c9-4a75-b39e-f4f267de1732","embeddableConfig":{"savedVis":{"title":"[Flights] Controls","description":"","type":"input_control_vis","params":{"controls":[{"id":"1525098134264","fieldName":"OriginCityName","parent":"","label":"Origin City","type":"list","options":{"type":"terms","multiselect":true,"size":100,"order":"desc"},"indexPatternRefName":"control_aa810aa2-29c9-4a75-b39e-f4f267de1732_0_index_pattern"},{"id":"1525099277699","fieldName":"DestCityName","parent":"1525098134264","label":"Destination City","type":"list","options":{"type":"terms","multiselect":true,"size":100,"order":"desc"},"indexPatternRefName":"control_aa810aa2-29c9-4a75-b39e-f4f267de1732_1_index_pattern"},{"id":"1525099307278","fieldName":"AvgTicketPrice","parent":"","label":"Average Ticket Price","type":"range","options":{"decimalPlaces":0,"step":10},"indexPatternRefName":"control_aa810aa2-29c9-4a75-b39e-f4f267de1732_2_index_pattern"}],"updateFiltersOnChange":false,"useTimeFilter":true,"pinFilters":false},"uiState":{},"data":{"aggs":[],"searchSource":{}}},"hidePanelTitles":true,"enhancements":{}}},{"version":"7.14.0","type":"visualization","gridData":{"x":32,"y":0,"w":16,"h":7,"i":"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9"},"panelIndex":"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9","embeddableConfig":{"savedVis":{"title":"[Flights] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":10,"openLinksInNewTab":true,"markdown":"## Sample Flight data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{}}},"hidePanelTitles":true,"enhancements":{}}},{"version":"7.14.0","type":"lens","gridData":{"x":0,"y":7,"w":24,"h":8,"i":"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65"},"panelIndex":"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsXY","state":{"datasourceStates":{"indexpattern":{"layers":{"03c34665-471c-49c7-acf1-5a11f517421c":{"columns":{"a5b94e30-4e77-4b0a-9187-1d8b13de1456":{"label":"timestamp","dataType":"date","operationType":"date_histogram","sourceField":"timestamp","isBucketed":true,"scale":"interval","params":{"interval":"auto"}},"3e267327-7317-4310-aee3-320e0f7c1e70":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records"}},"columnOrder":["a5b94e30-4e77-4b0a-9187-1d8b13de1456","3e267327-7317-4310-aee3-320e0f7c1e70"],"incompleteColumns":{}}}}},"visualization":{"legend":{"isVisible":true,"position":"right"},"valueLabels":"hide","fittingFunction":"None","yLeftExtent":{"mode":"full"},"yRightExtent":{"mode":"custom","lowerBound":0,"upperBound":1},"axisTitlesVisibilitySettings":{"x":false,"yLeft":false,"yRight":true},"tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"preferredSeriesType":"bar_stacked","layers":[{"layerId":"03c34665-471c-49c7-acf1-5a11f517421c","accessors":["3e267327-7317-4310-aee3-320e0f7c1e70"],"position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"a5b94e30-4e77-4b0a-9187-1d8b13de1456"}]},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c"}]},"hidePanelTitles":false,"enhancements":{}},"title":"[Flights] Flight count"},{"version":"7.14.0","type":"lens","gridData":{"x":24,"y":7,"w":8,"h":8,"i":"392b4936-f753-47bc-a98d-a4e41a0a4cd4"},"panelIndex":"392b4936-f753-47bc-a98d-a4e41a0a4cd4","embeddableConfig":{"enhancements":{},"attributes":{"title":"[Flights] Total Flights","description":"","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"8fa993db-c147-4954-adf7-4ff264d42576":{"columns":{"81124c45-6ab6-42f4-8859-495d55eb8065":{"label":"Total flights","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true}},"columnOrder":["81124c45-6ab6-42f4-8859-495d55eb8065"],"incompleteColumns":{}}}}},"visualization":{"layerId":"8fa993db-c147-4954-adf7-4ff264d42576","accessor":"81124c45-6ab6-42f4-8859-495d55eb8065"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576"}]},"hidePanelTitles":true}},{"version":"7.14.0","type":"lens","gridData":{"x":32,"y":7,"w":8,"h":4,"i":"9271deff-5a61-4665-83fc-f9fdc6bf0c0b"},"panelIndex":"9271deff-5a61-4665-83fc-f9fdc6bf0c0b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"FlightDelay : true","language":"kuery"},"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2":{"label":"Part of count(kql=\'FlightDelay : true\') / count()","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"location":{"min":0,"max":41},"text":"count(kql=\'FlightDelay : true\') / count()"}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ec":{"label":"Delayed","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'FlightDelay : true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"customLabel":true}},"columnOrder":["7e8fe9b1-f45c-4f3d-9561-30febcd357ec","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"7e8fe9b1-f45c-4f3d-9561-30febcd357ec"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317"}]},"enhancements":{}}},{"version":"7.14.0","type":"lens","gridData":{"x":40,"y":7,"w":8,"h":4,"i":"aa591c29-1a31-4ee1-a71d-b829c06fd162"},"panelIndex":"aa591c29-1a31-4ee1-a71d-b829c06fd162","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"c7851241-5526-499a-960b-357af8c2ce5bX0":{"label":"Part of Delayed","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX1":{"label":"Part of Delayed","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","timeShift":"1w","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX2":{"label":"Part of Delayed","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"subtract","args":[{"type":"function","name":"divide","args":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"location":{"min":0,"max":28},"text":"count() / count(shift=\'1w\') "},1],"location":{"min":0,"max":31},"text":"count() / count(shift=\'1w\') - 1"}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5b":{"label":"Delayed vs 1 week earlier","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count() / count(shift=\'1w\') - 1","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX2"],"customLabel":true}},"columnOrder":["c7851241-5526-499a-960b-357af8c2ce5b","c7851241-5526-499a-960b-357af8c2ce5bX2","c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"c7851241-5526-499a-960b-357af8c2ce5b"},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"alias":null,"negate":false,"disabled":false,"type":"phrase","key":"FlightDelay","params":{"query":true},"indexRefName":"filter-index-pattern-0"},"query":{"match_phrase":{"FlightDelay":true}},"$state":{"store":"appState"}}]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317"},{"name":"filter-index-pattern-0","type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d"}]},"enhancements":{}}},{"version":"7.14.0","type":"lens","gridData":{"x":32,"y":11,"w":8,"h":4,"i":"b766e3b8-4544-46ed-99e6-9ecc4847e2a2"},"panelIndex":"b766e3b8-4544-46ed-99e6-9ecc4847e2a2","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0":{"label":"Part of Cancelled","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"Cancelled : true","language":"kuery"},"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1":{"label":"Part of Cancelled","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2":{"label":"Part of Cancelled","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"location":{"min":0,"max":39},"text":"count(kql=\'Cancelled : true\') / count()"}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1"],"customLabel":true},"7e8fe9b1-f45c-4f3d-9561-30febcd357ec":{"label":"Cancelled","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'Cancelled : true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"customLabel":true}},"columnOrder":["7e8fe9b1-f45c-4f3d-9561-30febcd357ec","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1","7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"7e8fe9b1-f45c-4f3d-9561-30febcd357ec"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317"}]},"enhancements":{}}},{"version":"7.14.0","type":"lens","gridData":{"x":40,"y":11,"w":8,"h":4,"i":"2e33ade5-96e5-40b4-b460-493e5d4fa834"},"panelIndex":"2e33ade5-96e5-40b4-b460-493e5d4fa834","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"b4712d43-1e84-4f5b-878d-8e38ba748317":{"columns":{"c7851241-5526-499a-960b-357af8c2ce5bX0":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX1":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","timeShift":"1w","customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5bX2":{"label":"Part of Delayed vs 1 week earlier","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"subtract","args":[{"type":"function","name":"divide","args":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"location":{"min":0,"max":28},"text":"count() / count(shift=\'1w\') "},1],"location":{"min":0,"max":31},"text":"count() / count(shift=\'1w\') - 1"}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"customLabel":true},"c7851241-5526-499a-960b-357af8c2ce5b":{"label":"Cancelled vs 1 week earlier","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count() / count(shift=\'1w\') - 1","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["c7851241-5526-499a-960b-357af8c2ce5bX2"],"customLabel":true}},"columnOrder":["c7851241-5526-499a-960b-357af8c2ce5b","c7851241-5526-499a-960b-357af8c2ce5bX2","c7851241-5526-499a-960b-357af8c2ce5bX0","c7851241-5526-499a-960b-357af8c2ce5bX1"],"incompleteColumns":{}}}}},"visualization":{"layerId":"b4712d43-1e84-4f5b-878d-8e38ba748317","accessor":"c7851241-5526-499a-960b-357af8c2ce5b"},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"alias":null,"negate":false,"disabled":false,"type":"phrase","key":"Cancelled","params":{"query":true},"indexRefName":"filter-index-pattern-0"},"query":{"match_phrase":{"Cancelled":true}},"$state":{"store":"appState"}}]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317"},{"name":"filter-index-pattern-0","type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d"}]},"enhancements":{}}},{"version":"7.14.0","type":"lens","gridData":{"x":24,"y":15,"w":24,"h":20,"i":"fb86b32f-fb7a-45cf-9511-f366fef51bbd"},"panelIndex":"fb86b32f-fb7a-45cf-9511-f366fef51bbd","embeddableConfig":{"attributes":{"title":"Cities by delay, cancellation","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"indexpattern":{"layers":{"f26e8f7a-4118-4227-bea0-5c02d8b270f7":{"columns":{"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0":{"label":"Top values of OriginCityName","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"OriginCityName","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"alphabetical","fallback":true},"orderDirection":"asc","otherBucket":true,"missingBucket":false}},"52f6f2e9-6242-4c44-be63-b799150e7e60X0":{"label":"Part of Delay %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"FlightDelay : true ","language":"kuery"},"customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60X1":{"label":"Part of Delay %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60X2":{"label":"Part of Delay %","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1"],"location":{"min":0,"max":42},"text":"count(kql=\'FlightDelay : true \') / count()"}},"references":["52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1"],"customLabel":true},"52f6f2e9-6242-4c44-be63-b799150e7e60":{"label":"Delay %","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'FlightDelay : true \') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":0}}},"references":["52f6f2e9-6242-4c44-be63-b799150e7e60X2"],"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0":{"label":"Part of Cancel %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"Cancelled: true","language":"kuery"},"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1":{"label":"Part of Cancel %","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2":{"label":"Part of Cancel %","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1"],"location":{"min":0,"max":38},"text":"count(kql=\'Cancelled: true\') / count()"}},"references":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1"],"customLabel":true},"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6":{"label":"Cancel %","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'Cancelled: true\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":0}}},"references":["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2"],"customLabel":true}},"columnOrder":["3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0","52f6f2e9-6242-4c44-be63-b799150e7e60","52f6f2e9-6242-4c44-be63-b799150e7e60X0","52f6f2e9-6242-4c44-be63-b799150e7e60X1","52f6f2e9-6242-4c44-be63-b799150e7e60X2","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2","7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6"],"incompleteColumns":{}}}}},"visualization":{"columns":[{"isTransposed":false,"columnId":"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0","width":262.75},{"columnId":"52f6f2e9-6242-4c44-be63-b799150e7e60","isTransposed":false,"width":302.5,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#f7e0b8","stop":0.6},{"color":"#e7664c","stop":1}],"name":"custom","colorStops":[{"color":"#f7e0b8","stop":0.2},{"color":"#e7664c","stop":0.6}],"rangeType":"number","rangeMin":0.2,"rangeMax":0.6}},"alignment":"center"},{"columnId":"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6","isTransposed":false,"alignment":"center","colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#f7e0b8","stop":0.6},{"color":"#e7664c","stop":0.6666666666666666}],"rangeType":"number","name":"custom","colorStops":[{"color":"#f7e0b8","stop":0.2},{"color":"#e7664c","stop":0.6}],"rangeMin":0.2,"rangeMax":0.6}}}],"layerId":"f26e8f7a-4118-4227-bea0-5c02d8b270f7","sorting":{"columnId":"52f6f2e9-6242-4c44-be63-b799150e7e60","direction":"desc"}},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-f26e8f7a-4118-4227-bea0-5c02d8b270f7"}]},"enhancements":{},"hidePanelTitles":false},"title":"[Flights] Most delayed cities"},{"version":"7.14.0","type":"lens","gridData":{"x":0,"y":24,"w":24,"h":11,"i":"0cc42484-16f7-42ec-b38c-9bf8be69cde7"},"panelIndex":"0cc42484-16f7-42ec-b38c-9bf8be69cde7","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsXY","state":{"datasourceStates":{"indexpattern":{"layers":{"e80cc05e-c52a-4e5f-ac71-4b37274867f5":{"columns":{"caf7421e-93a3-439e-ab0a-fbdead93c21c":{"label":"Top values of FlightDelayType","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"FlightDelayType","isBucketed":true,"params":{"size":10,"orderBy":{"type":"column","columnId":"0233d302-ec81-4fbe-96cb-7fac84cf035c"},"orderDirection":"desc","otherBucket":true,"missingBucket":false}},"13ec79e3-9d73-4536-9056-3d92802bb30a":{"label":"timestamp","dataType":"date","operationType":"date_histogram","sourceField":"timestamp","isBucketed":true,"scale":"interval","params":{"interval":"auto"}},"0233d302-ec81-4fbe-96cb-7fac84cf035c":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records"}},"columnOrder":["caf7421e-93a3-439e-ab0a-fbdead93c21c","13ec79e3-9d73-4536-9056-3d92802bb30a","0233d302-ec81-4fbe-96cb-7fac84cf035c"],"incompleteColumns":{}}}}},"visualization":{"legend":{"isVisible":true,"position":"bottom"},"valueLabels":"hide","fittingFunction":"None","yLeftExtent":{"mode":"full"},"yRightExtent":{"mode":"full"},"axisTitlesVisibilitySettings":{"x":true,"yLeft":false,"yRight":true},"tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"preferredSeriesType":"bar_percentage_stacked","layers":[{"layerId":"e80cc05e-c52a-4e5f-ac71-4b37274867f5","accessors":["0233d302-ec81-4fbe-96cb-7fac84cf035c"],"position":"top","seriesType":"bar_percentage_stacked","showGridlines":false,"palette":{"type":"palette","name":"cool"},"xAccessor":"13ec79e3-9d73-4536-9056-3d92802bb30a","splitAccessor":"caf7421e-93a3-439e-ab0a-fbdead93c21c"}]},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5"}]},"hidePanelTitles":false,"enhancements":{}},"title":"[Flights] Delay Type"},{"version":"7.14.0","type":"lens","gridData":{"x":24,"y":57,"w":12,"h":11,"i":"5d53db36-2d5a-4adc-af7b-cec4c1a294e0"},"panelIndex":"5d53db36-2d5a-4adc-af7b-cec4c1a294e0","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsPie","state":{"datasourceStates":{"indexpattern":{"layers":{"0c8e136b-a822-4fb3-836d-e06cbea4eea4":{"columns":{"d1cee8bf-34cf-4141-99d7-ff043ee77b56":{"label":"Top values of FlightDelayType","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"FlightDelayType","isBucketed":true,"params":{"size":10,"orderBy":{"type":"column","columnId":"aa152ace-ee2d-447b-b86d-459bef4d7880"},"orderDirection":"desc","otherBucket":true,"missingBucket":false}},"aa152ace-ee2d-447b-b86d-459bef4d7880":{"label":"Count of records","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records"}},"columnOrder":["d1cee8bf-34cf-4141-99d7-ff043ee77b56","aa152ace-ee2d-447b-b86d-459bef4d7880"],"incompleteColumns":{}}}}},"visualization":{"shape":"pie","palette":{"type":"palette","name":"cool"},"layers":[{"layerId":"0c8e136b-a822-4fb3-836d-e06cbea4eea4","groups":["d1cee8bf-34cf-4141-99d7-ff043ee77b56"],"metric":"aa152ace-ee2d-447b-b86d-459bef4d7880","numberDisplay":"percent","categoryDisplay":"default","legendDisplay":"default","nestedLegend":false}]},"query":{"query":"","language":"kuery"},"filters":[{"meta":{"type":"phrase","key":"FlightDelayType","params":{"query":"No Delay"},"disabled":false,"negate":true,"alias":null,"indexRefName":"filter-index-pattern-0"},"query":{"match_phrase":{"FlightDelayType":"No Delay"}},"$state":{"store":"appState"}}]},"references":[{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d","name":"indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4"},{"name":"filter-index-pattern-0","type":"index-pattern","id":"d3d7af60-4c81-11e8-b3d7-01146121b73d"}]},"enhancements":{},"hidePanelTitles":false},"title":"[Flights] Delay Type"}]', - optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', - version: 1, - timeRestore: true, - timeTo: 'now', - timeFrom: 'now-7d', - refreshInterval: { - pause: true, - value: 0, - }, - kibanaSavedObjectMeta: { - searchSourceJSON: - '{"query":{"language":"kuery","query":""},"filter":[],"highlightAll":true,"version":true}', - }, + dashboard: '8.5.0', }, + coreMigrationVersion: '8.6.0', }, ]; diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts index b5f57e0edb2e6..5de45e08d0fec 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts @@ -381,48 +381,66 @@ export const getSavedObjects = (): SavedObject[] => [ { id: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b', type: 'dashboard', - updated_at: '2021-10-28T15:07:36.622Z', - version: '3', + namespaces: ['default'], + updated_at: '2022-09-26T16:24:51.698Z', + version: 'WzE1NTIsMV0=', + attributes: { + title: i18n.translate('home.sampleData.logsSpec.webTrafficTitle', { + defaultMessage: '[Logs] Web Traffic', + }), + hits: 0, + description: i18n.translate('home.sampleData.logsSpec.webTrafficDescription', { + defaultMessage: "Analyze mock web traffic log data for Elastic's website", + }), + panelsJSON: + '[{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":14,"w":24,"h":18,"i":"4"},"panelIndex":"4","embeddableConfig":{"isLayerTOCOpen":false,"enhancements":{},"mapCenter":{"lat":42.16337,"lon":-88.92107,"zoom":3.64},"mapBuffer":{"minLon":-112.5,"minLat":21.94305,"maxLon":-45,"maxLat":55.77657},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_4"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":47,"w":24,"h":13,"i":"9"},"panelIndex":"9","embeddableConfig":{"mapCenter":[36.8092847020594,-96.94335937500001],"vis":{"params":{"sort":{"columnIndex":null,"direction":null}}},"enhancements":{}},"panelRefName":"panel_9"},{"version":"8.6.0","type":"visualization","gridData":{"x":36,"y":0,"w":12,"h":7,"i":"11"},"panelIndex":"11","embeddableConfig":{"vis":{"colors":{"0 - 500":"#BF1B00","1000 - 1500":"#7EB26D","500 - 1000":"#F2C96D"},"defaultColors":{"0 - 500":"rgb(165,0,38)","1000 - 1500":"rgb(0,104,55)","500 - 1000":"rgb(255,255,190)"},"legendOpen":false},"enhancements":{},"hidePanelTitles":true},"title":"","panelRefName":"panel_11"},{"version":"8.6.0","type":"visualization","gridData":{"x":24,"y":14,"w":24,"h":33,"i":"14"},"panelIndex":"14","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_14"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":7,"w":24,"h":7,"i":"15"},"panelIndex":"15","embeddableConfig":{"enhancements":{"dynamicActions":{"events":[]}}},"panelRefName":"panel_15"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":0,"w":24,"h":7,"i":"343f0bef-0b19-452e-b1c8-59beb18b6f0c"},"panelIndex":"343f0bef-0b19-452e-b1c8-59beb18b6f0c","embeddableConfig":{"savedVis":{"title":"[Logs] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":12,"openLinksInNewTab":true,"markdown":"## Sample Logs Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{},"hidePanelTitles":true}},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":0,"w":12,"h":7,"i":"bb94016e-f4a6-49ca-87a9-296a2869d570"},"panelIndex":"bb94016e-f4a6-49ca-87a9-296a2869d570","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"483defd2-775b-4a62-bdef-496c819bb8ed":{"columns":{"37430d12-7452-4cc9-b035-5cfd4061edf0":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true}},"columnOrder":["37430d12-7452-4cc9-b035-5cfd4061edf0"],"incompleteColumns":{}}}}},"visualization":{"layerId":"483defd2-775b-4a62-bdef-496c819bb8ed","accessor":"37430d12-7452-4cc9-b035-5cfd4061edf0","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-483defd2-775b-4a62-bdef-496c819bb8ed","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":7,"w":12,"h":7,"i":"01d8e435-91c0-484f-a11e-856747050b0a"},"panelIndex":"01d8e435-91c0-484f-a11e-856747050b0a","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":36,"y":7,"w":12,"h":7,"i":"8c1456d4-1993-4ba2-b701-04aca02c9fef"},"panelIndex":"8c1456d4-1993-4ba2-b701-04aca02c9fef","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":32,"w":24,"h":15,"i":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},"panelIndex":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce","embeddableConfig":{"savedVis":{"title":"","description":"","type":"vega","params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logs\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.src\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: true\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: ordinal\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: reds\\n }\\n }\\n }\\n}\\n"},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{}},"panelRefName":"panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":47,"w":24,"h":13,"i":"cbca842c-b9fa-4523-9ce0-14e350866e33"},"panelIndex":"cbca842c-b9fa-4523-9ce0-14e350866e33","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"[Logs] Bytes distribution","panelRefName":"panel_cbca842c-b9fa-4523-9ce0-14e350866e33"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":60,"w":48,"h":19,"i":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b"},"panelIndex":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"indexpattern":{"layers":{"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0":{"columns":{"42783ad7-dbcf-4310-bc06-f21f4eaaac96":{"label":"URL","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"url.keyword","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"column","columnId":"f7835375-4d5b-4839-95ea-41928192a319"},"orderDirection":"desc","otherBucket":true,"missingBucket":false},"customLabel":true},"f7835375-4d5b-4839-95ea-41928192a319":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX0":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX1":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX2":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1da":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX2"],"customLabel":true},"791d5a5b-a7ba-4e9e-b533-51b33c7d7747":{"label":"Unique","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"customLabel":true},"611e3509-e834-4fdd-b573-44e959e95d27":{"label":"95th percentile of bytes","dataType":"number","operationType":"percentile","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"percentile":95,"format":{"id":"bytes","params":{"decimals":0}}}},"9f79ecca-123f-4098-a658-6b0e998da003":{"label":"Median of bytes","dataType":"number","operationType":"median","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"format":{"id":"bytes","params":{"decimals":0}}}},"491285fd-0196-402c-9b7f-4660fdc1c22aX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22a":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX2"],"customLabel":true}},"columnOrder":["42783ad7-dbcf-4310-bc06-f21f4eaaac96","f7835375-4d5b-4839-95ea-41928192a319","791d5a5b-a7ba-4e9e-b533-51b33c7d7747","07fc84ca-4147-4ba9-879e-d1b4e086e1da","491285fd-0196-402c-9b7f-4660fdc1c22a","491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1","491285fd-0196-402c-9b7f-4660fdc1c22aX2","07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1","07fc84ca-4147-4ba9-879e-d1b4e086e1daX2","611e3509-e834-4fdd-b573-44e959e95d27","9f79ecca-123f-4098-a658-6b0e998da003"],"incompleteColumns":{}}}}},"visualization":{"layerId":"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","columns":[{"columnId":"42783ad7-dbcf-4310-bc06-f21f4eaaac96","width":650.6666666666666},{"columnId":"f7835375-4d5b-4839-95ea-41928192a319"},{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","isTransposed":false,"width":81.66666666666669,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#CC5642","stop":1}],"rangeType":"number","name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#CC5642","stop":0.1}],"rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"07fc84ca-4147-4ba9-879e-d1b4e086e1da","isTransposed":false,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#cc5642","stop":1.1}],"name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#cc5642","stop":0.1}],"rangeType":"number","rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"791d5a5b-a7ba-4e9e-b533-51b33c7d7747","isTransposed":false},{"columnId":"611e3509-e834-4fdd-b573-44e959e95d27","isTransposed":false},{"columnId":"9f79ecca-123f-4098-a658-6b0e998da003","isTransposed":false}],"sorting":{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","direction":"desc"},"layerType":"data","rowHeight":"single","rowHeightLines":1},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","type":"index-pattern"}]},"enhancements":{"dynamicActions":{"events":[]}},"hidePanelTitles":false},"title":"[Logs] Errors by host"}]', + optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', + version: 2, + timeRestore: true, + timeTo: 'now', + timeFrom: 'now-7d', + refreshInterval: { + pause: false, + value: 900000, + }, + controlGroupInput: { + controlStyle: 'oneLine', + chainingSystem: 'HIERARCHICAL', + panelsJSON: + '{"612f8db8-9ba9-41cf-a809-d133fe9b83a8":{"order":0,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"fieldName":"geo.src","title":"Source Country","id":"612f8db8-9ba9-41cf-a809-d133fe9b83a8","enhancements":{}}},"9807212f-5078-4c42-879c-6f28b3033fc9":{"order":1,"width":"small","grow":true,"type":"optionsListControl","explicitInput":{"fieldName":"machine.os.keyword","parentFieldName":"machine.os","title":"OS","id":"9807212f-5078-4c42-879c-6f28b3033fc9","enhancements":{}}},"6bf7a1b4-282e-43ac-aa46-81b97fa3acae":{"order":2,"width":"small","grow":true,"type":"rangeSliderControl","explicitInput":{"fieldName":"bytes","title":"Bytes","id":"6bf7a1b4-282e-43ac-aa46-81b97fa3acae","enhancements":{}}}}', + ignoreParentSettingsJSON: + '{"ignoreFilters":false,"ignoreQuery":false,"ignoreTimerange":false,"ignoreValidations":false}', + }, + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"query":{"language":"kuery","query":""},"highlightAll":true,"version":true,"filter":[]}', + }, + }, references: [ { - id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99', name: '4:panel_4', type: 'visualization', + id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99', }, { - id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef7f5b', name: '9:panel_9', type: 'visualization', + id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef7f5b', }, { - id: '69a34b00-9ee8-11e7-8711-e7a007dcef99', name: '11:panel_11', type: 'visualization', + id: '69a34b00-9ee8-11e7-8711-e7a007dcef99', }, { - id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4', name: '14:panel_14', type: 'visualization', + id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4', }, { - id: '314c6f60-2224-11e8-b802-5bcf64c2cfb4', name: '15:panel_15', type: 'visualization', - }, - { - id: '90943e30-9a47-11e8-b64d-95841ca0b247', - name: '30326cdb-4ddd-49eb-a4f1-b555caa21d7c:control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_0_index_pattern', - type: 'index-pattern', - }, - { - id: '90943e30-9a47-11e8-b64d-95841ca0b247', - name: '30326cdb-4ddd-49eb-a4f1-b555caa21d7c:control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_1_index_pattern', - type: 'index-pattern', - }, - { - id: '90943e30-9a47-11e8-b64d-95841ca0b247', - name: '30326cdb-4ddd-49eb-a4f1-b555caa21d7c:control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_2_index_pattern', - type: 'index-pattern', + id: '314c6f60-2224-11e8-b802-5bcf64c2cfb4', }, { id: '90943e30-9a47-11e8-b64d-95841ca0b247', @@ -455,9 +473,14 @@ export const getSavedObjects = (): SavedObject[] => [ type: 'index-pattern', }, { - id: 'cb099a20-ea66-11eb-9425-113343a037e3', name: '8e59c7cf-6e42-4343-a113-c4a255fcf2ce:panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce', type: 'visualization', + id: 'cb099a20-ea66-11eb-9425-113343a037e3', + }, + { + name: 'cbca842c-b9fa-4523-9ce0-14e350866e33:panel_cbca842c-b9fa-4523-9ce0-14e350866e33', + type: 'lens', + id: '16b1d7d0-ea71-11eb-8b4b-f7b600de0f7d', }, { id: '90943e30-9a47-11e8-b64d-95841ca0b247', @@ -470,38 +493,25 @@ export const getSavedObjects = (): SavedObject[] => [ type: 'index-pattern', }, { - id: '16b1d7d0-ea71-11eb-8b4b-f7b600de0f7d', - name: 'cbca842c-b9fa-4523-9ce0-14e350866e33:panel_cbca842c-b9fa-4523-9ce0-14e350866e33', - type: 'lens', + name: 'controlGroup_612f8db8-9ba9-41cf-a809-d133fe9b83a8:optionsListDataView', + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0b247', }, - ], - migrationVersion: { - dashboard: '7.14.0', - }, - attributes: { - title: i18n.translate('home.sampleData.logsSpec.webTrafficTitle', { - defaultMessage: '[Logs] Web Traffic', - }), - hits: 0, - description: i18n.translate('home.sampleData.logsSpec.webTrafficDescription', { - defaultMessage: "Analyze mock web traffic log data for Elastic's website", - }), - panelsJSON: - '[{"version":"8.0.0","type":"map","gridData":{"x":0,"y":19,"w":24,"h":18,"i":"4"},"panelIndex":"4","embeddableConfig":{"isLayerTOCOpen":false,"enhancements":{},"mapCenter":{"lat":42.16337,"lon":-88.92107,"zoom":3.64},"mapBuffer":{"minLon":-112.5,"minLat":21.94305,"maxLon":-45,"maxLat":55.77657},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_4"},{"version":"8.0.0","type":"visualization","gridData":{"x":0,"y":52,"w":24,"h":13,"i":"9"},"panelIndex":"9","embeddableConfig":{"mapCenter":[36.8092847020594,-96.94335937500001],"vis":{"params":{"sort":{"columnIndex":null,"direction":null}}},"enhancements":{}},"panelRefName":"panel_9"},{"version":"8.0.0","type":"visualization","gridData":{"x":12,"y":6,"w":12,"h":8,"i":"11"},"panelIndex":"11","embeddableConfig":{"vis":{"colors":{"0 - 500":"#BF1B00","1000 - 1500":"#7EB26D","500 - 1000":"#F2C96D"},"defaultColors":{"0 - 500":"rgb(165,0,38)","1000 - 1500":"rgb(0,104,55)","500 - 1000":"rgb(255,255,190)"},"legendOpen":false},"enhancements":{}},"title":"","panelRefName":"panel_11"},{"version":"8.0.0","type":"visualization","gridData":{"x":24,"y":19,"w":24,"h":33,"i":"14"},"panelIndex":"14","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_14"},{"version":"8.0.0","type":"visualization","gridData":{"x":24,"y":6,"w":24,"h":13,"i":"15"},"panelIndex":"15","embeddableConfig":{"enhancements":{"dynamicActions":{"events":[]}}},"panelRefName":"panel_15"},{"version":"8.0.0","type":"visualization","gridData":{"x":0,"y":0,"w":17,"h":6,"i":"343f0bef-0b19-452e-b1c8-59beb18b6f0c"},"panelIndex":"343f0bef-0b19-452e-b1c8-59beb18b6f0c","embeddableConfig":{"savedVis":{"title":"[Logs] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":12,"openLinksInNewTab":true,"markdown":"## Sample Logs Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{},"hidePanelTitles":true}},{"version":"8.0.0","type":"visualization","gridData":{"x":17,"y":0,"w":31,"h":6,"i":"30326cdb-4ddd-49eb-a4f1-b555caa21d7c"},"panelIndex":"30326cdb-4ddd-49eb-a4f1-b555caa21d7c","embeddableConfig":{"savedVis":{"title":"[Logs] Input Controls","description":"","type":"input_control_vis","params":{"controls":[{"id":"1523980210832","fieldName":"geo.src","label":"Source Country","type":"list","options":{"type":"terms","multiselect":true,"size":100,"order":"desc"},"parent":"","indexPatternRefName":"control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_0_index_pattern"},{"id":"1523980191978","fieldName":"machine.os.keyword","label":"OS","type":"list","options":{"type":"terms","multiselect":true,"size":100,"order":"desc"},"parent":"1523980210832","indexPatternRefName":"control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_1_index_pattern"},{"id":"1523980232790","fieldName":"bytes","label":"Bytes","type":"range","options":{"decimalPlaces":0,"step":1024},"indexPatternRefName":"control_30326cdb-4ddd-49eb-a4f1-b555caa21d7c_2_index_pattern"}],"updateFiltersOnChange":true,"useTimeFilter":true,"pinFilters":false},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{},"hidePanelTitles":true}},{"version":"8.0.0","type":"lens","gridData":{"x":0,"y":6,"w":12,"h":8,"i":"bb94016e-f4a6-49ca-87a9-296a2869d570"},"panelIndex":"bb94016e-f4a6-49ca-87a9-296a2869d570","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"483defd2-775b-4a62-bdef-496c819bb8ed":{"columns":{"37430d12-7452-4cc9-b035-5cfd4061edf0":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true}},"columnOrder":["37430d12-7452-4cc9-b035-5cfd4061edf0"],"incompleteColumns":{}}}}},"visualization":{"layerId":"483defd2-775b-4a62-bdef-496c819bb8ed","accessor":"37430d12-7452-4cc9-b035-5cfd4061edf0"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-483defd2-775b-4a62-bdef-496c819bb8ed"}]},"enhancements":{}}},{"version":"8.0.0","type":"lens","gridData":{"x":0,"y":14,"w":12,"h":5,"i":"01d8e435-91c0-484f-a11e-856747050b0a"},"panelIndex":"01d8e435-91c0-484f-a11e-856747050b0a","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a"}]},"enhancements":{}}},{"version":"8.0.0","type":"lens","gridData":{"x":12,"y":14,"w":12,"h":5,"i":"8c1456d4-1993-4ba2-b701-04aca02c9fef"},"panelIndex":"8c1456d4-1993-4ba2-b701-04aca02c9fef","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a"}]},"enhancements":{}}},{"version":"8.0.0","type":"visualization","gridData":{"x":0,"y":37,"w":24,"h":15,"i":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},"panelIndex":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce","embeddableConfig":{"savedVis":{"title":"","description":"","type":"vega","params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logs\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.src\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: true\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: ordinal\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: reds\\n }\\n }\\n }\\n}\\n"},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{}},"panelRefName":"panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},{"version":"8.0.0","type":"lens","gridData":{"x":0,"y":65,"w":48,"h":19,"i":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b"},"panelIndex":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"indexpattern":{"layers":{"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0":{"columns":{"42783ad7-dbcf-4310-bc06-f21f4eaaac96":{"label":"URL","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"url.keyword","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"column","columnId":"f7835375-4d5b-4839-95ea-41928192a319"},"orderDirection":"desc","otherBucket":true,"missingBucket":false},"customLabel":true},"f7835375-4d5b-4839-95ea-41928192a319":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX0":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX1":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX2":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1da":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX2"],"customLabel":true},"791d5a5b-a7ba-4e9e-b533-51b33c7d7747":{"label":"Unique","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"customLabel":true},"611e3509-e834-4fdd-b573-44e959e95d27":{"label":"95th percentile of bytes","dataType":"number","operationType":"percentile","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"percentile":95,"format":{"id":"bytes","params":{"decimals":0}}}},"9f79ecca-123f-4098-a658-6b0e998da003":{"label":"Median of bytes","dataType":"number","operationType":"median","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"format":{"id":"bytes","params":{"decimals":0}}}},"491285fd-0196-402c-9b7f-4660fdc1c22aX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"Records","customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22a":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX2"],"customLabel":true}},"columnOrder":["42783ad7-dbcf-4310-bc06-f21f4eaaac96","f7835375-4d5b-4839-95ea-41928192a319","791d5a5b-a7ba-4e9e-b533-51b33c7d7747","07fc84ca-4147-4ba9-879e-d1b4e086e1da","491285fd-0196-402c-9b7f-4660fdc1c22a","491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1","491285fd-0196-402c-9b7f-4660fdc1c22aX2","07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1","07fc84ca-4147-4ba9-879e-d1b4e086e1daX2","611e3509-e834-4fdd-b573-44e959e95d27","9f79ecca-123f-4098-a658-6b0e998da003"],"incompleteColumns":{}}}}},"visualization":{"layerId":"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","columns":[{"columnId":"42783ad7-dbcf-4310-bc06-f21f4eaaac96","width":650.6666666666666},{"columnId":"f7835375-4d5b-4839-95ea-41928192a319"},{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","isTransposed":false,"width":81.66666666666669,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#CC5642","stop":1}],"rangeType":"number","name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#CC5642","stop":0.1}],"rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"07fc84ca-4147-4ba9-879e-d1b4e086e1da","isTransposed":false,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#cc5642","stop":1.1}],"name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#cc5642","stop":0.1}],"rangeType":"number","rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"791d5a5b-a7ba-4e9e-b533-51b33c7d7747","isTransposed":false},{"columnId":"611e3509-e834-4fdd-b573-44e959e95d27","isTransposed":false},{"columnId":"9f79ecca-123f-4098-a658-6b0e998da003","isTransposed":false}],"sorting":{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","direction":"desc"}},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern"},{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0"}]},"enhancements":{"dynamicActions":{"events":[]}},"hidePanelTitles":false},"title":"[Logs] Errors by host"},{"version":"8.0.0","type":"lens","gridData":{"x":24,"y":52,"w":24,"h":13,"i":"cbca842c-b9fa-4523-9ce0-14e350866e33"},"panelIndex":"cbca842c-b9fa-4523-9ce0-14e350866e33","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"[Logs] Bytes distribution","panelRefName":"panel_cbca842c-b9fa-4523-9ce0-14e350866e33"}]', - optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', - version: 2, - timeRestore: true, - timeTo: 'now', - timeFrom: 'now-7d', - refreshInterval: { - pause: false, - value: 900000, + { + name: 'controlGroup_9807212f-5078-4c42-879c-6f28b3033fc9:optionsListDataView', + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0b247', }, - kibanaSavedObjectMeta: { - searchSourceJSON: - '{"query":{"language":"kuery","query":""},"filter":[],"highlightAll":true,"version":true}', + { + name: 'controlGroup_6bf7a1b4-282e-43ac-aa46-81b97fa3acae:rangeSliderDataView', + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0b247', }, + ], + migrationVersion: { + dashboard: '8.5.0', }, + coreMigrationVersion: '8.6.0', }, { id: '2f360f30-ea74-11eb-b4c6-3d2afc1cb389', diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json index 8e617896e3f96..af121720eee0e 100644 --- a/src/plugins/home/tsconfig.json +++ b/src/plugins/home/tsconfig.json @@ -15,6 +15,7 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../share/tsconfig.json" }, { "path": "../url_forwarding/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" } + { "path": "../usage_collection/tsconfig.json" }, + { "path": "../../../x-pack/plugins/cloud/tsconfig.json" } ] } diff --git a/src/plugins/inspector/public/views/requests/components/details/req_code_viewer.tsx b/src/plugins/inspector/public/views/requests/components/details/req_code_viewer.tsx index df2b3d5c88b93..5ab50ba33a514 100644 --- a/src/plugins/inspector/public/views/requests/components/details/req_code_viewer.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/req_code_viewer.tsx @@ -132,7 +132,7 @@ export const RequestCodeViewer = ({ indexPattern, json }: RequestCodeViewerProps )}
- + { }; }; +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_field_list/README.md b/src/plugins/unified_field_list/README.md index 4f0e841de03b2..9030a32a3bdca 100755 --- a/src/plugins/unified_field_list/README.md +++ b/src/plugins/unified_field_list/README.md @@ -6,7 +6,52 @@ This Kibana plugin contains components and services for field list UI (as in fie ## Components -* `` - loads and renders stats (Top values, Histogram) for a data view field. +* `` - loads and renders stats (Top values, Distribution) for a data view field. + +* `` - renders a button to open this field in Lens. + +* `` - a popover container component for a field. + +* `` - this header component included a field name and common actions. +* +* `` - renders Visualize action in the popover footer. + +These components can be combined and customized as the following: +``` +} + renderHeader={() => + + } + renderContent={() => + <> + + '} + ... + /> + + } + ... +/> +``` ## Public Services diff --git a/src/plugins/unified_field_list/kibana.json b/src/plugins/unified_field_list/kibana.json index 6785d55989cc5..0615ee0473cce 100755 --- a/src/plugins/unified_field_list/kibana.json +++ b/src/plugins/unified_field_list/kibana.json @@ -9,7 +9,7 @@ "description": "Contains functionality for the field list which can be integrated into apps", "server": true, "ui": true, - "requiredPlugins": ["dataViews", "data", "fieldFormats", "charts"], + "requiredPlugins": ["dataViews", "data", "fieldFormats", "charts", "uiActions"], "optionalPlugins": [], "requiredBundles": [] } diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover.scss b/src/plugins/unified_field_list/public/components/field_popover/field_popover.scss new file mode 100644 index 0000000000000..14ce3768f0bea --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover.scss @@ -0,0 +1,4 @@ +.unifiedFieldList__fieldPopover__fieldPopoverPanel { + min-width: $euiSizeXXL * 6.5; + max-width: $euiSizeXXL * 7.5; +} diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover.test.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover.test.tsx new file mode 100644 index 0000000000000..bc5fe035ceff0 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover.test.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButton, EuiText, EuiPopoverTitle, EuiPopoverFooter } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { FieldPopover } from './field_popover'; +import { FieldPopoverHeader } from './field_popover_header'; + +describe('UnifiedFieldList ', () => { + it('should render correctly header only', async () => { + const wrapper = mountWithIntl( + } + renderHeader={() => {'header'}} + /> + ); + + expect(wrapper.find(EuiText).text()).toBe('header'); + expect(wrapper.find(EuiPopoverTitle)).toHaveLength(0); + expect(wrapper.find(EuiPopoverFooter)).toHaveLength(0); + }); + + it('should render correctly with header and content', async () => { + const wrapper = mountWithIntl( + } + renderHeader={() => {'header'}} + renderContent={() => {'content'}} + /> + ); + + expect(wrapper.find(EuiText).first().text()).toBe('header'); + expect(wrapper.find(EuiText).last().text()).toBe('content'); + expect(wrapper.find(EuiPopoverTitle)).toHaveLength(1); + }); + + it('should render nothing if popover is closed', async () => { + const wrapper = mountWithIntl( + } + renderHeader={() => {'header'}} + renderContent={() => {'content'}} + /> + ); + + expect(wrapper.text()).toBe(''); + expect(wrapper.find(EuiPopoverTitle)).toHaveLength(0); + }); + + it('should render correctly with popover header and content', async () => { + const mockClose = jest.fn(); + const mockEdit = jest.fn(); + const fieldName = 'extension'; + const wrapper = mountWithIntl( + } + renderHeader={() => ( + field.name === fieldName)!} + closePopover={mockClose} + onEditField={mockEdit} + /> + )} + renderContent={() => {'content'}} + /> + ); + + expect(wrapper.find(EuiPopoverTitle).text()).toBe(fieldName); + expect(wrapper.find(EuiText).last().text()).toBe('content'); + + wrapper + .find(`[data-test-subj="fieldPopoverHeader_editField-${fieldName}"]`) + .first() + .simulate('click'); + + expect(mockClose).toHaveBeenCalled(); + expect(mockEdit).toHaveBeenCalledWith(fieldName); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover.tsx new file mode 100644 index 0000000000000..59c6b9621f8c3 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiPopover, EuiPopoverProps, EuiPopoverTitle } from '@elastic/eui'; +import './field_popover.scss'; + +export interface FieldPopoverProps extends EuiPopoverProps { + renderHeader?: () => React.ReactNode; + renderContent?: () => React.ReactNode; +} + +export const FieldPopover: React.FC = ({ + isOpen, + closePopover, + renderHeader, + renderContent, + ...otherPopoverProps +}) => { + const header = (isOpen && renderHeader?.()) || null; + const content = (isOpen && renderContent?.()) || null; + + return ( + + {isOpen && ( + <> + {content && header ? {header} : header} + {content} + + )} + + ); +}; diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.test.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.test.tsx new file mode 100644 index 0000000000000..22d3269b2c0d6 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.test.tsx @@ -0,0 +1,167 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButtonIcon } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { FieldPopoverHeader } from './field_popover_header'; + +describe('UnifiedFieldList ', () => { + it('should render correctly without actions', async () => { + const mockClose = jest.fn(); + const fieldName = 'extension'; + const wrapper = mountWithIntl( + field.name === fieldName)!} + closePopover={mockClose} + /> + ); + + expect(wrapper.text()).toBe(fieldName); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(0); + }); + + it('should render correctly with all actions', async () => { + const mockClose = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + jest.spyOn(field, 'isRuntimeField', 'get').mockImplementation(() => true); + const wrapper = mountWithIntl( + + ); + + expect(wrapper.text()).toBe(fieldName); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_addField-${fieldName}"]`).exists() + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_addExistsFilter-${fieldName}"]`).exists() + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_editField-${fieldName}"]`).exists() + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_deleteField-${fieldName}"]`).exists() + ).toBeTruthy(); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(4); + }); + + it('should correctly handle add-field action', async () => { + const mockClose = jest.fn(); + const mockAddField = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + const wrapper = mountWithIntl( + + ); + + wrapper + .find(`[data-test-subj="fieldPopoverHeader_addField-${fieldName}"]`) + .first() + .simulate('click'); + + expect(mockClose).toHaveBeenCalled(); + expect(mockAddField).toHaveBeenCalledWith(field); + }); + + it('should correctly handle add-exists-filter action', async () => { + const mockClose = jest.fn(); + const mockAddFilter = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + + // available + let wrapper = mountWithIntl( + + ); + wrapper + .find(`[data-test-subj="fieldPopoverHeader_addExistsFilter-${fieldName}"]`) + .first() + .simulate('click'); + expect(mockClose).toHaveBeenCalled(); + expect(mockAddFilter).toHaveBeenCalledWith('_exists_', fieldName, '+'); + + // hidden + jest.spyOn(field, 'filterable', 'get').mockImplementation(() => false); + wrapper = mountWithIntl( + + ); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_addExistsFilter-${fieldName}"]`).exists() + ).toBeFalsy(); + }); + + it('should correctly handle edit-field action', async () => { + const mockClose = jest.fn(); + const mockEditField = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + + // available + jest.spyOn(field, 'isRuntimeField', 'get').mockImplementation(() => true); + let wrapper = mountWithIntl( + + ); + wrapper + .find(`[data-test-subj="fieldPopoverHeader_editField-${fieldName}"]`) + .first() + .simulate('click'); + expect(mockClose).toHaveBeenCalled(); + expect(mockEditField).toHaveBeenCalledWith(fieldName); + + // hidden + jest.spyOn(field, 'isRuntimeField', 'get').mockImplementation(() => false); + jest.spyOn(field, 'type', 'get').mockImplementation(() => 'unknown'); + wrapper = mountWithIntl( + + ); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_editField-${fieldName}"]`).exists() + ).toBeFalsy(); + }); + + it('should correctly handle delete-field action', async () => { + const mockClose = jest.fn(); + const mockDeleteField = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + + // available + jest.spyOn(field, 'isRuntimeField', 'get').mockImplementation(() => true); + let wrapper = mountWithIntl( + + ); + wrapper + .find(`[data-test-subj="fieldPopoverHeader_deleteField-${fieldName}"]`) + .first() + .simulate('click'); + expect(mockClose).toHaveBeenCalled(); + expect(mockDeleteField).toHaveBeenCalledWith(fieldName); + + // hidden + jest.spyOn(field, 'isRuntimeField', 'get').mockImplementation(() => false); + wrapper = mountWithIntl( + + ); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_deleteField-${fieldName}"]`).exists() + ).toBeFalsy(); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.tsx new file mode 100644 index 0000000000000..131273fbb4d42 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover_header.tsx @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiButtonIcon, + EuiButtonIconProps, + EuiFlexGroup, + EuiFlexItem, + EuiPopoverProps, + EuiToolTip, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import type { DataViewField } from '@kbn/data-views-plugin/common'; +import type { AddFieldFilterHandler } from '../../types'; + +export interface FieldPopoverHeaderProps { + field: DataViewField; + closePopover: EuiPopoverProps['closePopover']; + buttonAddFieldToWorkspaceProps?: Partial; + buttonAddFilterProps?: Partial; + buttonEditFieldProps?: Partial; + buttonDeleteFieldProps?: Partial; + onAddFieldToWorkspace?: (field: DataViewField) => unknown; + onAddFilter?: AddFieldFilterHandler; + onEditField?: (fieldName: string) => unknown; + onDeleteField?: (fieldName: string) => unknown; +} + +export const FieldPopoverHeader: React.FC = ({ + field, + closePopover, + buttonAddFieldToWorkspaceProps, + buttonAddFilterProps, + buttonEditFieldProps, + buttonDeleteFieldProps, + onAddFieldToWorkspace, + onAddFilter, + onEditField, + onDeleteField, +}) => { + if (!field) { + return null; + } + + const addFieldToWorkspaceTooltip = i18n.translate( + 'unifiedFieldList.fieldPopover.addFieldToWorkspaceLabel', + { + defaultMessage: 'Add "{field}" field', + values: { + field: field.displayName, + }, + } + ); + + const addExistsFilterTooltip = i18n.translate( + 'unifiedFieldList.fieldPopover.addExistsFilterLabel', + { + defaultMessage: 'Filter for field present', + } + ); + + const editFieldTooltip = i18n.translate('unifiedFieldList.fieldPopover.editFieldLabel', { + defaultMessage: 'Edit data view field', + }); + + const deleteFieldTooltip = i18n.translate('unifiedFieldList.fieldPopover.deleteFieldLabel', { + defaultMessage: 'Delete data view field', + }); + + return ( + + + +
{field.displayName}
+
+
+ {onAddFieldToWorkspace && ( + + + { + closePopover(); + onAddFieldToWorkspace(field); + }} + /> + + + )} + {onAddFilter && field.filterable && !field.scripted && ( + + + { + closePopover(); + onAddFilter('_exists_', field.name, '+'); + }} + /> + + + )} + {onEditField && + (field.isRuntimeField || !['unknown', 'unknown_selected'].includes(field.type)) && ( + + + { + closePopover(); + onEditField(field.name); + }} + /> + + + )} + {onDeleteField && field.isRuntimeField && ( + + + { + closePopover(); + onDeleteField(field.name); + }} + /> + + + )} +
+ ); +}; diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx new file mode 100644 index 0000000000000..0f6b06c6239c7 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiPopoverFooter } from '@elastic/eui'; +import { FieldVisualizeButton, type FieldVisualizeButtonProps } from '../field_visualize_button'; + +export type FieldPopoverVisualizeProps = Omit; + +const wrapInContainer = (element: React.ReactElement): React.ReactElement => { + return {element}; +}; + +export const FieldPopoverVisualize: React.FC = (props) => { + return ; +}; diff --git a/src/plugins/unified_field_list/public/components/field_popover/index.tsx b/src/plugins/unified_field_list/public/components/field_popover/index.tsx new file mode 100755 index 0000000000000..ecbcf36d83316 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/index.tsx @@ -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 FieldPopoverProps, FieldPopover } from './field_popover'; +export { type FieldPopoverHeaderProps, FieldPopoverHeader } from './field_popover_header'; +export { type FieldPopoverVisualizeProps, FieldPopoverVisualize } from './field_popover_visualize'; diff --git a/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx new file mode 100644 index 0000000000000..99f0543f536b9 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { ReactWrapper } from 'enzyme'; +import { EuiButton, EuiPopoverFooter } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { FieldVisualizeButton } from './field_visualize_button'; +import { + ACTION_VISUALIZE_LENS_FIELD, + VISUALIZE_FIELD_TRIGGER, + VISUALIZE_GEO_FIELD_TRIGGER, + createAction, + VisualizeFieldContext, +} from '@kbn/ui-actions-plugin/public'; +import { TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; + +const ORIGINATING_APP = 'test'; +const mockExecuteAction = jest.fn(); +const uiActions = uiActionsPluginMock.createStartContract(); +const visualizeAction = createAction({ + type: ACTION_VISUALIZE_LENS_FIELD, + id: ACTION_VISUALIZE_LENS_FIELD, + getDisplayName: () => 'test', + isCompatible: async () => true, + execute: async (context: VisualizeFieldContext) => { + mockExecuteAction(context); + }, + getHref: async () => '/app/test', +}); + +jest.spyOn(uiActions, 'getTriggerCompatibleActions').mockResolvedValue([visualizeAction]); +jest.spyOn(uiActions, 'getTrigger').mockReturnValue({ + id: ACTION_VISUALIZE_LENS_FIELD, + exec: mockExecuteAction, +} as unknown as TriggerContract); + +describe('UnifiedFieldList ', () => { + it('should render correctly', async () => { + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + const fieldNameKeyword = 'extension.keyword'; + const fieldKeyword = dataView.fields.find((f) => f.name === fieldNameKeyword)!; + const contextualFields = ['bytes']; + jest.spyOn(field, 'visualizable', 'get').mockImplementationOnce(() => false); + jest.spyOn(fieldKeyword, 'visualizable', 'get').mockImplementationOnce(() => true); + let wrapper: ReactWrapper; + + await act(async () => { + wrapper = await mountWithIntl( + {element}} + /> + ); + }); + + await wrapper!.update(); + + expect(uiActions.getTriggerCompatibleActions).toHaveBeenCalledWith(VISUALIZE_FIELD_TRIGGER, { + contextualFields, + dataViewSpec: dataView.toSpec(false), + fieldName: fieldNameKeyword, + }); + + expect(wrapper!.text()).toBe('Visualize'); + wrapper!.find(EuiButton).simulate('click'); + + expect(mockExecuteAction).toHaveBeenCalledWith({ + contextualFields, + dataViewSpec: dataView.toSpec(false), + fieldName: fieldNameKeyword, + originatingApp: ORIGINATING_APP, + }); + + expect(wrapper!.find(EuiButton).prop('href')).toBe('/app/test'); + expect(wrapper!.find(EuiPopoverFooter).find(EuiButton).exists()).toBeTruthy(); // wrapped in a container + }); + + it('should render correctly for geo fields', async () => { + const fieldName = 'geo.coordinates'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + jest.spyOn(field, 'visualizable', 'get').mockImplementationOnce(() => true); + let wrapper: ReactWrapper; + + await act(async () => { + wrapper = await mountWithIntl( + + ); + }); + + await wrapper!.update(); + + expect(uiActions.getTriggerCompatibleActions).toHaveBeenCalledWith( + VISUALIZE_GEO_FIELD_TRIGGER, + { + contextualFields: [], + dataViewSpec: dataView.toSpec(false), + fieldName, + } + ); + + expect(wrapper!.text()).toBe('Visualize'); + wrapper!.find(EuiButton).simulate('click'); + + expect(mockExecuteAction).toHaveBeenCalledWith({ + contextualFields: [], + dataViewSpec: dataView.toSpec(false), + fieldName, + originatingApp: ORIGINATING_APP, + }); + }); +}); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx similarity index 51% rename from src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize.tsx rename to src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx index f60838d375b68..aa9fe5266de09 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize.tsx +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx @@ -7,29 +7,48 @@ */ import React, { useEffect, useState } from 'react'; +import { EuiButtonProps } from '@elastic/eui'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import { triggerVisualizeActions, VisualizeInformation } from './lib/visualize_trigger_utils'; -import { getVisualizeInformation } from './lib/visualize_trigger_utils'; -import { DiscoverFieldVisualizeInner } from './discover_field_visualize_inner'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { FieldVisualizeButtonInner } from './field_visualize_button_inner'; +import { + triggerVisualizeActions, + getVisualizeInformation, + type VisualizeInformation, +} from './visualize_trigger_utils'; -interface Props { +export interface FieldVisualizeButtonProps { field: DataViewField; dataView: DataView; + originatingApp: string; // plugin id + uiActions: UiActionsStart; multiFields?: DataViewField[]; - contextualFields: string[]; + contextualFields?: string[]; // names of fields which were also selected (like columns in Discover grid) trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + buttonProps?: Partial; + wrapInContainer?: (element: React.ReactElement) => React.ReactElement; } -export const DiscoverFieldVisualize: React.FC = React.memo( - ({ field, dataView, contextualFields, trackUiMetric, multiFields }) => { +export const FieldVisualizeButton: React.FC = React.memo( + ({ + field, + dataView, + contextualFields, + trackUiMetric, + multiFields, + originatingApp, + uiActions, + buttonProps, + wrapInContainer, + }) => { const [visualizeInfo, setVisualizeInfo] = useState(); useEffect(() => { - getVisualizeInformation(field, dataView, contextualFields, multiFields).then( + getVisualizeInformation(uiActions, field, dataView, contextualFields, multiFields).then( setVisualizeInfo ); - }, [contextualFields, field, dataView, multiFields]); + }, [contextualFields, field, dataView, multiFields, uiActions]); if (!visualizeInfo) { return null; @@ -43,17 +62,26 @@ export const DiscoverFieldVisualize: React.FC = React.memo( const triggerVisualization = (updatedDataView: DataView) => { trackUiMetric?.(METRIC_TYPE.CLICK, 'visualize_link_click'); - triggerVisualizeActions(visualizeInfo.field, contextualFields, updatedDataView); + triggerVisualizeActions( + uiActions, + visualizeInfo.field, + contextualFields, + originatingApp, + updatedDataView + ); }; triggerVisualization(dataView); }; - return ( - ); + + return wrapInContainer?.(element) || element; } ); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize_inner.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button_inner.tsx similarity index 68% rename from src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize_inner.tsx rename to src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button_inner.tsx index 2bbdeed13fde3..a8185839925b5 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field_visualize_inner.tsx +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button_inner.tsx @@ -7,35 +7,40 @@ */ import React from 'react'; -import { EuiButton, EuiPopoverFooter } from '@elastic/eui'; +import { EuiButton, EuiButtonProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { DataViewField } from '@kbn/data-views-plugin/public'; -import { VisualizeInformation } from './lib/visualize_trigger_utils'; +import { VisualizeInformation } from './visualize_trigger_utils'; -interface DiscoverFieldVisualizeInnerProps { +interface FieldVisualizeButtonInnerProps { field: DataViewField; visualizeInfo: VisualizeInformation; handleVisualizeLinkClick: (event: React.MouseEvent) => void; + buttonProps?: Partial; } -export const DiscoverFieldVisualizeInner = (props: DiscoverFieldVisualizeInnerProps) => { - const { field, visualizeInfo, handleVisualizeLinkClick } = props; - +export const FieldVisualizeButtonInner: React.FC = ({ + field, + visualizeInfo, + handleVisualizeLinkClick, + buttonProps, +}) => { return ( - + <> {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - + ); }; diff --git a/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx new file mode 100755 index 0000000000000..812ffbcb0d0e1 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { type FieldVisualizeButtonProps, FieldVisualizeButton } from './field_visualize_button'; + +export { + triggerVisualizeActions, + triggerVisualizeActionsTextBasedLanguages, + getVisualizeInformation, + type VisualizeInformation, +} from './visualize_trigger_utils'; diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.test.ts b/src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.test.ts similarity index 87% rename from src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.test.ts rename to src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.test.ts index 9c2528bca7480..da87ff5f05343 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.test.ts +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.test.ts @@ -7,7 +7,7 @@ */ import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; -import type { Action } from '@kbn/ui-actions-plugin/public'; +import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { getVisualizeInformation } from './visualize_trigger_utils'; const field = { @@ -26,11 +26,9 @@ const mockGetActions = jest.fn>>, [string, { fieldN () => Promise.resolve([]) ); -jest.mock('../../../../../kibana_services', () => ({ - getUiActions: () => ({ - getTriggerCompatibleActions: mockGetActions, - }), -})); +const uiActions = { + getTriggerCompatibleActions: mockGetActions, +} as unknown as UiActionsStart; const action: Action = { id: 'action', @@ -51,7 +49,13 @@ describe('visualize_trigger_utils', () => { describe('getVisualizeInformation', () => { it('should return for a visualizeable field with an action', async () => { mockGetActions.mockResolvedValue([action]); - const information = await getVisualizeInformation(field, dataViewMock, [], undefined); + const information = await getVisualizeInformation( + uiActions, + field, + dataViewMock, + [], + undefined + ); expect(information).not.toBeUndefined(); expect(information?.field).toHaveProperty('name', 'fieldName'); expect(information?.href).toBeUndefined(); @@ -59,7 +63,13 @@ describe('visualize_trigger_utils', () => { it('should return field and href from the action', async () => { mockGetActions.mockResolvedValue([{ ...action, getHref: () => Promise.resolve('hreflink') }]); - const information = await getVisualizeInformation(field, dataViewMock, [], undefined); + const information = await getVisualizeInformation( + uiActions, + field, + dataViewMock, + [], + undefined + ); expect(information).not.toBeUndefined(); expect(information?.field).toHaveProperty('name', 'fieldName'); expect(information).toHaveProperty('href', 'hreflink'); @@ -68,6 +78,7 @@ describe('visualize_trigger_utils', () => { it('should return undefined if no field has a compatible action', async () => { mockGetActions.mockResolvedValue([]); const information = await getVisualizeInformation( + uiActions, { ...field, name: 'rootField' } as DataViewField, dataViewMock, [], @@ -82,6 +93,7 @@ describe('visualize_trigger_utils', () => { it('should return information for the root field, when multi fields and root are having actions', async () => { mockGetActions.mockResolvedValue([action]); const information = await getVisualizeInformation( + uiActions, { ...field, name: 'rootField' } as DataViewField, dataViewMock, [], @@ -102,6 +114,7 @@ describe('visualize_trigger_utils', () => { return []; }); const information = await getVisualizeInformation( + uiActions, { ...field, name: 'rootField' } as DataViewField, dataViewMock, [], diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.ts similarity index 84% rename from src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts rename to src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.ts index 85537df2ef364..babb7f40ff92b 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/visualize_trigger_utils.ts @@ -7,6 +7,7 @@ */ import { + type UiActionsStart, VISUALIZE_FIELD_TRIGGER, VISUALIZE_GEO_FIELD_TRIGGER, visualizeFieldTrigger, @@ -15,8 +16,6 @@ import { import type { AggregateQuery } from '@kbn/es-query'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; import { KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; -import { getUiActions } from '../../../../../kibana_services'; -import { PLUGIN_ID } from '../../../../../../common'; export function getTriggerConstant(type: string) { return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE @@ -31,12 +30,13 @@ function getTrigger(type: string) { } async function getCompatibleActions( + uiActions: UiActionsStart, fieldName: string, dataView: DataView, - contextualFields: string[], + contextualFields: string[] = [], trigger: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER ) { - const compatibleActions = await getUiActions().getTriggerCompatibleActions(trigger, { + const compatibleActions = await uiActions.getTriggerCompatibleActions(trigger, { dataViewSpec: dataView.toSpec(false), fieldName, contextualFields, @@ -45,8 +45,10 @@ async function getCompatibleActions( } export function triggerVisualizeActions( + uiActions: UiActionsStart, field: DataViewField, - contextualFields: string[], + contextualFields: string[] = [], + originatingApp: string, dataView?: DataView ) { if (!dataView) return; @@ -55,13 +57,15 @@ export function triggerVisualizeActions( dataViewSpec: dataView.toSpec(false), fieldName: field.name, contextualFields, - originatingApp: PLUGIN_ID, + originatingApp, }; - getUiActions().getTrigger(trigger).exec(triggerOptions); + uiActions.getTrigger(trigger).exec(triggerOptions); } export function triggerVisualizeActionsTextBasedLanguages( + uiActions: UiActionsStart, contextualFields: string[], + originatingApp: string, dataView?: DataView, query?: AggregateQuery ) { @@ -70,10 +74,10 @@ export function triggerVisualizeActionsTextBasedLanguages( dataViewSpec: dataView.toSpec(false), fieldName: '', contextualFields, - originatingApp: PLUGIN_ID, + originatingApp, query, }; - getUiActions().getTrigger(VISUALIZE_FIELD_TRIGGER).exec(triggerOptions); + uiActions.getTrigger(VISUALIZE_FIELD_TRIGGER).exec(triggerOptions); } export interface VisualizeInformation { @@ -86,9 +90,10 @@ export interface VisualizeInformation { * that has a compatible visualize uiAction. */ export async function getVisualizeInformation( + uiActions: UiActionsStart, field: DataViewField, dataView: DataView | undefined, - contextualFields: string[], + contextualFields: string[] = [], multiFields: DataViewField[] = [] ): Promise { if (field.name === '_id' || !dataView?.id) { @@ -102,6 +107,7 @@ export async function getVisualizeInformation( } // Retrieve compatible actions for the specific field const actions = await getCompatibleActions( + uiActions, f.name, dataView, contextualFields, @@ -111,7 +117,7 @@ export async function getVisualizeInformation( // if the field has compatible actions use this field for visualizing if (actions.length > 0) { const triggerOptions = { - dataViewSpec: dataView?.toSpec(), + dataViewSpec: dataView?.toSpec(false), fieldName: f.name, contextualFields, trigger: getTrigger(f.type), diff --git a/src/plugins/unified_field_list/public/index.ts b/src/plugins/unified_field_list/public/index.ts index bf1cd2175689b..2ada1027ee97a 100755 --- a/src/plugins/unified_field_list/public/index.ts +++ b/src/plugins/unified_field_list/public/index.ts @@ -16,6 +16,22 @@ export type { } from '../common/types'; export type { FieldStatsProps, FieldStatsServices } from './components/field_stats'; export { FieldStats } from './components/field_stats'; +export { + FieldPopover, + type FieldPopoverProps, + FieldPopoverHeader, + type FieldPopoverHeaderProps, + FieldPopoverVisualize, + type FieldPopoverVisualizeProps, +} from './components/field_popover'; +export { + FieldVisualizeButton, + type FieldVisualizeButtonProps, + getVisualizeInformation, + triggerVisualizeActions, + triggerVisualizeActionsTextBasedLanguages, + type VisualizeInformation, +} from './components/field_visualize_button'; export { loadFieldStats } from './services/field_stats'; export { loadFieldExisting } from './services/field_existing'; diff --git a/src/plugins/unified_field_list/public/types.ts b/src/plugins/unified_field_list/public/types.ts index dcd425b8a880b..f7a712534d59d 100755 --- a/src/plugins/unified_field_list/public/types.ts +++ b/src/plugins/unified_field_list/public/types.ts @@ -14,4 +14,8 @@ export interface UnifiedFieldListPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface UnifiedFieldListPluginStart {} -export type AddFieldFilterHandler = (field: DataViewField, value: unknown, type: '+' | '-') => void; +export type AddFieldFilterHandler = ( + field: DataViewField | '_exists_', + value: unknown, + type: '+' | '-' +) => void; diff --git a/src/plugins/unified_field_list/tsconfig.json b/src/plugins/unified_field_list/tsconfig.json index 221729fbd2b71..eabadac9ef6fe 100644 --- a/src/plugins/unified_field_list/tsconfig.json +++ b/src/plugins/unified_field_list/tsconfig.json @@ -18,6 +18,7 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../data_views/tsconfig.json" }, { "path": "../data/tsconfig.json" }, - { "path": "../charts/tsconfig.json" } + { "path": "../charts/tsconfig.json" }, + { "path": "../ui_actions/tsconfig.json" } ] } 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 72a2d8fea290f..1e71da3a0b20a 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -72,6 +72,7 @@ export function ChangeDataView({ onTextLangQuerySubmit, textBasedLanguage, isDisabled, + onEditDataView, onCreateDefaultAdHocDataView, }: DataViewPickerPropsExtended) { const { euiTheme } = useEuiTheme(); @@ -88,7 +89,7 @@ export function ChangeDataView({ const [selectedDataViewId, setSelectedDataViewId] = useState(currentDataViewId); const kibana = useKibana(); - const { application, data, storage } = kibana.services; + const { application, data, storage, dataViews, dataViewEditor } = kibana.services; const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() => Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY)) @@ -189,21 +190,35 @@ export function ChangeDataView({ defaultMessage: 'Add a field to this data view', })} , - { - setPopoverIsOpen(false); - application.navigateToApp('management', { - path: `/kibana/indexPatterns/patterns/${currentDataViewId}`, - }); - }} - > - {i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', { - defaultMessage: 'Manage this data view', - })} - , + 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/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 9fb794d58e642..8ed524b32ea12 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -41,6 +41,11 @@ export interface DataViewPickerProps { * Callback that is called when the user changes the currently selected dataview. */ onChangeDataView: (newId: string) => void; + /** + * Callback that is called when the user edits the current data view via flyout. + * The first parameter is the updated data view stub without fetched fields + */ + onEditDataView?: (updatedDataViewStub: DataView) => void; /** * The id of the selected dataview. */ @@ -98,6 +103,7 @@ export const DataViewPicker = ({ currentDataViewId, adHocDataViews, onChangeDataView, + onEditDataView, onAddField, onDataViewCreated, trigger, @@ -114,6 +120,7 @@ export const DataViewPicker = ({ isMissingCurrent={isMissingCurrent} currentDataViewId={currentDataViewId} onChangeDataView={onChangeDataView} + onEditDataView={onEditDataView} onAddField={onAddField} onDataViewCreated={onDataViewCreated} onCreateDefaultAdHocDataView={onCreateDefaultAdHocDataView} diff --git a/src/plugins/unified_search/public/filters_builder/__mock__/filters.ts b/src/plugins/unified_search/public/filters_builder/__mock__/filters.ts index 03d5b4333cff4..fae7dd7e93502 100644 --- a/src/plugins/unified_search/public/filters_builder/__mock__/filters.ts +++ b/src/plugins/unified_search/public/filters_builder/__mock__/filters.ts @@ -33,7 +33,7 @@ export const getFiltersMock = () => }, { meta: { - type: 'OR', + type: 'combined', params: [ { meta: { @@ -302,7 +302,7 @@ export const getDataThatNeedsNormalized = () => }, { meta: { - type: 'OR', + type: 'combined', params: [ { meta: { @@ -420,7 +420,7 @@ export const getDataAfterNormalized = () => }, { meta: { - type: 'OR', + type: 'combined', params: [ { meta: { @@ -515,7 +515,7 @@ export const getDataThatNeedNotNormalized = () => [ { meta: { - type: 'OR', + type: 'combined', params: [ { meta: { diff --git a/src/plugins/unified_search/public/filters_builder/filters_builder_utils.test.ts b/src/plugins/unified_search/public/filters_builder/filters_builder_utils.test.ts index 517a0cea4cce7..56a0eb97b80b2 100644 --- a/src/plugins/unified_search/public/filters_builder/filters_builder_utils.test.ts +++ b/src/plugins/unified_search/public/filters_builder/filters_builder_utils.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { buildEmptyFilter, Filter } from '@kbn/es-query'; +import { buildEmptyFilter, Filter, FilterItem } from '@kbn/es-query'; import { ConditionTypes } from '../utils'; import { getFilterByPath, @@ -16,7 +16,6 @@ import { moveFilter, normalizeFilters, } from './filters_builder_utils'; -import type { FilterItem } from '../utils'; import { getConditionalOperationType } from '../utils'; import { diff --git a/src/plugins/unified_search/public/filters_builder/filters_builder_utils.ts b/src/plugins/unified_search/public/filters_builder/filters_builder_utils.ts index dbbc81824a479..4d28d091341b1 100644 --- a/src/plugins/unified_search/public/filters_builder/filters_builder_utils.ts +++ b/src/plugins/unified_search/public/filters_builder/filters_builder_utils.ts @@ -7,10 +7,10 @@ */ import { DataViewField } from '@kbn/data-views-plugin/common'; -import type { Filter } from '@kbn/es-query'; +import type { Filter, FilterItem } from '@kbn/es-query'; import { cloneDeep } from 'lodash'; -import { ConditionTypes, getConditionalOperationType, isOrFilter, buildOrFilter } from '../utils'; -import type { FilterItem } from '../utils'; +import { buildCombinedFilter, isCombinedFilter } from '@kbn/es-query'; +import { ConditionTypes, getConditionalOperationType } from '../utils'; import type { Operator } from '../filter_bar/filter_editor'; const PATH_SEPARATOR = '.'; @@ -66,8 +66,8 @@ export const normalizeFilters = (filters: FilterItem[]) => { const doRecursive = (f: FilterItem, parent: FilterItem) => { if (Array.isArray(f)) { return normalizeArray(f, parent); - } else if (isOrFilter(f)) { - return normalizeOr(f); + } else if (isCombinedFilter(f)) { + return normalizeCombined(f); } return f; }; @@ -92,17 +92,17 @@ export const normalizeFilters = (filters: FilterItem[]) => { return Array.isArray(parent) ? partiallyNormalized.flat() : partiallyNormalized; }; - const normalizeOr = (orFilter: Filter): FilterItem => { - const orFilters = getGroupedFilters(orFilter); - if (orFilters.length < 2) { - return orFilters[0]; + const normalizeCombined = (combinedFilter: Filter): FilterItem => { + const combinedFilters = getGroupedFilters(combinedFilter); + if (combinedFilters.length < 2) { + return combinedFilters[0]; } return { - ...orFilter, + ...combinedFilter, meta: { - ...orFilter.meta, - params: doRecursive(orFilters, orFilter), + ...combinedFilter.meta, + params: doRecursive(combinedFilters, combinedFilter), }, }; }; @@ -138,7 +138,7 @@ export const addFilter = ( if (parentConditionType !== conditionalType) { if (conditionalType === ConditionTypes.OR) { - targetArray.splice(selector, 1, buildOrFilter([targetArray[selector], filter])); + targetArray.splice(selector, 1, buildCombinedFilter([targetArray[selector], filter])); } if (conditionalType === ConditionTypes.AND) { targetArray.splice(selector, 1, [targetArray[selector], filter]); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 54343ec245efe..02c22fe20586e 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -493,7 +493,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
{!isCompactFocused && ( @@ -566,7 +566,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ data-test-subj="unifiedTextLangEditor-expand" css={{ borderRadius: 0, - backgroundColor: '#e9edf3', + backgroundColor: isDark ? euiTheme.colors.lightestShade : '#e9edf3', border: '1px solid rgb(17 43 134 / 10%) !important', }} /> @@ -602,7 +602,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ css={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0, - backgroundColor: '#e9edf3', + backgroundColor: isDark ? euiTheme.colors.lightestShade : '#e9edf3', border: '1px solid rgb(17 43 134 / 10%) !important', borderLeft: 'transparent !important', }} 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/unified_search/public/types.ts b/src/plugins/unified_search/public/types.ts index 85f32a9d4b7e3..5142cd323c136 100755 --- a/src/plugins/unified_search/public/types.ts +++ b/src/plugins/unified_search/public/types.ts @@ -7,6 +7,7 @@ */ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -87,5 +88,6 @@ export interface IUnifiedSearchPluginServices extends Partial { docLinks: DocLinksStart; data: DataPublicPluginStart; dataViews: DataViewsPublicPluginStart; + dataViewEditor: DataViewEditorStart; usageCollection?: UsageCollectionStart; } diff --git a/src/plugins/unified_search/public/utils/or_filter.ts b/src/plugins/unified_search/public/utils/combined_filter.ts similarity index 54% rename from src/plugins/unified_search/public/utils/or_filter.ts rename to src/plugins/unified_search/public/utils/combined_filter.ts index 419e4f04d74ae..05810ed55f7c2 100644 --- a/src/plugins/unified_search/public/utils/or_filter.ts +++ b/src/plugins/unified_search/public/utils/combined_filter.ts @@ -6,20 +6,13 @@ * Side Public License, v 1. */ -// Methods from this file will be removed after they are moved to the package -import { buildEmptyFilter, Filter } from '@kbn/es-query'; +import { isCombinedFilter, FilterItem } from '@kbn/es-query'; export enum ConditionTypes { OR = 'OR', AND = 'AND', } -/** @internal **/ -export type FilterItem = Filter | FilterItem[]; - -/** to: @kbn/es-query **/ -export const isOrFilter = (filter: Filter) => Boolean(filter?.meta?.type === 'OR'); - /** * Defines a conditional operation type (AND/OR) from the filter otherwise returns undefined. * @param {FilterItem} filter @@ -27,21 +20,7 @@ export const isOrFilter = (filter: Filter) => Boolean(filter?.meta?.type === 'OR export const getConditionalOperationType = (filter: FilterItem) => { if (Array.isArray(filter)) { return ConditionTypes.AND; - } else if (isOrFilter(filter)) { + } else if (isCombinedFilter(filter)) { return ConditionTypes.OR; } }; - -/** to: @kbn/es-query **/ -export const buildOrFilter = (filters: FilterItem) => { - const filter = buildEmptyFilter(false); - - return { - ...filter, - meta: { - ...filter.meta, - type: 'OR', - params: filters, - }, - }; -}; diff --git a/src/plugins/unified_search/public/utils/index.ts b/src/plugins/unified_search/public/utils/index.ts index 395304c48a914..0e3ac5f05c20c 100644 --- a/src/plugins/unified_search/public/utils/index.ts +++ b/src/plugins/unified_search/public/utils/index.ts @@ -9,10 +9,4 @@ export { onRaf } from './on_raf'; export { shallowEqual } from './shallow_equal'; -export type { FilterItem } from './or_filter'; -export { - ConditionTypes, - isOrFilter, - getConditionalOperationType, - buildOrFilter, -} from './or_filter'; +export { ConditionTypes, getConditionalOperationType } from './combined_filter'; 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/vis_types/timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts index 487ad390a62dc..a0885d792ad3f 100644 --- a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts +++ b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts @@ -102,16 +102,9 @@ export function getTimelionRequestHandler({ const esQueryConfigs = getEsQueryConfig(uiSettings); - // parse the time range client side to make sure it behaves like other charts - const timeRangeBounds = timefilter.calculateBounds(timeRange); - const untrackSearch = - dataSearch.session.isCurrentSession(searchSessionId) && - dataSearch.session.trackSearch({ - abort: () => abortController.abort(), - }); - - try { - const searchSessionOptions = dataSearch.session.getSearchOptions(searchSessionId); + const doSearch = async ( + searchOptions: ReturnType + ): Promise => { return await http.post('/api/timelion/run', { body: JSON.stringify({ sheet: [expression], @@ -126,14 +119,40 @@ export function getTimelionRequestHandler({ interval: visParams.interval, timezone, }, - ...(searchSessionOptions && { - searchSession: searchSessionOptions, - }), + ...(searchOptions + ? { + searchSession: searchOptions, + } + : {}), }), context: executionContext, signal: abortController.signal, }); + }; + + // parse the time range client side to make sure it behaves like other charts + const timeRangeBounds = timefilter.calculateBounds(timeRange); + const searchTracker = dataSearch.session.isCurrentSession(searchSessionId) + ? dataSearch.session.trackSearch({ + abort: () => abortController.abort(), + poll: async () => { + // don't use, keep this empty, onSavingSession is used instead + }, + onSavingSession: async (searchSessionOptions) => { + await doSearch(searchSessionOptions); + }, + }) + : undefined; + + try { + const searchSessionOptions = dataSearch.session.getSearchOptions(searchSessionId); + const visData = await doSearch(searchSessionOptions); + + searchTracker?.complete(); + return visData; } catch (e) { + searchTracker?.error(); + if (e && e.body) { const err = new Error( `${i18n.translate('timelion.requestHandlerErrorTitle', { @@ -146,10 +165,6 @@ export function getTimelionRequestHandler({ throw e; } } finally { - if (untrackSearch && dataSearch.session.isCurrentSession(searchSessionId)) { - // call `untrack` if this search still belongs to current session - untrackSearch(); - } expressionAbortSignal.removeEventListener('abort', expressionAbortHandler); } }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts new file mode 100644 index 0000000000000..cbc899981717b --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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/public'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { TSVB_METRIC_TYPES } from '../../../common/enums'; +import { Metric } from '../../../common/types'; +import { convertToLens } from '.'; +import { createPanel, createSeries } from '../lib/__mocks__'; +import { AvgColumn } from '../lib/convert'; + +const mockGetMetricsColumns = jest.fn(); +const mockGetBucketsColumns = jest.fn(); +const mockGetConfigurationForGauge = jest.fn(); +const mockIsValidMetrics = jest.fn(); +const mockGetDatasourceValue = jest + .fn() + .mockImplementation(() => Promise.resolve(stubLogstashDataView)); +const mockGetDataSourceInfo = jest.fn(); +const mockGetSeriesAgg = jest.fn(); + +jest.mock('../../services', () => ({ + getDataViewsStart: jest.fn(() => mockGetDatasourceValue), +})); + +jest.mock('../lib/series', () => ({ + getMetricsColumns: jest.fn(() => mockGetMetricsColumns()), + getBucketsColumns: jest.fn(() => mockGetBucketsColumns()), + getSeriesAgg: jest.fn(() => mockGetSeriesAgg()), +})); + +jest.mock('../lib/configurations/metric', () => ({ + getConfigurationForGauge: jest.fn(() => mockGetConfigurationForGauge()), +})); + +jest.mock('../lib/metrics', () => { + const actual = jest.requireActual('../lib/metrics'); + return { + isValidMetrics: jest.fn(() => mockIsValidMetrics()), + getReducedTimeRange: jest.fn().mockReturnValue('10'), + SUPPORTED_METRICS: actual.SUPPORTED_METRICS, + getFormulaFromMetric: actual.getFormulaFromMetric, + }; +}); + +jest.mock('../lib/datasource', () => ({ + getDataSourceInfo: jest.fn(() => mockGetDataSourceInfo()), +})); + +describe('convertToLens', () => { + const metric = { id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }; + const model = createPanel({ + series: [createSeries({ metrics: [metric] })], + }); + + const metricColumn: AvgColumn = { + columnId: 'col-id', + dataType: 'number', + isSplit: false, + isBucketed: false, + meta: { metricId: metric.id }, + operationType: 'average', + sourceField: metric.field, + params: {}, + reducedTimeRange: '10m', + }; + + beforeEach(() => { + mockIsValidMetrics.mockReturnValue(true); + mockGetDataSourceInfo.mockReturnValue({ + indexPatternId: 'test-index-pattern', + timeField: 'timeField', + indexPattern: { id: 'test-index-pattern' }, + }); + mockGetMetricsColumns.mockReturnValue([{}]); + mockGetBucketsColumns.mockReturnValue([{}]); + mockGetConfigurationForGauge.mockReturnValue({}); + mockGetSeriesAgg.mockReturnValue({ metrics: [] }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should return null for invalid metrics', async () => { + mockIsValidMetrics.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockIsValidMetrics).toBeCalledTimes(1); + }); + + test('should return null for invalid or unsupported metrics', async () => { + mockGetMetricsColumns.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockGetMetricsColumns).toBeCalledTimes(1); + }); + + test('should return null for invalid or unsupported buckets', async () => { + mockGetBucketsColumns.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockGetBucketsColumns).toBeCalledTimes(1); + }); + + test('should return null if metric is staticValue', async () => { + const result = await convertToLens({ + ...model, + series: [ + { + ...model.series[0], + metrics: [...model.series[0].metrics, { type: TSVB_METRIC_TYPES.STATIC } as Metric], + }, + ], + }); + expect(result).toBeNull(); + expect(mockGetDataSourceInfo).toBeCalledTimes(0); + }); + + test('should return null if only series agg is specified', async () => { + const result = await convertToLens({ + ...model, + series: [ + { + ...model.series[0], + metrics: [ + { type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min', id: 'some-id' } as Metric, + ], + }, + ], + }); + expect(result).toBeNull(); + }); + + test('should return null configuration is not valid', async () => { + mockGetMetricsColumns.mockReturnValue([metricColumn]); + mockGetSeriesAgg.mockReturnValue({ metrics: [metric] }); + mockGetConfigurationForGauge.mockReturnValue(null); + + const result = await convertToLens(model); + expect(result).toBeNull(); + }); + + test('should return state', async () => { + mockGetMetricsColumns.mockReturnValue([metricColumn]); + mockGetSeriesAgg.mockReturnValue({ metrics: [metric] }); + mockGetConfigurationForGauge.mockReturnValue({}); + + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + ], + }) + ); + expect(result).toBeDefined(); + expect(result?.type).toBe('lnsMetric'); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts new file mode 100644 index 0000000000000..b97f8d59e9537 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import uuid from 'uuid'; +import { parseTimeShift } from '@kbn/data-plugin/common'; +import { + FormulaColumn, + getIndexPatternIds, + StaticValueColumn, +} from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { PANEL_TYPES, TSVB_METRIC_TYPES } from '../../../common/enums'; +import { Metric } from '../../../common/types'; +import { getDataViewsStart } from '../../services'; +import { getDataSourceInfo } from '../lib/datasource'; +import { getMetricsColumns, getBucketsColumns } from '../lib/series'; +import { getConfigurationForGauge as getConfiguration } from '../lib/configurations/metric'; +import { + getFormulaFromMetric, + getReducedTimeRange, + isValidMetrics, + SUPPORTED_METRICS, +} from '../lib/metrics'; +import { ConvertTsvbToLensVisualization } from '../types'; +import { + Column, + createFormulaColumnWithoutMeta, + createStaticValueColumn, + Layer as ExtendedLayer, +} from '../lib/convert'; +import { excludeMetaFromLayers, findMetricColumn, getMetricWithCollapseFn } from '../utils'; + +const getMaxFormula = (metric: Metric, column?: Column) => { + const baseFormula = `overall_max`; + if (column && column.operationType === 'formula') { + return `${baseFormula}(${column.params.formula})`; + } + + return `${baseFormula}(${getFormulaFromMetric(SUPPORTED_METRICS[metric.type]!)}(${ + metric.field ?? '' + }))`; +}; + +export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => { + const dataViews = getDataViewsStart(); + + const series = model.series[0]; + // not valid time shift + if (series.offset_time && parseTimeShift(series.offset_time) === 'invalid') { + return null; + } + + if (!isValidMetrics(series.metrics, PANEL_TYPES.GAUGE, series.time_range_mode)) { + return null; + } + + if (series.metrics[series.metrics.length - 1].type === TSVB_METRIC_TYPES.STATIC) { + return null; + } + + const reducedTimeRange = getReducedTimeRange(model, series, timeRange); + const datasourceInfo = await getDataSourceInfo( + model.index_pattern, + model.time_field, + Boolean(series.override_index_pattern), + series.series_index_pattern, + series.series_time_field, + dataViews + ); + + if (!datasourceInfo) { + return null; + } + + const { indexPatternId, indexPattern } = datasourceInfo; + + // handle multiple metrics + const metricsColumns = getMetricsColumns(series, indexPattern!, model.series.length, { + reducedTimeRange, + }); + if (metricsColumns === null) { + return null; + } + + const bucketsColumns = getBucketsColumns(model, series, metricsColumns, indexPattern!, false); + + if (bucketsColumns === null) { + return null; + } + + const [bucket] = bucketsColumns; + + const extendedLayer: ExtendedLayer = { + indexPatternId, + layerId: uuid(), + columns: [...metricsColumns, ...(bucket ? [bucket] : [])], + columnOrder: [], + }; + + const primarySeries = model.series[0]; + const primaryMetricWithCollapseFn = getMetricWithCollapseFn(primarySeries); + + if (!primaryMetricWithCollapseFn || !primaryMetricWithCollapseFn.metric) { + return null; + } + + const primaryColumn = findMetricColumn(primaryMetricWithCollapseFn.metric, extendedLayer.columns); + if (!primaryColumn) { + return null; + } + + let gaugeMaxColumn: StaticValueColumn | FormulaColumn | null = createFormulaColumnWithoutMeta( + getMaxFormula(primaryMetricWithCollapseFn.metric, primaryColumn) + ); + if (model.gauge_max !== undefined && model.gauge_max !== '') { + gaugeMaxColumn = createStaticValueColumn(model.gauge_max); + } + + const layer = { + ...extendedLayer, + columns: [...extendedLayer.columns, gaugeMaxColumn], + }; + const configuration = getConfiguration(model, layer, bucket, gaugeMaxColumn ?? undefined); + if (!configuration) { + return null; + } + + const layers = Object.values(excludeMetaFromLayers({ 0: layer })); + return { + type: 'lnsMetric', + layers, + configuration, + indexPatternIds: getIndexPatternIds(layers), + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts index a64118a1cb507..a3d08e89e91a2 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts @@ -25,6 +25,10 @@ const getConvertFnByType = (type: PANEL_TYPES) => { const { convertToLens } = await import('./metric'); return convertToLens; }, + [PANEL_TYPES.GAUGE]: async () => { + const { convertToLens } = await import('./gauge'); + return convertToLens; + }, }; return convertionFns[type]?.(); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts new file mode 100644 index 0000000000000..9cb0238f9d265 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts @@ -0,0 +1,344 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { TSVB_METRIC_TYPES } from '../../../../../common/enums'; +import { Column, FormulaColumn, Layer } from '../../convert'; +import { createPanel, createSeries } from '../../__mocks__'; +import { getConfigurationForMetric, getConfigurationForGauge } from '.'; + +const mockGetPalette = jest.fn(); + +jest.mock('./palette', () => ({ + getPalette: jest.fn(() => mockGetPalette()), +})); + +describe('getConfigurationForMetric', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockGetPalette.mockReturnValue(undefined); + }); + + const metricId = 'some-id'; + const metric = { id: metricId, type: METRIC_TYPES.COUNT }; + test('should return null if no series was provided', () => { + const layerId = 'layer-id-1'; + const model = createPanel({ series: [] }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer); + + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(0); + }); + + test('should return null if only series agg', () => { + const layerId = 'layer-id-1'; + const metric1 = { id: 'metric-id-2', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min' }; + const model = createPanel({ + series: [createSeries({ metrics: [metric1] })], + }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer); + + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(0); + }); + + test('should return null if multiple series aggs', () => { + const layerId = 'layer-id-1'; + const metric1 = { id: 'metric-id-1', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'sum' }; + const metric2 = { id: 'metric-id-2', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min' }; + const model = createPanel({ + series: [ + createSeries({ metrics: [metric, metric1] }), + createSeries({ metrics: [metric, metric2] }), + ], + }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer); + + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(0); + }); + + test('should return config if only one series agg is specified', () => { + const layerId = 'layer-id-1'; + const metricId1 = 'metric-id-1'; + + const metricId2 = 'metric-id-2'; + const metric1 = { id: metricId1, type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'sum' }; + const metric2 = { ...metric, id: metricId2 }; + const columnId1 = 'col-id-1'; + const columnId2 = 'col-id-2'; + + const model = createPanel({ + series: [createSeries({ metrics: [metric, metric1] }), createSeries({ metrics: [metric2] })], + }); + const layer: Layer = { + columns: [ + { + columnId: columnId1, + meta: { metricId }, + }, + { + columnId: columnId2, + meta: { metricId: metricId2 }, + }, + ] as Column[], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer); + + expect(config).toEqual({ + breakdownByAccessor: undefined, + collapseFn: 'sum', + layerId, + layerType: 'data', + metricAccessor: columnId1, + palette: undefined, + secondaryMetricAccessor: columnId2, + }); + expect(mockGetPalette).toBeCalledTimes(1); + }); + + test('should return config for single metric', () => { + const layerId = 'layer-id-1'; + const columnId = 'col-id-1'; + const bucketColumnId = 'col-id-2'; + const model = createPanel({ + series: [createSeries({ metrics: [metric] })], + }); + const bucket = { columnId: bucketColumnId } as Column; + const layer: Layer = { + columns: [ + { + columnId, + operationType: 'count', + dataType: 'number', + params: {}, + sourceField: 'document', + isBucketed: false, + isSplit: false, + meta: { metricId }, + }, + ], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer, bucket); + + expect(config).toEqual({ + layerId, + layerType: 'data', + metricAccessor: columnId, + breakdownByAccessor: bucketColumnId, + collapseFn: undefined, + palette: undefined, + secondaryMetricAccessor: undefined, + }); + expect(mockGetPalette).toBeCalledTimes(1); + }); + + test('should return null if palette is invalid', () => { + mockGetPalette.mockReturnValue(null); + const layerId = 'layer-id-1'; + const columnId = 'col-id-1'; + const model = createPanel({ + series: [createSeries({ metrics: [metric] })], + }); + const layer: Layer = { + columns: [ + { + columnId, + operationType: 'count', + dataType: 'number', + params: {}, + sourceField: 'document', + isBucketed: false, + isSplit: false, + meta: { metricId }, + }, + ], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForMetric(model, layer); + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(1); + }); +}); + +describe('getConfigurationForGauge', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockGetPalette.mockReturnValue(undefined); + }); + + const metricId = 'some-id'; + const maxColumnId = 'col-id-1'; + const metric = { id: metricId, type: METRIC_TYPES.COUNT }; + const gaugeMaxColumn: FormulaColumn = { + references: [], + columnId: maxColumnId, + operationType: 'formula', + isBucketed: false, + isSplit: false, + dataType: 'number', + params: { formula: '100' }, + meta: { metricId }, + }; + + test('should return null if no series was provided', () => { + const layerId = 'layer-id-1'; + const model = createPanel({ series: [] }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn); + + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(0); + }); + + test('should return null if only series agg', () => { + const layerId = 'layer-id-1'; + const metric1 = { id: 'metric-id-2', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min' }; + const model = createPanel({ + series: [createSeries({ metrics: [metric1] })], + }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn); + + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(0); + }); + + test('should return null if palette is invalid', () => { + mockGetPalette.mockReturnValueOnce(null); + const layerId = 'layer-id-1'; + const columnId = 'col-id-1'; + const model = createPanel({ + series: [createSeries({ metrics: [metric] })], + }); + const layer: Layer = { + columns: [ + { + columnId, + operationType: 'count', + dataType: 'number', + params: {}, + sourceField: 'document', + isBucketed: false, + isSplit: false, + meta: { metricId }, + }, + ], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn); + expect(config).toBeNull(); + expect(mockGetPalette).toBeCalledTimes(1); + }); + + test('should return config with color if palette is not valid', () => { + const layerId = 'layer-id-1'; + const metric1 = { id: 'metric-id-1', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'sum' }; + const color = '#fff'; + const model = createPanel({ series: [createSeries({ metrics: [metric, metric1], color })] }); + const layer: Layer = { + columns: [], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn); + + expect(config).toEqual({ + breakdownByAccessor: undefined, + collapseFn: 'sum', + layerId: 'layer-id-1', + layerType: 'data', + metricAccessor: undefined, + palette: undefined, + maxAccessor: maxColumnId, + color: '#FFFFFF', + }); + expect(mockGetPalette).toBeCalledTimes(1); + }); + + test('should return config with palette', () => { + const palette = { type: 'custom', name: 'default', params: {} }; + mockGetPalette.mockReturnValue(palette); + const layerId = 'layer-id-1'; + const columnId1 = 'col-id-1'; + + const metric1 = { id: 'metric-id-1', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'sum' }; + const color = '#fff'; + const model = createPanel({ series: [createSeries({ metrics: [metric, metric1], color })] }); + const bucketColumnId = 'bucket-column-id-1'; + const bucket = { columnId: bucketColumnId } as Column; + const layer: Layer = { + columns: [ + { + columnId: columnId1, + operationType: 'count', + dataType: 'number', + params: {}, + sourceField: 'document', + isBucketed: false, + isSplit: false, + meta: { metricId }, + }, + ], + columnOrder: [], + indexPatternId: 'some-index-pattern', + layerId, + }; + const config = getConfigurationForGauge(model, layer, bucket, gaugeMaxColumn); + + expect(config).toEqual({ + breakdownByAccessor: bucket.columnId, + collapseFn: 'sum', + layerId, + layerType: 'data', + metricAccessor: columnId1, + palette, + maxAccessor: maxColumnId, + }); + expect(mockGetPalette).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts index d1f24485d7646..7b49d604b2343 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts @@ -6,28 +6,12 @@ * Side Public License, v 1. */ +import color from 'color'; import { MetricVisConfiguration } from '@kbn/visualizations-plugin/common'; -import { Metric, Panel, Series } from '../../../../../common/types'; +import { Panel } from '../../../../../common/types'; import { Column, Layer } from '../../convert'; -import { getSeriesAgg } from '../../series'; import { getPalette } from './palette'; - -const getMetricWithCollapseFn = (series: Series | undefined) => { - if (!series) { - return; - } - const { metrics, seriesAgg } = getSeriesAgg(series.metrics); - const visibleMetric = metrics[metrics.length - 1]; - return { metric: visibleMetric, collapseFn: seriesAgg }; -}; - -const findMetricColumn = (metric: Metric | undefined, columns: Column[]) => { - if (!metric) { - return; - } - - return columns.find((column) => 'meta' in column && column.meta.metricId === metric.id); -}; +import { findMetricColumn, getMetricWithCollapseFn } from '../../../utils'; export const getConfigurationForMetric = ( model: Panel, @@ -37,7 +21,6 @@ export const getConfigurationForMetric = ( const [primarySeries, secondarySeries] = model.series.filter(({ hidden }) => !hidden); const primaryMetricWithCollapseFn = getMetricWithCollapseFn(primarySeries); - if (!primaryMetricWithCollapseFn || !primaryMetricWithCollapseFn.metric) { return null; } @@ -45,7 +28,6 @@ export const getConfigurationForMetric = ( const secondaryMetricWithCollapseFn = getMetricWithCollapseFn(secondarySeries); const primaryColumn = findMetricColumn(primaryMetricWithCollapseFn.metric, layer.columns); const secondaryColumn = findMetricColumn(secondaryMetricWithCollapseFn?.metric, layer.columns); - if (primaryMetricWithCollapseFn.collapseFn && secondaryMetricWithCollapseFn?.collapseFn) { return null; } @@ -65,3 +47,35 @@ export const getConfigurationForMetric = ( collapseFn: primaryMetricWithCollapseFn.collapseFn ?? secondaryMetricWithCollapseFn?.collapseFn, }; }; + +export const getConfigurationForGauge = ( + model: Panel, + layer: Layer, + bucket: Column | undefined, + gaugeMaxColumn: Column +): MetricVisConfiguration | null => { + const primarySeries = model.series[0]; + const primaryMetricWithCollapseFn = getMetricWithCollapseFn(primarySeries); + if (!primaryMetricWithCollapseFn || !primaryMetricWithCollapseFn.metric) { + return null; + } + + const primaryColumn = findMetricColumn(primaryMetricWithCollapseFn.metric, layer.columns); + const primaryColor = primarySeries.color ? color(primarySeries.color).hex() : undefined; + + const gaugePalette = getPalette(model.gauge_color_rules ?? [], primaryColor); + if (gaugePalette === null) { + return null; + } + + return { + layerId: layer.layerId, + layerType: 'data', + metricAccessor: primaryColumn?.columnId, + breakdownByAccessor: bucket?.columnId, + maxAccessor: gaugeMaxColumn.columnId, + palette: gaugePalette, + collapseFn: primaryMetricWithCollapseFn.collapseFn, + ...(gaugePalette ? {} : { color: primaryColor }), + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts index 827dc15ff171b..b7356f094f91a 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts @@ -9,6 +9,7 @@ import { getPalette } from './palette'; describe('getPalette', () => { + const baseColor = '#fff'; const invalidRules = [ { id: 'some-id-0' }, { id: 'some-id-1', value: 10 }, @@ -16,162 +17,346 @@ describe('getPalette', () => { { id: 'some-id-3', color: '#000' }, { id: 'some-id-4', background_color: '#000' }, ]; - test('should return undefined if no filled rules was provided', () => { - expect(getPalette([])).toBeUndefined(); - expect(getPalette(invalidRules)).toBeUndefined(); - }); - test('should return undefined if only one valid rule is provided and it is not lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'gt', value: 100, background_color: '#000' }, - ]) - ).toBeUndefined(); - }); + describe('Metric', () => { + test('should return undefined if no filled rules was provided', () => { + expect(getPalette([])).toBeUndefined(); + expect(getPalette(invalidRules)).toBeUndefined(); + }); + + test('should return undefined if only one valid rule is provided and it is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gt', value: 100, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); - test('should return custom palette if only one valid rule is provided and it is lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, - ]) - ).toEqual({ - name: 'custom', - params: { - colorStops: [{ color: '#000000', stop: 100 }], - continuity: 'below', - maxSteps: 5, + test('should return custom palette if only one valid rule is provided and it is lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + ]) + ).toEqual({ name: 'custom', - progression: 'fixed', - rangeMax: 100, - rangeMin: -Infinity, - rangeType: 'number', - reverse: false, - steps: 1, - stops: [{ color: '#000000', stop: 100 }], - }, - type: 'palette', + params: { + colorStops: [{ color: '#000000', stop: 100 }], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 100, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 1, + stops: [{ color: '#000000', stop: 100 }], + }, + type: 'palette', + }); }); - }); - test('should return undefined if more than two types of rules', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, - { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, - { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, - ]) - ).toBeUndefined(); - }); + test('should return undefined if more than two types of rules', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); - test('should return undefined if two types of rules and last rule is not lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, - { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, - { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, - ]) - ).toBeUndefined(); - }); + test('should return undefined if two types of rules and last rule is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); - test('should return undefined if all rules are lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, - { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, - { id: 'some-id-6', operator: 'lte', value: 150, background_color: '#000' }, - ]) - ).toBeUndefined(); - }); + test('should return undefined if all rules are lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lte', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); - test('should return undefined if two types of rules and all except last one are lt and last one is not lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, - { id: 'some-id-7', operator: 'gte', value: 200, background_color: '#000' }, - { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, - ]) - ).toBeUndefined(); - }); + test('should return undefined if two types of rules and all except last one are lt and last one is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'gte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); - test('should return custom palette if two types of rules and all except last one is lt and last one is lte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, - { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, - { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, - ]) - ).toEqual({ - name: 'custom', - params: { - colorStops: [ - { color: '#000000', stop: -Infinity }, - { color: '#000000', stop: 100 }, - { color: '#000000', stop: 150 }, - ], - continuity: 'below', - maxSteps: 5, + test('should return custom palette if two types of rules and all except last one is lt and last one is lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, + ]) + ).toEqual({ name: 'custom', - progression: 'fixed', - rangeMax: 200, - rangeMin: -Infinity, - rangeType: 'number', - reverse: false, - steps: 4, - stops: [ - { color: '#000000', stop: 100 }, - { color: '#000000', stop: 150 }, - { color: '#000000', stop: 200 }, - ], - }, - type: 'palette', + params: { + colorStops: [ + { color: '#000000', stop: -Infinity }, + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 4, + stops: [ + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); + }); + + test('should return custom palette if last one is lte and all previous are gte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + ]) + ).toEqual({ + name: 'custom', + params: { + colorStops: [ + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'none', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: 100, + rangeType: 'number', + reverse: false, + steps: 2, + stops: [ + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); }); }); - test('should return custom palette if last one is lte and all previous are gte', () => { - expect(getPalette([])).toBeUndefined(); - expect( - getPalette([ - ...invalidRules, - { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, - { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, - { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, - ]) - ).toEqual({ - name: 'custom', - params: { - colorStops: [ - { color: '#000000', stop: 100 }, - { color: '#000000', stop: 150 }, - ], - continuity: 'none', - maxSteps: 5, + describe('Gauge', () => { + test('should return undefined if no filled rules was provided', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect(getPalette(invalidRules, baseColor)).toBeUndefined(); + }); + + test('should return undefined if only one valid rule is provided and it is not lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [...invalidRules, { id: 'some-id-5', operator: 'gt', value: 100, gauge: '#000' }], + baseColor + ) + ).toBeUndefined(); + }); + + test('should return custom palette if only one valid rule is provided and it is lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [...invalidRules, { id: 'some-id-5', operator: 'lte', value: 100, gauge: '#000' }], + baseColor + ) + ).toEqual({ + name: 'custom', + params: { + colorStops: [{ color: '#000000', stop: 100 }], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 100, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 1, + stops: [{ color: '#000000', stop: 100 }], + }, + type: 'palette', + }); + }); + + test('should return undefined if more than two types of rules', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, gauge: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, gauge: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, gauge: '#000' }, + ], + baseColor + ) + ).toBeUndefined(); + }); + + test('should return undefined if two types of rules and last rule is not lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, gauge: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, gauge: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, gauge: '#000' }, + ], + baseColor + ) + ).toBeUndefined(); + }); + + test('should return undefined if all rules are lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, gauge: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, gauge: '#000' }, + { id: 'some-id-6', operator: 'lte', value: 150, gauge: '#000' }, + ], + baseColor + ) + ).toBeUndefined(); + }); + + test('should return undefined if two types of rules and all except last one are lt and last one is not lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, gauge: '#000' }, + { id: 'some-id-7', operator: 'gte', value: 200, gauge: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, gauge: '#000' }, + ], + baseColor + ) + ).toBeUndefined(); + }); + + test('should return custom palette if two types of rules and all except last one is lt and last one is lte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, gauge: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, gauge: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, gauge: '#000' }, + ], + baseColor + ) + ).toEqual({ + name: 'custom', + params: { + colorStops: [ + { color: '#000000', stop: -Infinity }, + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 4, + stops: [ + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); + }); + + test('should return custom palette if last one is lte and all previous are gte', () => { + expect(getPalette([], baseColor)).toBeUndefined(); + expect( + getPalette( + [ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, gauge: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, gauge: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, gauge: '#000' }, + ], + baseColor + ) + ).toEqual({ name: 'custom', - progression: 'fixed', - rangeMax: 200, - rangeMin: 100, - rangeType: 'number', - reverse: false, - steps: 2, - stops: [ - { color: '#000000', stop: 150 }, - { color: '#000000', stop: 200 }, - ], - }, - type: 'palette', + params: { + colorStops: [ + { color: baseColor, stop: -Infinity }, + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 3, + stops: [ + { color: baseColor, stop: 100 }, + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); }); }); }); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts index 55741c57595e7..4079ffb396647 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts @@ -22,15 +22,61 @@ type ColorStopsWithMinMax = Pick< 'colorStops' | 'stops' | 'steps' | 'rangeMax' | 'rangeMin' | 'continuity' >; +type MetricColorRules = Exclude; +type GaugeColorRules = Exclude; + +type MetricColorRule = MetricColorRules[number]; +type GaugeColorRule = GaugeColorRules[number]; + +type ValidMetricColorRule = Omit & + ( + | { + background_color: Exclude; + color: MetricColorRule['color']; + } + | { + background_color: MetricColorRule['background_color']; + color: Exclude; + } + ); + +type ValidGaugeColorRule = Omit & { + gauge: Exclude; +}; + +const isValidColorRule = ( + rule: MetricColorRule | GaugeColorRule +): rule is ValidMetricColorRule | ValidGaugeColorRule => { + const { background_color: bColor, color: textColor } = rule as MetricColorRule; + const { gauge } = rule as GaugeColorRule; + + return rule.operator && (bColor ?? textColor ?? gauge) && rule.value !== undefined ? true : false; +}; + +const isMetricColorRule = ( + rule: ValidMetricColorRule | ValidGaugeColorRule +): rule is ValidMetricColorRule => { + const metricRule = rule as ValidMetricColorRule; + return metricRule.background_color ?? metricRule.color ? true : false; +}; + +const getColor = (rule: ValidMetricColorRule | ValidGaugeColorRule) => { + if (isMetricColorRule(rule)) { + return rule.background_color ?? rule.color; + } + return rule.gauge; +}; + const getColorStopsWithMinMaxForAllGteOrWithLte = ( - rules: Exclude, - tailOperator: string + rules: Array, + tailOperator: string, + baseColor?: string ): ColorStopsWithMinMax => { const lastRule = rules[rules.length - 1]; - const lastRuleColor = (lastRule.background_color ?? lastRule.color)!; - + const lastRuleColor = getColor(lastRule); + const initRules = baseColor ? [{ stop: -Infinity, color: baseColor }] : []; const colorStops = rules.reduce((colors, rule, index, rulesArr) => { - const rgbColor = (rule.background_color ?? rule.color)!; + const rgbColor = getColor(rule); if (index === rulesArr.length - 1 && tailOperator === Operators.LTE) { return colors; } @@ -51,7 +97,7 @@ const getColorStopsWithMinMaxForAllGteOrWithLte = ( stop: rule.value!, }, ]; - }, []); + }, initRules); const stops = colorStops.reduce((prevStops, colorStop, index, colorStopsArr) => { if (index === colorStopsArr.length - 1) { @@ -68,24 +114,25 @@ const getColorStopsWithMinMaxForAllGteOrWithLte = ( const [rule] = rules; return { - rangeMin: rule.value, + rangeMin: baseColor ? -Infinity : rule.value, rangeMax: tailOperator === Operators.LTE ? lastRule.value : Infinity, colorStops, stops, steps: colorStops.length, - continuity: tailOperator === Operators.LTE ? 'none' : 'above', + continuity: + tailOperator === Operators.LTE ? (baseColor ? 'below' : 'none') : baseColor ? 'all' : 'above', }; }; const getColorStopsWithMinMaxForLtWithLte = ( - rules: Exclude + rules: Array ): ColorStopsWithMinMax => { const lastRule = rules[rules.length - 1]; const colorStops = rules.reduce((colors, rule, index, rulesArr) => { if (index === 0) { - return [{ color: color((rule.background_color ?? rule.color)!).hex(), stop: -Infinity }]; + return [{ color: color(getColor(rule)).hex(), stop: -Infinity }]; } - const rgbColor = (rule.background_color ?? rule.color)!; + const rgbColor = getColor(rule); return [ ...colors, { @@ -119,10 +166,10 @@ const getColorStopsWithMinMaxForLtWithLte = ( }; const getColorStopWithMinMaxForLte = ( - rule: Exclude[number] + rule: ValidMetricColorRule | ValidGaugeColorRule ): ColorStopsWithMinMax => { const colorStop = { - color: color((rule.background_color ?? rule.color)!).hex(), + color: color(getColor(rule)).hex(), stop: rule.value!, }; return { @@ -135,6 +182,27 @@ const getColorStopWithMinMaxForLte = ( }; }; +const getColorStopWithMinMaxForGte = ( + rule: ValidMetricColorRule | ValidGaugeColorRule, + baseColor?: string +): ColorStopsWithMinMax => { + const colorStop = { + color: color(getColor(rule)).hex(), + stop: rule.value!, + }; + return { + colorStops: [...(baseColor ? [{ color: baseColor, stop: -Infinity }] : []), colorStop], + continuity: baseColor ? 'all' : 'above', + rangeMax: Infinity, + rangeMin: baseColor ? -Infinity : colorStop.stop, + steps: 2, + stops: [ + ...(baseColor ? [{ color: baseColor, stop: colorStop.stop }] : []), + { color: colorStop.color, stop: Infinity }, + ], + }; +}; + const getCustomPalette = ( colorStopsWithMinMax: ColorStopsWithMinMax ): PaletteOutput => { @@ -156,13 +224,12 @@ const getCustomPalette = ( }; export const getPalette = ( - rules: Exclude + rules: MetricColorRules | GaugeColorRules, + baseColor?: string ): PaletteOutput | null | undefined => { - const validRules = - rules.filter( - ({ operator, color: textColor, value, background_color: bColor }) => - operator && (bColor ?? textColor) && value !== undefined - ) ?? []; + const validRules = (rules as Array).filter< + ValidMetricColorRule | ValidGaugeColorRule + >((rule): rule is ValidMetricColorRule | ValidGaugeColorRule => isValidColorRule(rule)); validRules.sort((rule1, rule2) => { return rule1.value! - rule2.value!; @@ -176,10 +243,14 @@ export const getPalette = ( // lnsMetric is supporting lte only, if one rule is defined if (validRules.length === 1) { - if (validRules[0].operator !== Operators.LTE) { - return; + if (validRules[0].operator === Operators.LTE) { + return getCustomPalette(getColorStopWithMinMaxForLte(validRules[0])); } - return getCustomPalette(getColorStopWithMinMaxForLte(validRules[0])); + + if (validRules[0].operator === Operators.GTE) { + return getCustomPalette(getColorStopWithMinMaxForGte(validRules[0], baseColor)); + } + return; } const headRules = validRules.slice(0, -1); @@ -208,7 +279,7 @@ export const getPalette = ( if (rule.operator === Operators.GTE) { return getCustomPalette( - getColorStopsWithMinMaxForAllGteOrWithLte(validRules, tailRule.operator!) + getColorStopsWithMinMaxForAllGteOrWithLte(validRules, tailRule.operator!, baseColor) ); } }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts index 6c94971397d3e..3bb4b743c2588 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts @@ -24,6 +24,33 @@ jest.mock('uuid', () => ({ v4: () => 'test-id', })); +const mockedIndices = [ + { + id: 'test', + title: 'test', + timeFieldName: 'test_field', + getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), + }, +] as unknown as DataView[]; + +const indexPatternsService = { + getDefault: jest.fn(() => + Promise.resolve({ + id: 'default', + title: 'index', + getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), + }) + ), + get: jest.fn((id) => Promise.resolve({ ...mockedIndices[0], id })), + find: jest.fn((search: string, size: number) => { + if (size !== 1) { + // shouldn't request more than one data view since there is a significant performance penalty + throw new Error('trying to fetch too many data views'); + } + return Promise.resolve(mockedIndices || []); + }), +} as unknown as DataViewsPublicPluginStart; + describe('getLayers', () => { const dataSourceLayers: Record = [ { @@ -83,7 +110,6 @@ describe('getLayers', () => { params: { value: '100', }, - meta: { metricId: 'metric-1' }, }, ], columnOrder: [], @@ -331,10 +357,16 @@ describe('getLayers', () => { series: [createSeries({ metrics: staticValueMetric })], }); - test.each<[string, [Record, Panel], Array>]>([ + test.each< + [ + string, + [Record, Panel, DataViewsPublicPluginStart, boolean], + Array> + ] + >([ [ 'data layer if columns do not include static column', - [dataSourceLayers, panel], + [dataSourceLayers, panel, indexPatternsService, false], [ { layerType: 'data', @@ -353,9 +385,30 @@ describe('getLayers', () => { }, ], ], + [ + 'data layer with "left" axisMode if isSingleAxis is provided', + [dataSourceLayers, panel, indexPatternsService, true], + [ + { + layerType: 'data', + accessors: ['column-id-1'], + xAccessor: 'column-id-2', + splitAccessor: 'column-id-3', + seriesType: 'area', + layerId: 'test-layer-1', + yConfig: [ + { + forAccessor: 'column-id-1', + axisMode: 'left', + color: '#68BC00', + }, + ], + }, + ], + ], [ 'reference line layer if columns include static column', - [dataSourceLayersWithStatic, panelWithStaticValue], + [dataSourceLayersWithStatic, panelWithStaticValue, indexPatternsService, false], [ { layerType: 'referenceLine', @@ -364,9 +417,10 @@ describe('getLayers', () => { yConfig: [ { forAccessor: 'column-id-1', - axisMode: 'right', + axisMode: 'left', color: '#68BC00', fill: 'below', + lineWidth: 1, }, ], }, @@ -374,7 +428,7 @@ describe('getLayers', () => { ], [ 'correct colors if columns include percentile columns', - [dataSourceLayersWithPercentile, panelWithPercentileMetric], + [dataSourceLayersWithPercentile, panelWithPercentileMetric, indexPatternsService, false], [ { yConfig: [ @@ -394,7 +448,12 @@ describe('getLayers', () => { ], [ 'correct colors if columns include percentile rank columns', - [dataSourceLayersWithPercentileRank, panelWithPercentileRankMetric], + [ + dataSourceLayersWithPercentileRank, + panelWithPercentileRankMetric, + indexPatternsService, + false, + ], [ { yConfig: [ @@ -414,7 +473,7 @@ describe('getLayers', () => { ], [ 'annotation layer gets correct params and converts color, extraFields and icons', - [dataSourceLayersWithStatic, panelWithSingleAnnotation], + [dataSourceLayersWithStatic, panelWithSingleAnnotation, indexPatternsService, false], [ { layerType: 'referenceLine', @@ -423,9 +482,10 @@ describe('getLayers', () => { yConfig: [ { forAccessor: 'column-id-1', - axisMode: 'right', + axisMode: 'left', color: '#68BC00', fill: 'below', + lineWidth: 1, }, ], }, @@ -459,7 +519,12 @@ describe('getLayers', () => { ], [ 'annotation layer should gets correct default params', - [dataSourceLayersWithStatic, panelWithSingleAnnotationWithoutQueryStringAndTimefield], + [ + dataSourceLayersWithStatic, + panelWithSingleAnnotationWithoutQueryStringAndTimefield, + indexPatternsService, + false, + ], [ { layerType: 'referenceLine', @@ -468,9 +533,10 @@ describe('getLayers', () => { yConfig: [ { forAccessor: 'column-id-1', - axisMode: 'right', + axisMode: 'left', color: '#68BC00', fill: 'below', + lineWidth: 1, }, ], }, @@ -504,7 +570,7 @@ describe('getLayers', () => { ], [ 'multiple annotations with different data views create separate layers', - [dataSourceLayersWithStatic, panelWithMultiAnnotations], + [dataSourceLayersWithStatic, panelWithMultiAnnotations, indexPatternsService, false], [ { layerType: 'referenceLine', @@ -513,9 +579,10 @@ describe('getLayers', () => { yConfig: [ { forAccessor: 'column-id-1', - axisMode: 'right', + axisMode: 'left', color: '#68BC00', fill: 'below', + lineWidth: 1, }, ], }, @@ -598,7 +665,12 @@ describe('getLayers', () => { ], [ 'annotation layer gets correct dataView when none is defined', - [dataSourceLayersWithStatic, panelWithSingleAnnotationDefaultDataView], + [ + dataSourceLayersWithStatic, + panelWithSingleAnnotationDefaultDataView, + indexPatternsService, + false, + ], [ { layerType: 'referenceLine', @@ -607,9 +679,10 @@ describe('getLayers', () => { yConfig: [ { forAccessor: 'column-id-1', - axisMode: 'right', + axisMode: 'left', color: '#68BC00', fill: 'below', + lineWidth: 1, }, ], }, @@ -642,34 +715,7 @@ describe('getLayers', () => { ], ], ])('should return %s', async (_, input, expected) => { - const layers = await getLayers(...input, indexPatternsService as DataViewsPublicPluginStart); + const layers = await getLayers(...input); expect(layers).toEqual(expected.map(expect.objectContaining)); }); }); - -const mockedIndices = [ - { - id: 'test', - title: 'test', - timeFieldName: 'test_field', - getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), - }, -] as unknown as DataView[]; - -const indexPatternsService = { - getDefault: jest.fn(() => - Promise.resolve({ - id: 'default', - title: 'index', - getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), - }) - ), - get: jest.fn((id) => Promise.resolve({ ...mockedIndices[0], id })), - find: jest.fn((search: string, size: number) => { - if (size !== 1) { - // shouldn't request more than one data view since there is a significant performance penalty - throw new Error('trying to fetch too many data views'); - } - return Promise.resolve(mockedIndices || []); - }), -} as unknown as DataViewsPublicPluginStart; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts index ec0e24e2db873..8784c2952807d 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts @@ -24,7 +24,7 @@ import { getDefaultQueryLanguage } from '../../../../application/components/lib/ import { fetchIndexPattern } from '../../../../../common/index_patterns_utils'; import { ICON_TYPES_MAP } from '../../../../application/visualizations/constants'; import { SUPPORTED_METRICS } from '../../metrics'; -import type { Annotation, Metric, Panel } from '../../../../../common/types'; +import type { Annotation, Metric, Panel, Series } from '../../../../../common/types'; import { getSeriesAgg } from '../../series'; import { isPercentileRanksColumnWithMeta, @@ -44,6 +44,10 @@ function getPalette(palette: PaletteOutput): PaletteOutput { : palette; } +function getAxisMode(series: Series, model: Panel): YAxisMode { + return (series.separate_axis ? series.axis_position : model.axis_position) as YAxisMode; +} + function getColor( metricColumn: Column, metric: Metric, @@ -69,7 +73,8 @@ function nonNullable(value: T): value is NonNullable { export const getLayers = async ( dataSourceLayers: Record, model: Panel, - dataViews: DataViewsPublicPluginStart + dataViews: DataViewsPublicPluginStart, + isSingleAxis: boolean = false ): Promise => { const nonAnnotationsLayers: XYLayerConfig[] = Object.keys(dataSourceLayers).map((key) => { const series = model.series[parseInt(key, 10)]; @@ -84,13 +89,13 @@ export const getLayers = async ( const metricColumns = dataSourceLayer.columns.filter( (l) => !l.isBucketed && l.columnId !== referenceColumnId ); - const isReferenceLine = metrics.length === 1 && metrics[0].type === 'static'; + const isReferenceLine = + metricColumns.length === 1 && metricColumns[0].operationType === 'static_value'; const splitAccessor = dataSourceLayer.columns.find( (column) => column.isBucketed && column.isSplit )?.columnId; const chartType = getChartType(series, model.type); const commonProps = { - seriesType: chartType, layerId: dataSourceLayer.layerId, accessors: metricColumns.map((metricColumn) => { return metricColumn.columnId; @@ -102,19 +107,19 @@ export const getLayers = async ( return { forAccessor: metricColumn.columnId, color: getColor(metricColumn, metric!, series.color, splitAccessor), - axisMode: (series.separate_axis - ? series.axis_position - : model.axis_position) as YAxisMode, + axisMode: isReferenceLine // reference line should be assigned to axis with real data + ? model.series.some((s) => s.id !== series.id && getAxisMode(s, model) === 'right') + ? 'right' + : 'left' + : isSingleAxis + ? 'left' + : getAxisMode(series, model), ...(isReferenceLine && { - fill: chartType === 'area' ? FillTypes.BELOW : FillTypes.NONE, + fill: chartType.includes('area') ? FillTypes.BELOW : FillTypes.NONE, + lineWidth: series.line_width, }), }; }), - xAccessor: dataSourceLayer.columns.find((column) => column.isBucketed && !column.isSplit) - ?.columnId, - splitAccessor, - collapseFn: seriesAgg, - palette: getPalette(series.palette as PaletteOutput), }; if (isReferenceLine) { return { @@ -123,8 +128,14 @@ export const getLayers = async ( }; } else { return { + seriesType: chartType, layerType: 'data', ...commonProps, + xAccessor: dataSourceLayer.columns.find((column) => column.isBucketed && !column.isSplit) + ?.columnId, + splitAccessor, + collapseFn: seriesAgg, + palette: getPalette(series.palette as PaletteOutput), }; } }); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/formula.ts index 730e770160ecd..15b35fade92c2 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/formula.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/formula.ts @@ -6,8 +6,12 @@ * Side Public License, v 1. */ +import uuid from 'uuid'; import { METRIC_TYPES } from '@kbn/data-plugin/public'; -import { FormulaParams } from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { + FormulaParams, + FormulaColumn as BaseFormulaColumn, +} from '@kbn/visualizations-plugin/common/convert_to_lens'; import { CommonColumnConverterArgs, CommonColumnsConverterArgs, FormulaColumn } from './types'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import type { Metric } from '../../../../common/types'; @@ -39,6 +43,19 @@ export const createFormulaColumn = ( }; }; +export const createFormulaColumnWithoutMeta = (formula: string): BaseFormulaColumn => { + const params = convertToFormulaParams(formula); + return { + columnId: uuid(), + operationType: 'formula', + references: [], + dataType: 'string', + isSplit: false, + isBucketed: false, + params: { ...params }, + }; +}; + const convertFormulaScriptForPercentileAggs = ( mathScript: string, variables: Exclude, diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts index e03701e6ea153..b1b15339b37a6 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts @@ -9,7 +9,11 @@ export { isColumnWithMeta, excludeMetaFromColumn } from './column'; export { convertToPercentileColumns, isPercentileColumnWithMeta } from './percentile'; export { convertToPercentileRankColumns, isPercentileRanksColumnWithMeta } from './percentile_rank'; -export { convertMathToFormulaColumn, convertOtherAggsToFormulaColumn } from './formula'; +export { + convertMathToFormulaColumn, + convertOtherAggsToFormulaColumn, + createFormulaColumnWithoutMeta, +} from './formula'; export { convertParentPipelineAggToColumns, convertMetricAggregationColumnWithoutSpecialParams, @@ -17,7 +21,11 @@ export { export { convertToCumulativeSumColumns } from './cumulative_sum'; export { convertFilterRatioToFormulaColumn } from './filter_ratio'; export { convertToLastValueColumn } from './last_value'; -export { convertToStaticValueColumn, convertStaticValueToFormulaColumn } from './static_value'; +export { + convertToStaticValueColumn, + createStaticValueColumn, + convertStaticValueToFormulaColumn, +} from './static_value'; export { convertToFiltersColumn } from './filters'; export { convertToDateHistogramColumn } from './date_histogram'; export { convertToTermsColumn } from './terms'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts index e03a9d7821364..d3e6aef09b1cf 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { StaticValueParams } from '@kbn/visualizations-plugin/common/convert_to_lens'; +import uuid from 'uuid'; +import { + StaticValueParams, + StaticValueColumn as BaseStaticValueColumn, +} from '@kbn/visualizations-plugin/common/convert_to_lens'; import { CommonColumnsConverterArgs, FormulaColumn, StaticValueColumn } from './types'; import type { Metric } from '../../../../common/types'; import { createColumn, getFormat } from './column'; @@ -39,6 +43,19 @@ export const convertToStaticValueColumn = ( }; }; +export const createStaticValueColumn = (staticValue: number): BaseStaticValueColumn => ({ + columnId: uuid(), + operationType: 'static_value', + references: [], + dataType: 'number', + isStaticValue: true, + isBucketed: false, + isSplit: false, + params: { + value: staticValue.toString(), + }, +}); + export const convertStaticValueToFormulaColumn = ( { series, metrics, dataView }: CommonColumnsConverterArgs, { diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts index e5b862a0fe70f..4b3b6c582f915 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts @@ -85,7 +85,13 @@ export type MovingAverageColumn = GenericColumnWithMeta; export type StaticValueColumn = GenericColumnWithMeta; -export type ColumnsWithoutMeta = FiltersColumn | TermsColumn | DateHistogramColumn; +export type ColumnsWithoutMeta = + | FiltersColumn + | TermsColumn + | DateHistogramColumn + | BaseStaticValueColumn + | BaseFormulaColumn; + export type AnyColumnWithReferences = GenericColumnWithMeta; type CommonColumns = Exclude; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts index 76d15793f4516..8be4b444be099 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts @@ -66,6 +66,7 @@ const supportedPanelTypes: readonly PANEL_TYPES[] = [ PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N, PANEL_TYPES.METRIC, + PANEL_TYPES.GAUGE, ]; const supportedTimeRangeModes: readonly TIME_RANGE_DATA_MODES[] = [ diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts index 25f55b5a1c44c..149acc513b9ff 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts @@ -17,7 +17,7 @@ import { getConfigurationForMetric as getConfiguration } from '../lib/configurat import { getReducedTimeRange, isValidMetrics } from '../lib/metrics'; import { ConvertTsvbToLensVisualization } from '../types'; import { ColumnsWithoutMeta, Layer as ExtendedLayer } from '../lib/convert'; -import { excludeMetaFromLayers, getUniqueBuckets } from './utils'; +import { excludeMetaFromLayers, getUniqueBuckets } from '../utils'; const MAX_SERIES = 2; const MAX_BUCKETS = 2; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts index 50aa1a6c6f7f4..c81db38e05384 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts @@ -112,6 +112,19 @@ describe('convertToLens', () => { expect(mockGetBucketsColumns).toBeCalledTimes(1); }); + test('should return null for static value with buckets', async () => { + mockGetBucketsColumns.mockReturnValue([{}]); + mockGetMetricsColumns.mockReturnValue([ + { + operationType: 'static_value', + }, + ]); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockGetMetricsColumns).toBeCalledTimes(1); + expect(mockGetBucketsColumns).toBeCalledTimes(1); + }); + test('should return state for valid model', async () => { const result = await convertToLens(model); expect(result).toBeDefined(); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts index 8cbbbf0f9e739..ef678fcc2dab4 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts @@ -98,11 +98,21 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model: Panel return null; } + const isReferenceLine = + metricsColumns.length === 1 && metricsColumns[0].operationType === 'static_value'; + + // only static value without split is supported + if (isReferenceLine && bucketsColumns.length) { + return null; + } + const layerId = uuid(); extendedLayers[layerIdx] = { indexPatternId, layerId, - columns: [...metricsColumns, dateHistogramColumn, ...bucketsColumns], + columns: isReferenceLine + ? [...metricsColumns] + : [...metricsColumns, dateHistogramColumn, ...bucketsColumns], columnOrder: [], }; } diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts index 020aaec28f573..130646f72f127 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts @@ -86,7 +86,7 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeR }; } - const configLayers = await getLayers(extendedLayers, model, dataViews); + const configLayers = await getLayers(extendedLayers, model, dataViews, true); if (configLayers === null) { return null; } diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/utils.test.ts similarity index 97% rename from src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/utils.test.ts index 8f880dfe95c5e..c60370871294a 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/utils.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Column, DateHistogramColumn, TermsColumn } from '../lib/convert'; +import { Column, DateHistogramColumn, TermsColumn } from './lib/convert'; import { getUniqueBuckets } from './utils'; describe('getUniqueBuckets', () => { diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/utils.ts similarity index 75% rename from src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/utils.ts index 8df1b0f40f8be..93fb56e39012c 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/utils.ts @@ -9,7 +9,14 @@ import { uniqWith } from 'lodash'; import deepEqual from 'react-fast-compare'; import { Layer, Operations, TermsColumn } from '@kbn/visualizations-plugin/common/convert_to_lens'; -import { Layer as ExtendedLayer, excludeMetaFromColumn, ColumnsWithoutMeta } from '../lib/convert'; +import { + Layer as ExtendedLayer, + excludeMetaFromColumn, + ColumnsWithoutMeta, + Column, +} from './lib/convert'; +import { getSeriesAgg } from './lib/series'; +import { Metric, Series } from '../../common/types'; export const excludeMetaFromLayers = ( layers: Record @@ -63,3 +70,20 @@ export const getUniqueBuckets = (buckets: ColumnsWithoutMeta[]) => return deepEqual(bucketWithoutColumnIds1, bucketWithoutColumnIds2); }); + +export const getMetricWithCollapseFn = (series: Series | undefined) => { + if (!series) { + return; + } + const { metrics, seriesAgg } = getSeriesAgg(series.metrics); + const visibleMetric = metrics[metrics.length - 1]; + return { metric: visibleMetric, collapseFn: seriesAgg }; +}; + +export const findMetricColumn = (metric: Metric | undefined, columns: Column[]) => { + if (!metric) { + return; + } + + return columns.find((column) => 'meta' in column && column.meta.metricId === metric.id); +}; diff --git a/src/plugins/vis_types/timeseries/public/request_handler.ts b/src/plugins/vis_types/timeseries/public/request_handler.ts index 24768dc10a17f..4512034d3b124 100644 --- a/src/plugins/vis_types/timeseries/public/request_handler.ts +++ b/src/plugins/vis_types/timeseries/public/request_handler.ts @@ -49,33 +49,46 @@ export const metricsRequestHandler = async ({ const dataSearch = data.search; const parsedTimeRange = data.query.timefilter.timefilter.calculateBounds(input?.timeRange!); + const doSearch = async ( + searchOptions: ReturnType + ): Promise => { + return await getCoreStart().http.post(ROUTES.VIS_DATA, { + body: JSON.stringify({ + timerange: { + timezone, + ...parsedTimeRange, + }, + query: input?.query, + filters: input?.filters, + panels: [visParams], + state: uiStateObj, + ...(searchOptions + ? { + searchSession: searchOptions, + } + : {}), + }), + context: executionContext, + signal: abortController.signal, + }); + }; + if (visParams && visParams.id && !visParams.isModelInvalid && !expressionAbortSignal.aborted) { - const untrackSearch = - dataSearch.session.isCurrentSession(searchSessionId) && - dataSearch.session.trackSearch({ - abort: () => abortController.abort(), - }); + const searchTracker = dataSearch.session.isCurrentSession(searchSessionId) + ? dataSearch.session.trackSearch({ + abort: () => abortController.abort(), + poll: async () => { + // don't use, keep this empty, onSavingSession is used instead + }, + onSavingSession: async (searchSessionOptions) => { + await doSearch(searchSessionOptions); + }, + }) + : undefined; try { const searchSessionOptions = dataSearch.session.getSearchOptions(searchSessionId); - - const visData: TimeseriesVisData = await getCoreStart().http.post(ROUTES.VIS_DATA, { - body: JSON.stringify({ - timerange: { - timezone, - ...parsedTimeRange, - }, - query: input?.query, - filters: input?.filters, - panels: [visParams], - state: uiStateObj, - ...(searchSessionOptions && { - searchSession: searchSessionOptions, - }), - }), - context: executionContext, - signal: abortController.signal, - }); + const visData: TimeseriesVisData = await doSearch(searchSessionOptions); inspectorAdapters?.requests?.reset(); @@ -86,12 +99,12 @@ export const metricsRequestHandler = async ({ .ok({ time: query.time, json: { rawResponse: query.response } }); }); + searchTracker?.complete(); + return visData; + } catch (e) { + searchTracker?.error(); } finally { - if (untrackSearch && dataSearch.session.isCurrentSession(searchSessionId)) { - // untrack if this search still belongs to current session - untrackSearch(); - } expressionAbortSignal.removeEventListener('abort', expressionAbortHandler); } } 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/analytics/tests/instrumented_events/from_the_browser/loaded_dashboard.ts b/test/analytics/tests/instrumented_events/from_the_browser/loaded_dashboard.ts index 0522ab5b41c2c..7b21a5637d167 100644 --- a/test/analytics/tests/instrumented_events/from_the_browser/loaded_dashboard.ts +++ b/test/analytics/tests/instrumented_events/from_the_browser/loaded_dashboard.ts @@ -26,7 +26,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardAddPanel = getService('dashboardAddPanel'); const queryBar = getService('queryBar'); - describe('Loaded Dashboard', () => { + // Failing: See https://github.com/elastic/kibana/issues/142548 + describe.skip('Loaded Dashboard', () => { let fromTimestamp: string | undefined; const getEvents = async (count: number, options?: GetEventsOptions) => @@ -200,7 +201,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); expect(event.properties.key2).to.be('num_of_panels'); - expect(event.properties.value2).to.be(17); + expect(event.properties.value2).to.be(16); }); /** diff --git a/test/api_integration/apis/saved_objects/find.ts b/test/api_integration/apis/saved_objects/find.ts index 4afcc4f162a62..5c11b6f74d7ae 100644 --- a/test/api_integration/apis/saved_objects/find.ts +++ b/test/api_integration/apis/saved_objects/find.ts @@ -338,6 +338,131 @@ export default function ({ getService }: FtrProviderContext) { }); }); + describe('`has_no_reference` and `has_no_reference_operator` parameters', () => { + before(async () => { + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json', + { space: SPACE_ID } + ); + }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json', + { space: SPACE_ID } + ); + }); + + it('search for objects not containing a reference', async () => { + await supertest + .get(`/s/${SPACE_ID}/api/saved_objects/_find`) + .query({ + type: 'visualization', + has_no_reference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), + }) + .expect(200) + .then((resp) => { + const objects = resp.body.saved_objects; + const ids = objects.map((obj: SavedObject) => obj.id); + expect(ids).to.contain('only-ref-2'); + expect(ids).to.contain('only-ref-3'); + expect(ids).not.to.contain('only-ref-1'); + expect(ids).not.to.contain('ref-1-and-ref-2'); + }); + }); + + it('search for multiple references with OR operator', async () => { + await supertest + .get(`/s/${SPACE_ID}/api/saved_objects/_find`) + .query({ + type: 'visualization', + has_no_reference: JSON.stringify([ + { type: 'ref-type', id: 'ref-1' }, + { type: 'ref-type', id: 'ref-2' }, + ]), + has_no_reference_operator: 'OR', + }) + .expect(200) + .then((resp) => { + const objects = resp.body.saved_objects; + const ids = objects.map((obj: SavedObject) => obj.id); + + expect(ids).to.contain('only-ref-3'); + expect(ids).not.to.contain('only-ref-1'); + expect(ids).not.to.contain('only-ref-2'); + expect(ids).not.to.contain('ref-1-and-ref-2'); + }); + }); + + it('search for multiple references with AND operator', async () => { + await supertest + .get(`/s/${SPACE_ID}/api/saved_objects/_find`) + .query({ + type: 'visualization', + has_no_reference: JSON.stringify([ + { type: 'ref-type', id: 'ref-1' }, + { type: 'ref-type', id: 'ref-2' }, + ]), + has_no_reference_operator: 'AND', + }) + .expect(200) + .then((resp) => { + const objects = resp.body.saved_objects; + const ids = objects.map((obj: SavedObject) => obj.id); + expect(ids).to.contain('only-ref-1'); + expect(ids).to.contain('only-ref-2'); + expect(ids).to.contain('only-ref-3'); + expect(ids).not.to.contain('ref-1-and-ref-2'); + }); + }); + }); + + describe('with both `has_reference` and `has_no_reference` parameters', () => { + before(async () => { + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json', + { space: SPACE_ID } + ); + }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json', + { space: SPACE_ID } + ); + }); + + it('search for objects containing a reference and excluding another reference', async () => { + await supertest + .get(`/s/${SPACE_ID}/api/saved_objects/_find`) + .query({ + type: 'visualization', + has_reference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), + has_no_reference: JSON.stringify({ type: 'ref-type', id: 'ref-2' }), + }) + .expect(200) + .then((resp) => { + const objects = resp.body.saved_objects; + const ids = objects.map((obj: SavedObject) => obj.id); + expect(ids).to.eql(['only-ref-1']); + }); + }); + + it('search for objects with same reference passed to `has_reference` and `has_no_reference`', async () => { + await supertest + .get(`/s/${SPACE_ID}/api/saved_objects/_find`) + .query({ + type: 'visualization', + has_reference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), + has_no_reference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), + }) + .expect(200) + .then((resp) => { + const objects = resp.body.saved_objects; + const ids = objects.map((obj: SavedObject) => obj.id); + expect(ids).to.eql([]); + }); + }); + }); + describe('searching for special characters', () => { before(async () => { await kibanaServer.importExport.load( diff --git a/test/functional/apps/console/_xjson.ts b/test/functional/apps/console/_xjson.ts index 386cda8ef32ea..1535337a2a848 100644 --- a/test/functional/apps/console/_xjson.ts +++ b/test/functional/apps/console/_xjson.ts @@ -7,6 +7,7 @@ */ import expect from '@kbn/expect'; +import { rgbToHex } from '@elastic/eui'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getService, getPageObjects }: FtrProviderContext) => { @@ -14,10 +15,9 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { const log = getService('log'); const PageObjects = getPageObjects(['common', 'console', 'header']); - describe("Console's XJSON features", function testXjson() { + describe('XJSON', function testXjson() { this.tags('includeFirefox'); before(async () => { - log.debug('navigateTo console'); await PageObjects.common.navigateToApp('console'); await retry.try(async () => { await PageObjects.console.collapseHelp(); @@ -28,22 +28,158 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { await PageObjects.console.clearTextArea(); }); - describe('with triple quoted strings', () => { + const executeRequest = async (request = '\n GET _search') => { + await PageObjects.console.enterRequest(request); + await PageObjects.console.clickPlay(); + await PageObjects.header.waitUntilLoadingHasFinished(); + }; + + describe('inline http request', () => { + it('should have method and path', async () => { + await PageObjects.console.enterRequest('\n PUT foo/bar'); + expect(await PageObjects.console.getRequestMethod()).to.be('PUT'); + expect(await PageObjects.console.getRequestPath()).to.be('foo/bar'); + }); + + it('should have optional query parameters', async () => { + await PageObjects.console.enterRequest('\n GET foo/bar?pretty'); + expect(await PageObjects.console.getRequestQueryParams()).to.be('pretty'); + }); + + it('should have optional request body', async () => { + await PageObjects.console.enterRequest('\n POST foo/bar\n {"foo": "bar"}'); + log.debug('request body: ' + (await PageObjects.console.getRequestBody())); + expect(await PageObjects.console.getRequestBody()).to.be('{"foo": "bar"}'); + }); + + it('should not have validation errors', async () => { + await PageObjects.console.enterRequest('\n GET foo/bar'); + expect(await PageObjects.console.hasErrorMarker()).to.be(false); + }); + + it('should have validation error for invalid method', async () => { + await PageObjects.console.enterRequest('\n FOO foo/bar'); + // Retry because the error marker is not always immediately visible. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(true); + }); + }); + + it('should have validation error for invalid path', async () => { + await PageObjects.console.enterRequest('\n GET'); + // Retry because the error marker is not always immediately visible. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(true); + }); + }); + + it('should have validation error for invalid body', async () => { + await PageObjects.console.enterRequest('\n POST foo/bar\n {"foo": "bar"'); + // Retry because the error marker is not always immediately visible. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(true); + }); + }); + + it('should have correct syntax highlighting', async () => { + await PageObjects.console.enterRequest('\n GET foo/bar'); + expect(await PageObjects.console.getRequestLineHighlighting()).to.contain( + 'ace_method ace_whitespace ace_url ace_part ace_url ace_slash ace_url ace_part' + ); + }); + + it('should have correct syntax highlighting for method', async () => { + await PageObjects.console.enterRequest('\n PUT foo/bar'); + const color = await PageObjects.console.getRequestMethodColor(); + expect(rgbToHex(color)).to.be('#c80a68'); + }); + + it('should have correct syntax highlighting for path', async () => { + await PageObjects.console.enterRequest('\n PUT foo/bar'); + const color = await PageObjects.console.getRequestPathColor(); + expect(rgbToHex(color)).to.be('#00756c'); + }); + + it('should have correct syntax highlighting for query', async () => { + await PageObjects.console.enterRequest('\n PUT foo/bar?pretty'); + const color = await PageObjects.console.getRequestQueryColor(); + expect(rgbToHex(color)).to.be('#00756c'); + }); + + it('should have correct syntax highlighting for body', async () => { + await PageObjects.console.enterRequest('\n PUT foo/bar\n {"foo": "bar"}'); + const color = await PageObjects.console.getRequestBodyColor(); + expect(rgbToHex(color)).to.be('#343741'); + }); + + it('should have multiple bodies for _msearch requests', async () => { + await PageObjects.console.enterRequest( + '\nGET foo/_msearch \n{}\n{"query": {"match_all": {}}}\n{"index": "bar"}\n{"query": {"match_all": {}}}' + ); + // Retry because body elements are not always immediately visible. + await retry.try(async () => { + expect(await PageObjects.console.getRequestBodyCount()).to.be(4); + }); + }); + + it('should not trigger error for multiple bodies for _msearch requests', async () => { + await PageObjects.console.enterRequest( + '\nGET foo/_msearch \n{}\n{"query": {"match_all": {}}}\n{"index": "bar"}\n{"query": {"match_all": {}}}' + ); + // Retry until typing is finished. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(false); + }); + }); + + it('should not trigger validation errors for multiple JSON blocks', async () => { + await PageObjects.console.enterRequest('\nPOST test/doc/1 \n{\n "foo": "bar"'); + await PageObjects.console.enterRequest('\nPOST test/doc/2 \n{\n "foo": "baz"'); + await PageObjects.console.enterRequest('\nPOST test/doc/3 \n{\n "foo": "qux"'); + // Retry until typing is finished. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(false); + }); + }); + it('should allow escaping quotation mark by wrapping it in triple quotes', async () => { await PageObjects.console.enterRequest( '\nPOST test/_doc/1 \n{\n "foo": """look "escaped" quotes"""' ); - await PageObjects.console.clickPlay(); - await PageObjects.header.waitUntilLoadingHasFinished(); + // Retry until typing is finished and validation errors are gone. + await retry.try(async () => { + expect(await PageObjects.console.hasErrorMarker()).to.be(false); + }); + }); + + it('should have correct syntax highlighting for inline comments', async () => { + await PageObjects.console.enterRequest( + '\nPOST test/_doc/1 \n{\n "foo": "bar" # inline comment' + ); + const color = await PageObjects.console.getCommentColor(); + expect(rgbToHex(color)).to.be('#41755c'); + }); + + it('should allow inline comments in request url row', async () => { + await executeRequest('\n GET _search // inline comment'); expect(await PageObjects.console.hasErrorMarker()).to.be(false); + expect(await PageObjects.console.getResponseStatus()).to.eql(200); + }); + + it('should allow inline comments in request body', async () => { + await executeRequest('\n GET _search \n{\n "query": {\n "match_all": {} // inline comment'); + expect(await PageObjects.console.hasErrorMarker()).to.be(false); + expect(await PageObjects.console.getResponseStatus()).to.eql(200); + }); + + it('should print warning for deprecated request', async () => { + await executeRequest('\nGET .kibana'); + expect(await PageObjects.console.responseHasDeprecationWarning()).to.be(true); }); - }); - describe('with invalid syntax', () => { - it('should trigger validation errors', async () => { - await PageObjects.console.enterRequest('\nGET test/doc/1 \n{\n "foo": \'\''); - expect(await PageObjects.console.hasInvalidSyntax()).to.be(true); - expect(await PageObjects.console.hasErrorMarker()).to.be(true); + it('should not print warning for non-deprecated request', async () => { + await executeRequest('\n GET _search'); + expect(await PageObjects.console.responseHasDeprecationWarning()).to.be(false); }); }); }); diff --git a/test/functional/apps/discover/group2/_sql_view.ts b/test/functional/apps/discover/group2/_sql_view.ts index 89a2e06085779..d8ec69db66ee4 100644 --- a/test/functional/apps/discover/group2/_sql_view.ts +++ b/test/functional/apps/discover/group2/_sql_view.ts @@ -53,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await testSubjects.exists('toggleFieldFilterButton')).to.be(true); expect(await testSubjects.exists('fieldTypesHelpButton')).to.be(true); await testSubjects.click('field-@message-showDetails'); - expect(await testSubjects.exists('discoverFieldListPanelEditItem')).to.be(true); + expect(await testSubjects.exists('discoverFieldListPanelEdit-@message')).to.be(true); await PageObjects.discover.selectTextBaseLang('SQL'); diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index 7b362aefc609d..4120a9ecc1a54 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -95,7 +95,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const panelCount = await PageObjects.dashboard.getPanelCount(); - expect(panelCount).to.be(17); + expect(panelCount).to.be(16); }); it('should render visualizations', async () => { @@ -107,7 +107,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug('Checking saved searches rendered'); await dashboardExpect.savedSearchRowCount(10); log.debug('Checking input controls rendered'); - await dashboardExpect.inputControlItemCount(3); + await dashboardExpect.controlCount(3); log.debug('Checking tag cloud rendered'); await dashboardExpect.tagCloudWithValuesFound(['Sunny', 'Rain', 'Clear', 'Cloudy', 'Hail']); log.debug('Checking vega chart rendered'); @@ -119,7 +119,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const panelCount = await PageObjects.dashboard.getPanelCount(); - expect(panelCount).to.be(13); + expect(panelCount).to.be(12); }); it('should launch sample ecommerce data set dashboard', async () => { @@ -127,7 +127,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const panelCount = await PageObjects.dashboard.getPanelCount(); - expect(panelCount).to.be(15); + expect(panelCount).to.be(14); }); }); diff --git a/test/functional/apps/management/_index_pattern_filter.ts b/test/functional/apps/management/_index_pattern_filter.ts index edfd32952da49..afa64c474d39d 100644 --- a/test/functional/apps/management/_index_pattern_filter.ts +++ b/test/functional/apps/management/_index_pattern_filter.ts @@ -15,9 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['settings']); const esArchiver = getService('esArchiver'); - // FLAKY: https://github.com/elastic/kibana/issues/128558 - // Failing: See https://github.com/elastic/kibana/issues/130190 - describe.skip('data view filter', function describeIndexTests() { + describe('index pattern filter', function describeIndexTests() { before(async function () { await esArchiver.emptyKibanaIndex(); await kibanaServer.uiSettings.replace({}); diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 9f662a09146e9..0069682f828d6 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -227,6 +227,19 @@ export class ConsolePageObject extends FtrService { return await this.find.existsByCssSelector('.ace_error'); } + public async getTokenColor(token: string) { + const element = await this.find.byClassName(token); + return await element.getComputedStyle('color'); + } + + public async responseHasDeprecationWarning() { + // Retry for a while to allow the deprecation warning to appear + return await this.retry.try(async () => { + const response = await this.getResponse(); + return response.trim().startsWith('#!'); + }); + } + public async clickFoldWidget() { const widget = await this.find.byCssSelector('.ace_fold-widget'); await widget.click(); @@ -404,4 +417,92 @@ export class ConsolePageObject extends FtrService { const button = await this.testSubjects.find('consoleMenuAutoIndent'); await button.click(); } + + public async getRequestMethod() { + const requestEditor = await this.getRequestEditor(); + const requestMethod = await requestEditor.findByClassName('ace_method'); + const method = await requestMethod.getVisibleText(); + return method.trim(); + } + + public async getRequestPath() { + const requestEditor = await this.getRequestEditor(); + const requestPath = await requestEditor.findAllByCssSelector('.ace_url'); + const path = []; + for (const pathPart of requestPath) { + const className = await pathPart.getAttribute('class'); + if (className.includes('ace_param')) { + // This is a parameter, we don't want to include it in the path + break; + } + path.push(await pathPart.getVisibleText()); + } + return path.join('').trim(); + } + + public async getRequestQueryParams() { + const requestEditor = await this.getRequestEditor(); + const requestQueryParams = await requestEditor.findAllByCssSelector('.ace_url.ace_param'); + + if (requestQueryParams.length === 0) { + // No query params + return; + } + + const params = []; + for (const param of requestQueryParams) { + params.push(await param.getVisibleText()); + } + return params.join('').trim(); + } + + public async getRequestBody() { + let request = await this.getRequest(); + // Remove new lines at the beginning of the request + request = request.replace(/^\n/, ''); + const method = await this.getRequestMethod(); + const path = await this.getRequestPath(); + const query = await this.getRequestQueryParams(); + + if (query) { + return request.replace(`${method} ${path}?${query}`, '').trim(); + } + + return request.replace(`${method} ${path}`, '').trim(); + } + + public async getRequestLineHighlighting() { + const requestEditor = await this.getRequestEditor(); + const requestLine = await requestEditor.findAllByCssSelector('.ace_line > *'); + const line = []; + for (const linePart of requestLine) { + line.push(await linePart.getAttribute('class')); + } + return line.join(' '); + } + + public async getRequestMethodColor() { + return await this.getTokenColor('ace_method'); + } + + public async getRequestPathColor() { + return await this.getTokenColor('ace_url'); + } + + public async getRequestQueryColor() { + return await this.getTokenColor('ace_param'); + } + + public async getRequestBodyColor() { + return await this.getTokenColor('ace_paren'); + } + + public async getCommentColor() { + return await this.getTokenColor('ace_comment'); + } + + public async getRequestBodyCount() { + const body = await this.getRequestBody(); + return body.split('\n').length; + } } diff --git a/test/functional/services/dashboard/expectations.ts b/test/functional/services/dashboard/expectations.ts index 73c49525ef4a1..6e20e5b574202 100644 --- a/test/functional/services/dashboard/expectations.ts +++ b/test/functional/services/dashboard/expectations.ts @@ -281,6 +281,7 @@ export class DashboardExpectService extends FtrService { }); } + // legacy controls visualization async inputControlItemCount(expectedCount: number) { this.log.debug(`DashboardExpect.inputControlItemCount(${expectedCount})`); await this.retry.try(async () => { @@ -289,6 +290,14 @@ export class DashboardExpectService extends FtrService { }); } + async controlCount(expectedCount: number) { + this.log.debug(`DashboardExpect.controlCount(${expectedCount})`); + await this.retry.try(async () => { + const controls = await this.testSubjects.findAll('control-frame'); + expect(controls.length).to.be(expectedCount); + }); + } + async lineChartPointsCount(expectedCount: number) { this.log.debug(`DashboardExpect.lineChartPointsCount(${expectedCount})`); await this.retry.try(async () => { diff --git a/test/functional/services/dashboard/panel_actions.ts b/test/functional/services/dashboard/panel_actions.ts index 17c2ffee733b2..79370e8e6af6a 100644 --- a/test/functional/services/dashboard/panel_actions.ts +++ b/test/functional/services/dashboard/panel_actions.ts @@ -179,6 +179,14 @@ export class DashboardPanelActionsService extends FtrService { return searchSessionId; } + async getSearchResponseByTitle(title: string) { + await this.openInspectorByTitle(title); + await this.inspector.openInspectorRequestsView(); + const response = await this.inspector.getResponse(); + await this.inspector.close(); + return response; + } + async openInspector(parent?: WebElementWrapper) { await this.openContextMenu(parent); const exists = await this.testSubjects.exists(OPEN_INSPECTOR_TEST_SUBJ); diff --git a/test/functional/services/inspector.ts b/test/functional/services/inspector.ts index 1917fa6dcdc50..0a7468a5b9be4 100644 --- a/test/functional/services/inspector.ts +++ b/test/functional/services/inspector.ts @@ -17,6 +17,7 @@ export class InspectorService extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly find = this.ctx.getService('find'); private readonly comboBox = this.ctx.getService('comboBox'); + private readonly monacoEditor = this.ctx.getService('monacoEditor'); private async getIsEnabled(): Promise { const ariaDisabled = await this.testSubjects.getAttribute('openInspectorButton', 'disabled'); @@ -212,6 +213,7 @@ export class InspectorService extends FtrService { * Opens inspector requests view */ public async openInspectorRequestsView(): Promise { + if (!(await this.testSubjects.exists('inspectorViewChooser'))) return; await this.openInspectorView('Requests'); } @@ -253,6 +255,15 @@ export class InspectorService extends FtrService { return this.testSubjects.find('inspectorRequestDetailResponse'); } + public async getResponse(): Promise> { + await (await this.getOpenRequestDetailResponseButton()).click(); + + await this.monacoEditor.waitCodeEditorReady('inspectorRequestCodeViewerContainer'); + const responseString = await this.monacoEditor.getCodeEditorValue(); + this.log.debug('Response string from inspector:', responseString); + return JSON.parse(responseString); + } + /** * Returns true if the value equals the combobox options list * @param value default combobox single option text 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/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts b/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts index 7616583de2e66..bf33c24f0239d 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/event_annotation/fetch_event_annotations.ts @@ -18,8 +18,7 @@ export default function ({ }: FtrProviderContext & { updateBaselines: boolean }) { let expectExpression: ExpectExpression; - // Failing: See https://github.com/elastic/kibana/issues/140113 - describe.skip('fetch event annotation tests', () => { + describe('fetch event annotation tests', () => { before(() => { expectExpression = expectExpressionProvider({ getService, updateBaselines }); }); diff --git a/test/plugin_functional/config.ts b/test/plugin_functional/config.ts index b2dbc762ab657..750da63e27d1c 100644 --- a/test/plugin_functional/config.ts +++ b/test/plugin_functional/config.ts @@ -60,6 +60,14 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--corePluginDeprecations.noLongerUsed=still_using', // for testing set buffer duration to 0 to immediately flush counters into saved objects. '--usageCollection.usageCounters.bufferDuration=0', + // explicitly enable the cloud integration plugins to validate the rendered config keys + '--xpack.cloud_integrations.chat.enabled=true', + '--xpack.cloud_integrations.chat.chatURL=a_string', + '--xpack.cloud_integrations.experiments.enabled=true', + '--xpack.cloud_integrations.experiments.launch_darkly.sdk_key=a_string', + '--xpack.cloud_integrations.experiments.launch_darkly.client_id=a_string', + '--xpack.cloud_integrations.full_story.enabled=true', + '--xpack.cloud_integrations.full_story.org_id=a_string', ...plugins.map( (pluginDir) => `--plugin-path=${path.resolve(__dirname, 'plugins', pluginDir)}` ), diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index cbc98ec7bb07b..a3968330e1425 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -91,20 +91,14 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'unifiedSearch.autocomplete.valueSuggestions.tiers (array)', 'unifiedSearch.autocomplete.valueSuggestions.timeout (duration)', 'data.search.aggs.shardDelay.enabled (boolean)', - 'data.search.sessions.cleanupInterval (duration)', 'data.search.sessions.defaultExpiration (duration)', 'data.search.sessions.enabled (boolean)', - 'data.search.sessions.expireInterval (duration)', 'data.search.sessions.management.expiresSoonWarning (duration)', 'data.search.sessions.management.maxSessions (number)', 'data.search.sessions.management.refreshInterval (duration)', 'data.search.sessions.management.refreshTimeout (duration)', 'data.search.sessions.maxUpdateRetries (number)', - 'data.search.sessions.monitoringTaskTimeout (duration)', - 'data.search.sessions.notTouchedInProgressTimeout (duration)', 'data.search.sessions.notTouchedTimeout (duration)', - 'data.search.sessions.pageSize (number)', - 'data.search.sessions.trackingInterval (duration)', 'enterpriseSearch.host (string)', 'guidedOnboarding.ui (boolean)', 'home.disableWelcomeScreen (boolean)', @@ -171,14 +165,17 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.cases.markdownPlugins.lens (boolean)', 'xpack.ccr.ui.enabled (boolean)', 'xpack.cloud.base_url (string)', - 'xpack.cloud.chat.chatURL (string)', - 'xpack.cloud.chat.enabled (boolean)', 'xpack.cloud.cname (string)', 'xpack.cloud.deployment_url (string)', - 'xpack.cloud.full_story.enabled (boolean)', - 'xpack.cloud.full_story.org_id (any)', + 'xpack.cloud_integrations.chat.chatURL (string)', + // No PII. This is an escape patch to override LaunchDarkly's flag resolution mechanism for testing or quick fix. + 'xpack.cloud_integrations.experiments.flag_overrides (record)', + // Commented because it's inside a schema conditional, and the test is not able to resolve it. But it's shared. + // Added here for documentation purposes. + // 'xpack.cloud_integrations.experiments.launch_darkly.client_id (string)', + 'xpack.cloud_integrations.full_story.org_id (any)', // No PII. Just the list of event types we want to forward to FullStory. - 'xpack.cloud.full_story.eventTypesAllowlist (array)', + 'xpack.cloud_integrations.full_story.eventTypesAllowlist (array)', 'xpack.cloud.id (string)', 'xpack.cloud.organization_url (string)', 'xpack.cloud.profile_url (string)', diff --git a/tsconfig.base.json b/tsconfig.base.json index b62beb6650448..3054a36f2bb86 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -313,8 +313,14 @@ "@kbn/canvas-plugin/*": ["x-pack/plugins/canvas/*"], "@kbn/cases-plugin": ["x-pack/plugins/cases"], "@kbn/cases-plugin/*": ["x-pack/plugins/cases/*"], + "@kbn/cloud-chat-plugin": ["x-pack/plugins/cloud_integrations/cloud_chat"], + "@kbn/cloud-chat-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_chat/*"], "@kbn/cloud-experiments-plugin": ["x-pack/plugins/cloud_integrations/cloud_experiments"], "@kbn/cloud-experiments-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_experiments/*"], + "@kbn/cloud-full-story-plugin": ["x-pack/plugins/cloud_integrations/cloud_full_story"], + "@kbn/cloud-full-story-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_full_story/*"], + "@kbn/cloud-links-plugin": ["x-pack/plugins/cloud_integrations/cloud_links"], + "@kbn/cloud-links-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_links/*"], "@kbn/cloud-security-posture-plugin": ["x-pack/plugins/cloud_security_posture"], "@kbn/cloud-security-posture-plugin/*": ["x-pack/plugins/cloud_security_posture/*"], "@kbn/cloud-plugin": ["x-pack/plugins/cloud"], 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/.i18nrc.json b/x-pack/.i18nrc.json index 83466ba749605..4f89798c71faf 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -10,6 +10,8 @@ "xpack.canvas": "plugins/canvas", "xpack.cases": "plugins/cases", "xpack.cloud": "plugins/cloud", + "xpack.cloudChat": "plugins/cloud_integrations/cloud_chat", + "xpack.cloudLinks": "plugins/cloud_integrations/cloud_links", "xpack.csp": "plugins/cloud_security_posture", "xpack.dashboard": "plugins/dashboard_enhanced", "xpack.discover": "plugins/discover_enhanced", 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/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx b/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx index 8521e4333be7d..d36ed2485b5ba 100644 --- a/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx +++ b/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import _ from 'lodash'; import { ReactElement } from 'react'; import { calculateBounds } from '@kbn/data-plugin/common'; import { FieldFormatter, MIN_ZOOM, MAX_ZOOM } from '@kbn/maps-plugin/common'; @@ -16,15 +17,18 @@ import type { Timeslice, } from '@kbn/maps-plugin/common/descriptor_types'; import type { + DataRequest, IField, ImmutableSourceProperty, - ITMSSource, + IRasterSource, SourceEditorArgs, } from '@kbn/maps-plugin/public'; +import { RasterTileSourceData } from '@kbn/maps-plugin/public/classes/sources/raster_source'; +import { RasterTileSource } from 'maplibre-gl'; type CustomRasterSourceDescriptor = AbstractSourceDescriptor; -export class CustomRasterSource implements ITMSSource { +export class CustomRasterSource implements IRasterSource { static type = 'CUSTOM_RASTER'; readonly _descriptor: CustomRasterSourceDescriptor; @@ -39,6 +43,25 @@ export class CustomRasterSource implements ITMSSource { this._descriptor = sourceDescriptor; } + async canSkipSourceUpdate( + dataRequest: DataRequest, + nextRequestMeta: DataRequestMeta + ): Promise { + const prevMeta = dataRequest.getMeta(); + if (!prevMeta) { + return Promise.resolve(false); + } + + return Promise.resolve(_.isEqual(prevMeta.timeslice, nextRequestMeta.timeslice)); + } + + isSourceStale(mbSource: RasterTileSource, sourceData: RasterTileSourceData): boolean { + if (!sourceData.url) { + return false; + } + return mbSource.tiles?.[0] !== sourceData.url; + } + cloneDescriptor(): CustomRasterSourceDescriptor { return { type: this._descriptor.type, diff --git a/x-pack/performance/journeys/ecommerce_dashboard.ts b/x-pack/performance/journeys/ecommerce_dashboard.ts index 89f05902f4153..05e46eab851b9 100644 --- a/x-pack/performance/journeys/ecommerce_dashboard.ts +++ b/x-pack/performance/journeys/ecommerce_dashboard.ts @@ -52,5 +52,5 @@ export const journey = new Journey({ await page.click(subj('launchSampleDataSetecommerce')); await page.click(subj('viewSampleDataSetecommerce-dashboard')); - await waitForVisualizations(page, 13); + await waitForVisualizations(page, 12); }); diff --git a/x-pack/performance/journeys/flight_dashboard.ts b/x-pack/performance/journeys/flight_dashboard.ts index ac6e589d391a5..1fbf2e3e77cb2 100644 --- a/x-pack/performance/journeys/flight_dashboard.ts +++ b/x-pack/performance/journeys/flight_dashboard.ts @@ -52,7 +52,7 @@ export const journey = new Journey({ await page.click(subj('launchSampleDataSetflights')); await page.click(subj('viewSampleDataSetflights-dashboard')); - await waitForVisualizations(page, 15); + await waitForVisualizations(page, 14); }) .step('Go to Airport Connections Visualizations Edit', async ({ page }) => { diff --git a/x-pack/performance/journeys/web_logs_dashboard.ts b/x-pack/performance/journeys/web_logs_dashboard.ts index 64ea47d412e0e..efba62acc517e 100644 --- a/x-pack/performance/journeys/web_logs_dashboard.ts +++ b/x-pack/performance/journeys/web_logs_dashboard.ts @@ -52,5 +52,5 @@ export const journey = new Journey({ await page.click(subj('launchSampleDataSetlogs')); await page.click(subj('viewSampleDataSetlogs-dashboard')); - await waitForVisualizations(page, 12); + await waitForVisualizations(page, 11); }); diff --git a/x-pack/plugins/actions/server/actions_config.test.ts b/x-pack/plugins/actions/server/actions_config.test.ts index a6b68d907cb44..b1af4a843b496 100644 --- a/x-pack/plugins/actions/server/actions_config.test.ts +++ b/x-pack/plugins/actions/server/actions_config.test.ts @@ -43,6 +43,15 @@ const defaultActionsConfig: ActionsConfig = { }; describe('ensureUriAllowed', () => { + test('throws an error when the Uri is an empty string', () => { + const config: ActionsConfig = defaultActionsConfig; + expect(() => + getActionsConfigurationUtilities(config).ensureUriAllowed('') + ).toThrowErrorMatchingInlineSnapshot( + `"target url \\"\\" is not added to the Kibana config xpack.actions.allowedHosts"` + ); + }); + test('returns true when "any" hostnames are allowed', () => { const config: ActionsConfig = { ...defaultActionsConfig, diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 7f9b45c368e90..1c7a66978ffb3 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -139,3 +139,5 @@ export const config: PluginConfigDescriptor = { }, ], }; + +export { urlAllowListValidator } from './sub_action_framework/helpers'; 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 90951692f5457..d3ca9c7143462 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/README.md +++ b/x-pack/plugins/actions/server/sub_action_framework/README.md @@ -6,6 +6,7 @@ The Kibana actions plugin provides a framework to create executable actions that - Register a sub action and map it to a function of your choice. - Define a schema for the parameters of your sub action. +- Define custom validators (or use the provided helpers) for the parameters of your sub action. - Define a response schema for responses from external services. - Create connectors that are supported by the Cases management system. @@ -350,7 +351,23 @@ plugins.actions.registerSubActionConnectorType({ minimumLicenseRequired: 'platinum' as const, schema: { config: TestConfigSchema, secrets: TestSecretsSchema }, Service: TestSubActionConnector, + renderParameterTemplates: renderTestTemplate }); ``` -You can see a full example in [x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/sub_action_connector.ts](../../../../test/alerting_api_integration/common/fixtures/plugins/alerts/server/sub_action_connector.ts) \ No newline at end of file +You can see a full example in [x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/sub_action_connector.ts](../../../../test/alerting_api_integration/common/fixtures/plugins/alerts/server/sub_action_connector.ts) + +### Example: Register sub action connector with custom validators + +The sub actions framework allows custom validators during registration of the connector type. Below is an example of including the URL validation for the `TestSubActionConnector` `url` configuration field. + +```typescript +plugins.actions.registerSubActionConnectorType({ + id: '.test-sub-action-connector', + name: 'Test: Sub action connector', + minimumLicenseRequired: 'platinum' as const, + schema: { config: TestConfigSchema, secrets: TestSecretsSchema }, + validators: [{type: ValidatorType.CONFIG, validate: urlAllowListValidator('url')}] + Service: TestSubActionConnector, +}); +``` diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts b/x-pack/plugins/actions/server/sub_action_framework/helpers/index.ts similarity index 65% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts rename to x-pack/plugins/actions/server/sub_action_framework/helpers/index.ts index 553cf2edde846..c69caff6b0c71 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/helpers/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -export { - getServiceNowITSMConnectorType, - getServiceNowSIRConnectorType, - getServiceNowITOMConnectorType, -} from './servicenow'; +export { urlAllowListValidator } from './validators'; diff --git a/x-pack/plugins/actions/server/sub_action_framework/helpers/validators.ts b/x-pack/plugins/actions/server/sub_action_framework/helpers/validators.ts new file mode 100644 index 0000000000000..7618fef0f3ea4 --- /dev/null +++ b/x-pack/plugins/actions/server/sub_action_framework/helpers/validators.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; +import { ValidatorServices } from '../../types'; + +export const urlAllowListValidator = (urlKey: string) => { + return (obj: T, validatorServices: ValidatorServices) => { + const { configurationUtilities } = validatorServices; + try { + const url = get(obj, urlKey, ''); + + configurationUtilities.ensureUriAllowed(url); + } catch (allowListError) { + throw new Error( + i18n.translate('xpack.actions.subActionsFramework.urlValidationError', { + defaultMessage: 'error validating url: {message}', + values: { + message: allowListError.message, + }, + }) + ); + } + }; +}; 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 cdc05524cf842..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 } 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; @@ -34,6 +46,29 @@ export type IServiceAbstract = abstract new ( params: ServiceParams ) => SubActionConnector; +export enum ValidatorType { + CONFIG, + SECRETS, +} + +interface Validate { + validator: ValidateFn; +} + +export type ValidateFn = NonNullable['customValidator']>; + +interface ConfigValidator extends Validate { + type: ValidatorType.CONFIG; +} + +interface SecretsValidator extends Validate { + type: ValidatorType.SECRETS; +} + +export type Validators = Array< + ConfigValidator | SecretsValidator +>; + export interface SubActionConnectorType { id: string; name: string; @@ -43,7 +78,9 @@ export interface SubActionConnectorType { config: Type; secrets: Type; }; + validators?: Array | SecretsValidator>; Service: IService; + renderParameterTemplates?: RenderParameterTemplates; } export interface ExecutorParams extends ActionTypeParams { diff --git a/x-pack/plugins/actions/server/sub_action_framework/validators.test.ts b/x-pack/plugins/actions/server/sub_action_framework/validators.test.ts index 6cae35141b498..b28adc0b545bf 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/validators.test.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/validators.test.ts @@ -14,7 +14,7 @@ import { TestSecrets, TestSubActionConnector, } from './mocks'; -import { IService } from './types'; +import { IService, SubActionConnectorType, ValidatorType } from './types'; import { buildValidators } from './validators'; describe('Validators', () => { @@ -36,6 +36,39 @@ describe('Validators', () => { return buildValidators({ configurationUtilities: mockedActionsConfig, connector }); }; + const createValidatorWithCustomValidation = (Service: IService) => { + const configValidator = jest.fn(); + const secretsValidator = jest.fn(); + + const connector: SubActionConnectorType = { + id: '.test', + name: 'Test', + minimumLicenseRequired: 'basic' as const, + supportedFeatureIds: ['alerting'], + schema: { + config: TestConfigSchema, + secrets: TestSecretsSchema, + }, + validators: [ + { + type: ValidatorType.CONFIG, + validator: configValidator, + }, + { + type: ValidatorType.SECRETS, + validator: secretsValidator, + }, + ], + Service, + }; + + return { + validators: buildValidators({ configurationUtilities: mockedActionsConfig, connector }), + configValidator, + secretsValidator, + }; + }; + beforeEach(() => { jest.resetAllMocks(); jest.clearAllMocks(); @@ -96,4 +129,28 @@ describe('Validators', () => { const { params } = validator; expect(() => params.schema.validate({ subAction, subActionParams: {} })).toThrow(); }); + + it('calls the config and secrets custom validator functions', () => { + const validator = createValidatorWithCustomValidation(TestSubActionConnector); + + validator.validators.config.customValidator?.( + { url: 'http://www.example.com' }, + { configurationUtilities: mockedActionsConfig } + ); + + validator.validators.secrets.customValidator?.( + { password: '123', username: 'sam' }, + { configurationUtilities: mockedActionsConfig } + ); + + expect(validator.configValidator).toHaveBeenCalledWith( + { url: 'http://www.example.com' }, + expect.anything() + ); + + expect(validator.secretsValidator).toHaveBeenCalledWith( + { password: '123', username: 'sam' }, + expect.anything() + ); + }); }); diff --git a/x-pack/plugins/actions/server/sub_action_framework/validators.ts b/x-pack/plugins/actions/server/sub_action_framework/validators.ts index be6dafed28163..e9cbbb3ae8f80 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/validators.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/validators.ts @@ -7,8 +7,8 @@ import { schema } from '@kbn/config-schema'; import { ActionsConfigurationUtilities } from '../actions_config'; -import { ActionTypeConfig, ActionTypeSecrets } from '../types'; -import { SubActionConnectorType } from './types'; +import { ActionTypeConfig, ActionTypeSecrets, ValidatorServices } from '../types'; +import { SubActionConnectorType, ValidateFn, Validators, ValidatorType } from './types'; export const buildValidators = < Config extends ActionTypeConfig, @@ -20,12 +20,16 @@ export const buildValidators = < configurationUtilities: ActionsConfigurationUtilities; connector: SubActionConnectorType; }) => { + const { config, secrets } = buildCustomValidators(connector.validators); + return { config: { schema: connector.schema.config, + customValidator: config, }, secrets: { schema: connector.schema.secrets, + customValidator: secrets, }, params: { schema: schema.object({ @@ -42,3 +46,35 @@ export const buildValidators = < }, }; }; + +const buildCustomValidators = (validators?: Validators) => { + const partitionedValidators: { + config: Array>; + secrets: Array>; + } = { config: [], secrets: [] }; + + for (const validatorInfo of validators ?? []) { + if (validatorInfo.type === ValidatorType.CONFIG) { + partitionedValidators.config.push(validatorInfo.validator); + } else { + partitionedValidators.secrets.push(validatorInfo.validator); + } + } + + return { + config: createCustomValidatorFunction(partitionedValidators.config), + secrets: createCustomValidatorFunction(partitionedValidators.secrets), + }; +}; + +const createCustomValidatorFunction = (validators: Array>) => { + if (validators.length <= 0) { + return; + } + + return (value: T, validatorServices: ValidatorServices) => { + for (const validate of validators) { + validate(value, validatorServices); + } + }; +}; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index ae344d4f62dbc..3806dae00c237 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -91,7 +91,7 @@ export type ExecutorType = ( options: ActionTypeExecutorOptions ) => Promise>; -interface ValidatorType { +export interface ValidatorType { schema: { validate(value: unknown): Type; }; @@ -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/common/api/explain_log_rate_spikes/actions.ts b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts index e050946a489be..7c4e3a47f8b79 100644 --- a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts +++ b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts @@ -18,6 +18,7 @@ export const API_ACTION_NAME = { ADD_CHANGE_POINTS_GROUP: 'add_change_point_group', ADD_CHANGE_POINTS_GROUP_HISTOGRAM: 'add_change_point_group_histogram', ADD_ERROR: 'add_error', + PING: 'ping', RESET: 'reset', UPDATE_LOADING_STATE: 'update_loading_state', } as const; @@ -89,6 +90,14 @@ export function addErrorAction(payload: ApiActionAddError['payload']): ApiAction }; } +interface ApiActionPing { + type: typeof API_ACTION_NAME.PING; +} + +export function pingAction(): ApiActionPing { + return { type: API_ACTION_NAME.PING }; +} + interface ApiActionReset { type: typeof API_ACTION_NAME.RESET; } @@ -121,5 +130,6 @@ export type AiopsExplainLogRateSpikesApiAction = | ApiActionAddChangePointsHistogram | ApiActionAddChangePointsGroupHistogram | ApiActionAddError + | ApiActionPing | ApiActionReset | ApiActionUpdateLoadingState; diff --git a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts index 5628b509980ad..c092b34c8b2b6 100644 --- a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts +++ b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts @@ -11,6 +11,7 @@ export { addChangePointsGroupHistogramAction, addChangePointsHistogramAction, addErrorAction, + pingAction, resetAction, updateLoadingStateAction, API_ACTION_NAME, diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx index 2425161615915..9949ec537b77a 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx @@ -172,6 +172,33 @@ export const ExplainLogRateSpikesAnalysis: FC onCancel={cancel} shouldRerunAnalysis={shouldRerunAnalysis} /> + {errors.length > 0 ? ( + <> + + + {errors.length === 1 ? ( +

{errors[0]}

+ ) : ( +
    + {errors.map((e, i) => ( +
  • {e}
  • + ))} +
+ )} +
+
+ + + ) : null} {showSpikeAnalysisTable && foundGroups && ( } /> )} - {errors.length > 0 && ( - <> - - - {errors.length === 1 ? ( -

{errors[0]}

- ) : ( -
    - {errors.map((e, i) => ( -
  • {e}
  • - ))} -
- )} -
-
- - - )} {showSpikeAnalysisTable && groupResults && foundGroups ? ( = ({ '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/aiops/server/routes/explain_log_rate_spikes.ts b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts index 8a8a7372f80e2..949b535ca16fb 100644 --- a/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts +++ b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts @@ -27,6 +27,7 @@ import { addChangePointsHistogramAction, aiopsExplainLogRateSpikesSchema, addErrorAction, + pingAction, resetAction, updateLoadingStateAction, AiopsExplainLogRateSpikesApiAction, @@ -74,6 +75,15 @@ export const defineExplainLogRateSpikesRoute = ( return response.forbidden(); } + let logMessageCounter = 1; + + function logInfoMessage(msg: string) { + logger.info(`Explain Log Rate Spikes #${logMessageCounter}: ${msg}`); + logMessageCounter++; + } + + logInfoMessage('Starting analysis.'); + const groupingEnabled = !!request.body.grouping; const client = (await context.core).elasticsearch.client.asCurrentUser; @@ -83,19 +93,33 @@ export const defineExplainLogRateSpikesRoute = ( let loaded = 0; let shouldStop = false; request.events.aborted$.subscribe(() => { + logInfoMessage('aborted$ subscription trigger.'); shouldStop = true; controller.abort(); }); request.events.completed$.subscribe(() => { + logInfoMessage('completed$ subscription trigger.'); shouldStop = true; controller.abort(); }); - const { end, push, responseWithHeaders } = streamFactory( - request.headers, - logger, - true - ); + const { + end: streamEnd, + push, + responseWithHeaders, + } = streamFactory(request.headers, logger, true); + + function pushPing() { + push(pingAction()); + } + + const pingInterval = setInterval(pushPing, 1000); + + function end() { + logInfoMessage('Ending analysis.'); + clearInterval(pingInterval); + streamEnd(); + } function endWithUpdatedLoadingState() { push( @@ -114,9 +138,16 @@ export const defineExplainLogRateSpikesRoute = ( end(); } + function pushError(m: string) { + logInfoMessage('Push error.'); + push(addErrorAction(m)); + } + // Async IIFE to run the analysis while not blocking returning `responseWithHeaders`. (async () => { + logInfoMessage('Reset.'); push(resetAction()); + logInfoMessage('Load field candidates.'); push( updateLoadingStateAction({ ccsWarning: false, @@ -134,7 +165,8 @@ export const defineExplainLogRateSpikesRoute = ( try { fieldCandidates = await fetchFieldCandidates(client, request.body); } catch (e) { - push(addErrorAction(e.toString())); + logger.error(`Failed to fetch field candidates, got: \n${e.toString()}`); + pushError(`Failed to fetch field candidates.`); end(); return; } @@ -168,17 +200,33 @@ export const defineExplainLogRateSpikesRoute = ( const changePoints: ChangePoint[] = []; const fieldsToSample = new Set(); const chunkSize = 10; + let chunkCount = 0; const fieldCandidatesChunks = chunk(fieldCandidates, chunkSize); + logInfoMessage('Fetch p-values.'); + for (const fieldCandidatesChunk of fieldCandidatesChunks) { + chunkCount++; + logInfoMessage(`Fetch p-values. Chunk ${chunkCount} of ${fieldCandidatesChunks.length}`); let pValues: Awaited>; try { - pValues = await fetchChangePointPValues(client, request.body, fieldCandidatesChunk); + pValues = await fetchChangePointPValues( + client, + request.body, + fieldCandidatesChunk, + logger, + pushError + ); } catch (e) { - push(addErrorAction(e.toString())); - end(); - return; + logger.error( + `Failed to fetch p-values for ${JSON.stringify( + fieldCandidatesChunk + )}, got: \n${e.toString()}` + ); + pushError(`Failed to fetch p-values for ${JSON.stringify(fieldCandidatesChunk)}.`); + // Still continue the analysis even if chunks of p-value queries fail. + continue; } if (pValues.length > 0) { @@ -210,12 +258,15 @@ export const defineExplainLogRateSpikesRoute = ( ); if (shouldStop) { + logInfoMessage('shouldStop fetching p-values.'); + end(); return; } } if (changePoints?.length === 0) { + logInfoMessage('Stopping analysis, did not find change points.'); endWithUpdatedLoadingState(); return; } @@ -224,16 +275,27 @@ export const defineExplainLogRateSpikesRoute = ( { fieldName: request.body.timeFieldName, type: KBN_FIELD_TYPES.DATE }, ]; - const [overallTimeSeries] = (await fetchHistogramsForFields( - client, - request.body.index, - { match_all: {} }, - // fields - histogramFields, - // samplerShardSize - -1, - undefined - )) as [NumericChartData]; + logInfoMessage('Fetch overall histogram.'); + + let overallTimeSeries: NumericChartData | undefined; + try { + overallTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + { match_all: {} }, + // fields + histogramFields, + // samplerShardSize + -1, + undefined + )) as [NumericChartData] + )[0]; + } catch (e) { + logger.error(`Failed to fetch the overall histogram data, got: \n${e.toString()}`); + pushError(`Failed to fetch overall histogram data.`); + // Still continue the analysis even if loading the overall histogram fails. + } function pushHistogramDataLoadingState() { push( @@ -251,6 +313,8 @@ export const defineExplainLogRateSpikesRoute = ( } if (groupingEnabled) { + logInfoMessage('Group results.'); + push( updateLoadingStateAction({ ccsWarning: false, @@ -283,208 +347,242 @@ export const defineExplainLogRateSpikesRoute = ( (g) => g.group.length > 1 ); - const { fields, df } = await fetchFrequentItems( - client, - request.body.index, - JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, - deduplicatedChangePoints, - request.body.timeFieldName, - request.body.deviationMin, - request.body.deviationMax - ); - - // The way the `frequent_items` aggregations works could return item sets that include - // field/value pairs that are not part of the original list of significant change points. - // This cleans up groups and removes those unrelated field/value pairs. - const filteredDf = df - .map((fi) => { - fi.set = Object.entries(fi.set).reduce( - (set, [field, value]) => { - if ( - changePoints.some((cp) => cp.fieldName === field && cp.fieldValue === value) - ) { - set[field] = value; + try { + const { fields, df } = await fetchFrequentItems( + client, + request.body.index, + JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, + deduplicatedChangePoints, + request.body.timeFieldName, + request.body.deviationMin, + request.body.deviationMax, + logger, + pushError + ); + + if (fields.length > 0 && df.length > 0) { + // The way the `frequent_items` aggregations works could return item sets that include + // field/value pairs that are not part of the original list of significant change points. + // This cleans up groups and removes those unrelated field/value pairs. + const filteredDf = df + .map((fi) => { + fi.set = Object.entries(fi.set).reduce( + (set, [field, value]) => { + if ( + changePoints.some((cp) => cp.fieldName === field && cp.fieldValue === value) + ) { + set[field] = value; + } + return set; + }, + {} + ); + fi.size = Object.keys(fi.set).length; + return fi; + }) + .filter((fi) => fi.size > 1); + + // `frequent_items` returns lot of different small groups of field/value pairs that co-occur. + // The following steps analyse these small groups, identify overlap between these groups, + // and then summarize them in larger groups where possible. + + // Get a tree structure based on `frequent_items`. + const { root } = getSimpleHierarchicalTree(filteredDf, true, false, fields); + + // Each leave of the tree will be a summarized group of co-occuring field/value pairs. + const treeLeaves = getSimpleHierarchicalTreeLeaves(root, []); + + // To be able to display a more cleaned up results table in the UI, we identify field/value pairs + // that occur in multiple groups. This will allow us to highlight field/value pairs that are + // unique to a group in a better way. This step will also re-add duplicates we identified in the + // beginning and didn't pass on to the `frequent_items` agg. + const fieldValuePairCounts = getFieldValuePairCounts(treeLeaves); + const changePointGroups = markDuplicates(treeLeaves, fieldValuePairCounts).map( + (g) => { + const group = [...g.group]; + + for (const groupItem of g.group) { + const { duplicate } = groupItem; + const duplicates = groupedChangePoints.find((d) => + d.group.some( + (dg) => + dg.fieldName === groupItem.fieldName && + dg.fieldValue === groupItem.fieldValue + ) + ); + + if (duplicates !== undefined) { + group.push( + ...duplicates.group.map((d) => { + return { + fieldName: d.fieldName, + fieldValue: d.fieldValue, + duplicate, + }; + }) + ); + } } - return set; - }, - {} - ); - fi.size = Object.keys(fi.set).length; - return fi; - }) - .filter((fi) => fi.size > 1); - - // `frequent_items` returns lot of different small groups of field/value pairs that co-occur. - // The following steps analyse these small groups, identify overlap between these groups, - // and then summarize them in larger groups where possible. - - // Get a tree structure based on `frequent_items`. - const { root } = getSimpleHierarchicalTree(filteredDf, true, false, fields); - - // Each leave of the tree will be a summarized group of co-occuring field/value pairs. - const treeLeaves = getSimpleHierarchicalTreeLeaves(root, []); - - // To be able to display a more cleaned up results table in the UI, we identify field/value pairs - // that occur in multiple groups. This will allow us to highlight field/value pairs that are - // unique to a group in a better way. This step will also re-add duplicates we identified in the - // beginning and didn't pass on to the `frequent_items` agg. - const fieldValuePairCounts = getFieldValuePairCounts(treeLeaves); - const changePointGroups = markDuplicates(treeLeaves, fieldValuePairCounts).map((g) => { - const group = [...g.group]; - - for (const groupItem of g.group) { - const { duplicate } = groupItem; - const duplicates = groupedChangePoints.find((d) => - d.group.some( - (dg) => - dg.fieldName === groupItem.fieldName && dg.fieldValue === groupItem.fieldValue - ) - ); - - if (duplicates !== undefined) { - group.push( - ...duplicates.group.map((d) => { - return { - fieldName: d.fieldName, - fieldValue: d.fieldValue, - duplicate, - }; - }) - ); - } - } - return { - ...g, - group, - }; - }); - - // Some field/value pairs might not be part of the `frequent_items` result set, for example - // because they don't co-occur with other field/value pairs or because of the limits we set on the query. - // In this next part we identify those missing pairs and add them as individual groups. - const missingChangePoints = deduplicatedChangePoints.filter((cp) => { - return !changePointGroups.some((cpg) => { - return cpg.group.some( - (d) => d.fieldName === cp.fieldName && d.fieldValue === cp.fieldValue + return { + ...g, + group, + }; + } ); - }); - }); - changePointGroups.push( - ...missingChangePoints.map(({ fieldName, fieldValue, doc_count: docCount, pValue }) => { - const duplicates = groupedChangePoints.find((d) => - d.group.some((dg) => dg.fieldName === fieldName && dg.fieldValue === fieldValue) + // Some field/value pairs might not be part of the `frequent_items` result set, for example + // because they don't co-occur with other field/value pairs or because of the limits we set on the query. + // In this next part we identify those missing pairs and add them as individual groups. + const missingChangePoints = deduplicatedChangePoints.filter((cp) => { + return !changePointGroups.some((cpg) => { + return cpg.group.some( + (d) => d.fieldName === cp.fieldName && d.fieldValue === cp.fieldValue + ); + }); + }); + + changePointGroups.push( + ...missingChangePoints.map( + ({ fieldName, fieldValue, doc_count: docCount, pValue }) => { + const duplicates = groupedChangePoints.find((d) => + d.group.some( + (dg) => dg.fieldName === fieldName && dg.fieldValue === fieldValue + ) + ); + if (duplicates !== undefined) { + return { + id: `${stringHash( + JSON.stringify( + duplicates.group.map((d) => ({ + fieldName: d.fieldName, + fieldValue: d.fieldValue, + })) + ) + )}`, + group: duplicates.group.map((d) => ({ + fieldName: d.fieldName, + fieldValue: d.fieldValue, + duplicate: false, + })), + docCount, + pValue, + }; + } else { + return { + id: `${stringHash(JSON.stringify({ fieldName, fieldValue }))}`, + group: [ + { + fieldName, + fieldValue, + duplicate: false, + }, + ], + docCount, + pValue, + }; + } + } + ) ); - if (duplicates !== undefined) { - return { - id: `${stringHash( - JSON.stringify( - duplicates.group.map((d) => ({ - fieldName: d.fieldName, - fieldValue: d.fieldValue, - })) - ) - )}`, - group: duplicates.group.map((d) => ({ - fieldName: d.fieldName, - fieldValue: d.fieldValue, - duplicate: false, - })), - docCount, - pValue, - }; - } else { - return { - id: `${stringHash(JSON.stringify({ fieldName, fieldValue }))}`, - group: [ - { - fieldName, - fieldValue, - duplicate: false, - }, - ], - docCount, - pValue, - }; - } - }) - ); - - // Finally, we'll find out if there's at least one group with at least two items, - // only then will we return the groups to the clients and make the grouping option available. - const maxItems = Math.max(...changePointGroups.map((g) => g.group.length)); - if (maxItems > 1) { - push(addChangePointsGroupAction(changePointGroups)); - } + // Finally, we'll find out if there's at least one group with at least two items, + // only then will we return the groups to the clients and make the grouping option available. + const maxItems = Math.max(...changePointGroups.map((g) => g.group.length)); - loaded += PROGRESS_STEP_GROUPING; + if (maxItems > 1) { + push(addChangePointsGroupAction(changePointGroups)); + } - pushHistogramDataLoadingState(); + loaded += PROGRESS_STEP_GROUPING; - if (changePointGroups) { - await asyncForEach(changePointGroups, async (cpg, index) => { - const histogramQuery = { - bool: { - filter: cpg.group.map((d) => ({ - term: { [d.fieldName]: d.fieldValue }, - })), - }, - }; + pushHistogramDataLoadingState(); - const [cpgTimeSeries] = (await fetchHistogramsForFields( - client, - request.body.index, - histogramQuery, - // fields - [ - { - fieldName: request.body.timeFieldName, - type: KBN_FIELD_TYPES.DATE, - interval: overallTimeSeries.interval, - min: overallTimeSeries.stats[0], - max: overallTimeSeries.stats[1], - }, - ], - // samplerShardSize - -1, - undefined - )) as [NumericChartData]; + logInfoMessage('Fetch group histograms.'); - const histogram = - overallTimeSeries.data.map((o, i) => { - const current = cpgTimeSeries.data.find( - (d1) => d1.key_as_string === o.key_as_string - ) ?? { - doc_count: 0, - }; - return { - key: o.key, - key_as_string: o.key_as_string ?? '', - doc_count_change_point: current.doc_count, - doc_count_overall: Math.max(0, o.doc_count - current.doc_count), + await asyncForEach(changePointGroups, async (cpg) => { + if (overallTimeSeries !== undefined) { + const histogramQuery = { + bool: { + filter: cpg.group.map((d) => ({ + term: { [d.fieldName]: d.fieldValue }, + })), + }, }; - }) ?? []; - push( - addChangePointsGroupHistogramAction([ - { - id: cpg.id, - histogram, - }, - ]) - ); - }); + let cpgTimeSeries: NumericChartData; + try { + cpgTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + histogramQuery, + // fields + [ + { + fieldName: request.body.timeFieldName, + type: KBN_FIELD_TYPES.DATE, + interval: overallTimeSeries.interval, + min: overallTimeSeries.stats[0], + max: overallTimeSeries.stats[1], + }, + ], + // samplerShardSize + -1, + undefined + )) as [NumericChartData] + )[0]; + } catch (e) { + logger.error( + `Failed to fetch the histogram data for group #${ + cpg.id + }, got: \n${e.toString()}` + ); + pushError(`Failed to fetch the histogram data for group #${cpg.id}.`); + return; + } + const histogram = + overallTimeSeries.data.map((o, i) => { + const current = cpgTimeSeries.data.find( + (d1) => d1.key_as_string === o.key_as_string + ) ?? { + doc_count: 0, + }; + return { + key: o.key, + key_as_string: o.key_as_string ?? '', + doc_count_change_point: current.doc_count, + doc_count_overall: Math.max(0, o.doc_count - current.doc_count), + }; + }) ?? []; + + push( + addChangePointsGroupHistogramAction([ + { + id: cpg.id, + histogram, + }, + ]) + ); + } + }); + } + } catch (e) { + logger.error( + `Failed to transform field/value pairs into groups, got: \n${e.toString()}` + ); + pushError(`Failed to transform field/value pairs into groups.`); } } loaded += PROGRESS_STEP_HISTOGRAMS_GROUPS; + logInfoMessage('Fetch field/value histograms.'); + // time series filtered by fields - if (changePoints) { - await asyncForEach(changePoints, async (cp, index) => { - if (changePoints) { + if (changePoints && overallTimeSeries !== undefined) { + await asyncForEach(changePoints, async (cp) => { + if (overallTimeSeries !== undefined) { const histogramQuery = { bool: { filter: [ @@ -495,24 +593,40 @@ export const defineExplainLogRateSpikesRoute = ( }, }; - const [cpTimeSeries] = (await fetchHistogramsForFields( - client, - request.body.index, - histogramQuery, - // fields - [ - { - fieldName: request.body.timeFieldName, - type: KBN_FIELD_TYPES.DATE, - interval: overallTimeSeries.interval, - min: overallTimeSeries.stats[0], - max: overallTimeSeries.stats[1], - }, - ], - // samplerShardSize - -1, - undefined - )) as [NumericChartData]; + let cpTimeSeries: NumericChartData; + + try { + cpTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + histogramQuery, + // fields + [ + { + fieldName: request.body.timeFieldName, + type: KBN_FIELD_TYPES.DATE, + interval: overallTimeSeries.interval, + min: overallTimeSeries.stats[0], + max: overallTimeSeries.stats[1], + }, + ], + // samplerShardSize + -1, + undefined + )) as [NumericChartData] + )[0]; + } catch (e) { + logger.error( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${ + cp.fieldValue + }", got: \n${e.toString()}` + ); + pushError( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${cp.fieldValue}".` + ); + return; + } const histogram = overallTimeSeries.data.map((o, i) => { diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts b/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts index 03242a4bc8ae5..0fb7f90c89c12 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts +++ b/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts @@ -8,6 +8,7 @@ import { uniqBy } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ElasticsearchClient } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; import { ChangePoint } from '@kbn/ml-agg-utils'; import { SPIKE_ANALYSIS_THRESHOLD } from '../../../common/constants'; import type { AiopsExplainLogRateSpikesSchema } from '../../../common/api/explain_log_rate_spikes'; @@ -92,7 +93,9 @@ interface Aggs extends estypes.AggregationsSignificantLongTermsAggregate { export const fetchChangePointPValues = async ( esClient: ElasticsearchClient, params: AiopsExplainLogRateSpikesSchema, - fieldNames: string[] + fieldNames: string[], + logger: Logger, + emitError: (m: string) => void ): Promise => { const result: ChangePoint[] = []; @@ -101,7 +104,16 @@ export const fetchChangePointPValues = async ( const resp = await esClient.search(request); if (resp.aggregations === undefined) { - throw new Error('fetchChangePoint failed, did not return aggregations.'); + logger.error( + `Failed to fetch p-value aggregation for fieldName "${fieldName}", got: \n${JSON.stringify( + resp, + null, + 2 + )}` + ); + emitError(`Failed to fetch p-value aggregation for fieldName "${fieldName}".`); + // Still continue the analysis even if individual p-value queries fail. + continue; } const overallResult = resp.aggregations.change_point_p_value; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_items.ts b/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_items.ts index 055c22397064f..c9444aaca22af 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_items.ts +++ b/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_items.ts @@ -10,6 +10,7 @@ import { uniq, uniqWith, pick, isEqual } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { Logger } from '@kbn/logging'; import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils'; interface FrequentItemsAggregation extends estypes.AggregationsSamplerAggregation { @@ -53,9 +54,11 @@ export async function fetchFrequentItems( changePoints: ChangePoint[], timeFieldName: string, deviationMin: number, - deviationMax: number + deviationMax: number, + logger: Logger, + emitError: (m: string) => void ) { - // get unique fields that are left + // get unique fields from change points const fields = [...new Set(changePoints.map((t) => t.fieldName))]; // TODO add query params @@ -91,6 +94,8 @@ export async function fetchFrequentItems( sampleProbability = Math.min(0.5, minDocCount / totalDocCount); } + logger.debug(`frequent_items sample probability: ${sampleProbability}`); + // frequent items can be slow, so sample and use 10% min_support const aggs: Record = { sample: { @@ -103,7 +108,7 @@ export async function fetchFrequentItems( frequent_items: { minimum_set_size: 2, size: 200, - minimum_support: 0.01, + minimum_support: 0.1, fields: aggFields, }, }, @@ -125,12 +130,18 @@ export async function fetchFrequentItems( { maxRetries: 0 } ); - const totalDocCountFi = (body.hits.total as estypes.SearchTotalHits).value; - if (body.aggregations === undefined) { - throw new Error('fetchFrequentItems failed, did not return aggregations.'); + logger.error(`Failed to fetch frequent_items, got: \n${JSON.stringify(body, null, 2)}`); + emitError(`Failed to fetch frequent_items.`); + return { + fields: [], + df: [], + totalDocCount: 0, + }; } + const totalDocCountFi = (body.hits.total as estypes.SearchTotalHits).value; + const shape = body.aggregations.sample.fi.buckets.length; let maximum = shape; if (maximum > 50000) { diff --git a/x-pack/plugins/alerting/server/routes/resolve_rule.ts b/x-pack/plugins/alerting/server/routes/resolve_rule.ts index cde747f9272fe..b3576c0c5ed44 100644 --- a/x-pack/plugins/alerting/server/routes/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/routes/resolve_rule.ts @@ -75,7 +75,7 @@ export const resolveRuleRoute = ( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; - const rule = await rulesClient.resolve({ id }); + const rule = await rulesClient.resolve({ id, includeSnoozeData: true }); return res.ok({ body: rewriteBodyRes(rule), }); diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 89ce20b59ae9b..d08f12f054a50 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -41,7 +41,11 @@ import { InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult, } from '@kbn/security-plugin/server'; import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server'; -import { TaskManagerStartContract, TaskStatus } from '@kbn/task-manager-plugin/server'; +import { + ConcreteTaskInstance, + TaskManagerStartContract, + TaskStatus, +} from '@kbn/task-manager-plugin/server'; import { IEvent, IEventLogClient, @@ -711,9 +715,11 @@ export class RulesClient { public async resolve({ id, includeLegacyId, + includeSnoozeData = false, }: { id: string; includeLegacyId?: boolean; + includeSnoozeData?: boolean; }): Promise> { const { saved_object: result, ...resolveResponse } = await this.unsecuredSavedObjectsClient.resolve('alert', id); @@ -746,7 +752,9 @@ export class RulesClient { result.attributes.alertTypeId, result.attributes, result.references, - includeLegacyId + includeLegacyId, + false, + includeSnoozeData ); return { @@ -2977,9 +2985,27 @@ export class RulesClient { this.ruleTypeRegistry.ensureRuleTypeEnabled(attributes.alertTypeId); - const taskDoc = attributes.scheduledTaskId - ? await this.taskManager.get(attributes.scheduledTaskId) - : null; + // Check that the rule is enabled + if (!attributes.enabled) { + return i18n.translate('xpack.alerting.rulesClient.runSoon.disabledRuleError', { + defaultMessage: 'Error running rule: rule is disabled', + }); + } + + let taskDoc: ConcreteTaskInstance | null = null; + try { + taskDoc = attributes.scheduledTaskId + ? await this.taskManager.get(attributes.scheduledTaskId) + : null; + } catch (err) { + return i18n.translate('xpack.alerting.rulesClient.runSoon.getTaskError', { + defaultMessage: 'Error running rule: {errMessage}', + values: { + errMessage: err.message, + }, + }); + } + if ( taskDoc && (taskDoc.status === TaskStatus.Claiming || taskDoc.status === TaskStatus.Running) @@ -2989,7 +3015,16 @@ export class RulesClient { }); } - await this.taskManager.runSoon(id); + try { + await this.taskManager.runSoon(attributes.scheduledTaskId ? attributes.scheduledTaskId : id); + } catch (err) { + return i18n.translate('xpack.alerting.rulesClient.runSoon.runSoonError', { + defaultMessage: 'Error running rule: {errMessage}', + values: { + errMessage: err.message, + }, + }); + } } public async listAlertTypes() { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index 297c4b6d60fcc..b4a48be3a37fc 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -201,6 +201,58 @@ describe('resolve()', () => { `); }); + test('calls saved objects client with id and includeSnoozeData params', async () => { + const rulesClient = new RulesClient(rulesClientParams); + unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ + saved_object: { + id: '1', + type: 'alert', + attributes: { + legacyId: 'some-legacy-id', + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + snoozeSchedule: [ + { + duration: 10000, + rRule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + count: 1, + }, + }, + ], + muteAll: false, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + outcome: 'aliasMatch', + alias_target_id: '2', + }); + const result = await rulesClient.resolve({ id: '1', includeSnoozeData: true }); + expect(result.isSnoozedUntil).toBeTruthy(); + }); + test('should call useSavedObjectReferences.injectReferences if defined for rule type', async () => { const injectReferencesFn = jest.fn().mockReturnValue({ bar: true, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts index bea66a31ead25..138b167549c09 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts @@ -59,7 +59,7 @@ describe('runSoon()', () => { consumer: 'myApp', schedule: { interval: '10s' }, alertTypeId: 'myType', - enabled: false, + enabled: true, apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', actions: [ @@ -179,7 +179,7 @@ describe('runSoon()', () => { }); test('does not run a rule if that rule is already running', async () => { - taskManager.get.mockResolvedValue({ + taskManager.get.mockResolvedValueOnce({ id: '1', scheduledAt: new Date(), attempts: 0, @@ -196,4 +196,32 @@ describe('runSoon()', () => { expect(message).toBe('Rule is already running'); expect(taskManager.runSoon).not.toHaveBeenCalled(); }); + + test('does not run a rule if that rule is disabled', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValue({ + ...existingRule, + attributes: { + ...existingRule.attributes, + enabled: false, + }, + }); + const message = await rulesClient.runSoon({ id: '1' }); + expect(message).toBe('Error running rule: rule is disabled'); + expect(taskManager.get).not.toHaveBeenCalled(); + expect(taskManager.runSoon).not.toHaveBeenCalled(); + }); + + test('gracefully handles errors getting task document', async () => { + taskManager.get.mockRejectedValueOnce(new Error('oh no!')); + const message = await rulesClient.runSoon({ id: '1' }); + expect(message).toBe('Error running rule: oh no!'); + expect(taskManager.runSoon).not.toHaveBeenCalled(); + }); + + test('gracefully handles errors calling runSoon', async () => { + taskManager.runSoon.mockRejectedValueOnce(new Error('fail!')); + const message = await rulesClient.runSoon({ id: '1' }); + expect(message).toBe('Error running rule: fail!'); + expect(taskManager.runSoon).toHaveBeenCalled(); + }); }); 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/ftr_e2e/cypress.config.ts b/x-pack/plugins/apm/ftr_e2e/cypress.config.ts index 7a92b84ac36bd..bcccae43adc7e 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress.config.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress.config.ts @@ -6,9 +6,10 @@ */ import { defineConfig } from 'cypress'; -import { plugin } from './cypress/plugins'; +import { setupNodeEvents } from './setup_cypress_node_events'; module.exports = defineConfig({ + projectId: 'omwh6f', fileServerFolder: './cypress', fixturesFolder: './cypress/fixtures', screenshotsFolder: './cypress/screenshots', @@ -18,16 +19,16 @@ module.exports = defineConfig({ defaultCommandTimeout: 30000, execTimeout: 120000, pageLoadTimeout: 120000, - viewportHeight: 900, + viewportHeight: 1800, viewportWidth: 1440, - video: false, - screenshotOnRunFailure: false, + video: true, + videoUploadOnPasses: false, + screenshotOnRunFailure: true, + retries: { + runMode: 1, + }, e2e: { - // We've imported your old cypress plugins here. - // You may want to clean this up later by importing these. - setupNodeEvents(on, config) { - plugin(on, config); - }, + setupNodeEvents, baseUrl: 'http://localhost:5601', supportFile: './cypress/support/e2e.ts', specPattern: './cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/feature_flag/comparison.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/feature_flag/comparison.cy.ts index d1159efd0fc90..7d40105db192e 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/feature_flag/comparison.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/feature_flag/comparison.cy.ts @@ -36,19 +36,19 @@ describe('Comparison feature flag', () => { it('shows the comparison feature enabled in services overview', () => { cy.visitKibana('/app/apm/services'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); + cy.getByTestSubj('comparisonSelect').should('not.be.disabled'); }); it('shows the comparison feature enabled in dependencies overview', () => { cy.visitKibana('/app/apm/dependencies'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); + cy.getByTestSubj('comparisonSelect').should('not.be.disabled'); }); it('shows the comparison feature disabled in service map overview page', () => { cy.visitKibana('/app/apm/service-map'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); + cy.getByTestSubj('comparisonSelect').should('not.be.disabled'); }); }); @@ -71,7 +71,7 @@ describe('Comparison feature flag', () => { it('shows the comparison feature disabled in services overview', () => { cy.visitKibana('/app/apm/services'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); + cy.getByTestSubj('comparisonSelect').should('be.disabled'); }); it('shows the comparison feature disabled in dependencies overview page', () => { @@ -81,13 +81,13 @@ describe('Comparison feature flag', () => { cy.visitKibana('/app/apm/dependencies'); cy.wait('@topDependenciesRequest'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); + cy.getByTestSubj('comparisonSelect').should('be.disabled'); }); it('shows the comparison feature disabled in service map overview page', () => { cy.visitKibana('/app/apm/service-map'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); - cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); + cy.getByTestSubj('comparisonSelect').should('be.disabled'); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/integration_settings/integration_policy.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/integration_settings/integration_policy.cy.ts index c25e6a6800311..5d275770e462d 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/integration_settings/integration_policy.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/integration_settings/integration_policy.cy.ts @@ -60,21 +60,19 @@ describe('when navigating to integration page', () => { cy.visitKibana(integrationsPath); // open integration policy form - cy.get('[data-test-subj="integration-card:epr:apm:featured').click(); + cy.getByTestSubj('integration-card:epr:apm:featured').click(); cy.contains('Elastic APM in Fleet').click(); cy.contains('a', 'APM integration').click(); - cy.get('[data-test-subj="addIntegrationPolicyButton"]').click(); + cy.getByTestSubj('addIntegrationPolicyButton').click(); }); it('checks validators for required fields', () => { const requiredFields = policyFormFields.filter((field) => field.required); requiredFields.map((field) => { - cy.get(`[data-test-subj="${field.selector}"`).clear(); - cy.get('[data-test-subj="createPackagePolicySaveButton"').should( - 'be.disabled' - ); - cy.get(`[data-test-subj="${field.selector}"`).type(field.value); + cy.getByTestSubj(field.selector).clear(); + cy.getByTestSubj('createPackagePolicySaveButton').should('be.disabled'); + cy.getByTestSubj(field.selector).type(field.value); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/agent_configurations.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/agent_configurations.cy.ts index 5be39b4f082dc..47f8c537b100c 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/agent_configurations.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/agent_configurations.cy.ts @@ -90,7 +90,7 @@ describe('Agent configuration', () => { '/api/apm/settings/agent-configuration/environments?*' ).as('serviceEnvironmentApi'); cy.contains('Create configuration').click(); - cy.get('[data-test-subj="serviceNameComboBox"]') + cy.getByTestSubj('serviceNameComboBox') .click() .type('opbeans-node') .type('{enter}'); @@ -98,7 +98,7 @@ describe('Agent configuration', () => { cy.contains('opbeans-node').realClick(); cy.wait('@serviceEnvironmentApi'); - cy.get('[data-test-subj="serviceEnviromentComboBox"]') + cy.getByTestSubj('serviceEnviromentComboBox') .click({ force: true }) .type('prod') .type('{enter}'); @@ -115,14 +115,11 @@ describe('Agent configuration', () => { '/api/apm/settings/agent-configuration/environments' ).as('serviceEnvironmentApi'); cy.contains('Create configuration').click(); - cy.get('[data-test-subj="serviceNameComboBox"]') - .click() - .type('All') - .type('{enter}'); + cy.getByTestSubj('serviceNameComboBox').click().type('All').type('{enter}'); cy.contains('All').realClick(); cy.wait('@serviceEnvironmentApi'); - cy.get('[data-test-subj="serviceEnviromentComboBox"]') + cy.getByTestSubj('serviceEnviromentComboBox') .click({ force: true }) .type('All'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/custom_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/custom_links.cy.ts index 615ff2b49a85a..b680f745609bc 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/custom_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/settings/custom_links.cy.ts @@ -52,7 +52,7 @@ describe('Custom links', () => { it('creates custom link', () => { cy.visitKibana(basePath); - const emptyPrompt = cy.get('[data-test-subj="customLinksEmptyPrompt"]'); + const emptyPrompt = cy.getByTestSubj('customLinksEmptyPrompt'); cy.contains('Create custom link').click(); cy.contains('Create link'); cy.contains('Save').should('be.disabled'); @@ -63,7 +63,7 @@ describe('Custom links', () => { emptyPrompt.should('not.exist'); cy.contains('foo'); cy.contains('https://foo.com'); - cy.get('[data-test-subj="editCustomLink"]').click(); + cy.getByTestSubj('editCustomLink').click(); cy.contains('Delete').click(); }); @@ -71,14 +71,14 @@ describe('Custom links', () => { cy.visitKibana(basePath); // wait for empty prompt - cy.get('[data-test-subj="customLinksEmptyPrompt"]').should('be.visible'); + cy.getByTestSubj('customLinksEmptyPrompt').should('be.visible'); cy.contains('Create custom link').click(); - cy.get('[data-test-subj="filter-0"]').select('service.name'); + cy.getByTestSubj('filter-0').select('service.name'); cy.get( '[data-test-subj="service.name.value"] [data-test-subj="comboBoxSearchInput"]' ).type('foo'); - cy.get('[data-test-subj="filter-0"]').select('service.environment'); + cy.getByTestSubj('filter-0').select('service.environment'); cy.get( '[data-test-subj="service.environment.value"] [data-test-subj="comboBoxInput"]' ).should('not.contain', 'foo'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts index e989ea5cf0faf..2efebecf25756 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts @@ -85,18 +85,31 @@ describe('Storage Explorer', () => { }); it('renders the storage timeseries chart', () => { - cy.get('[data-test-subj="storageExplorerTimeseriesChart"]'); + cy.getByTestSubj('storageExplorerTimeseriesChart'); }); it('has a list of services and environments', () => { - cy.contains('opbeans-node'); - cy.contains('opbeans-java'); - cy.contains('opbeans-rum'); + cy.contains( + '[data-test-subj="apmStorageExplorerServiceLink"]', + 'opbeans-node' + ); + cy.contains( + '[data-test-subj="apmStorageExplorerServiceLink"]', + 'opbeans-java' + ); + cy.contains( + '[data-test-subj="apmStorageExplorerServiceLink"]', + 'opbeans-rum' + ); cy.get('td:contains(production)').should('have.length', 3); }); it('when clicking on a service it loads the service overview for that service', () => { - cy.contains('opbeans-node').click({ force: true }); + cy.contains( + '[data-test-subj="apmStorageExplorerServiceLink"]', + 'opbeans-node' + ).click(); + cy.url().should('include', '/apm/services/opbeans-node/overview'); cy.contains('h1', 'opbeans-node'); }); @@ -115,7 +128,7 @@ describe('Storage Explorer', () => { it('with the correct environment when changing the environment', () => { cy.wait(mainAliasNames); - cy.get('[data-test-subj="environmentFilter"]').type('production'); + cy.getByTestSubj('environmentFilter').type('production'); cy.contains('button', 'production').click({ force: true }); @@ -148,7 +161,7 @@ describe('Storage Explorer', () => { it('with the correct lifecycle phase when changing the lifecycle phase', () => { cy.wait(mainAliasNames); - cy.get('[data-test-subj="storageExplorerLifecyclePhaseSelect"]').click(); + cy.getByTestSubj('storageExplorerLifecyclePhaseSelect').click(); cy.contains('button', 'Warm').click(); cy.expectAPIsToHaveBeenCalledWith({ @@ -180,13 +193,13 @@ describe('Storage Explorer', () => { cy.wait(mainAliasNames); cy.contains('opbeans-node'); - cy.get('[data-test-subj="storageDetailsButton_opbeans-node"]').click(); - cy.get('[data-test-subj="loadingSpinner"]').should('be.visible'); + cy.getByTestSubj('storageDetailsButton_opbeans-node').click(); + cy.getByTestSubj('loadingSpinner').should('be.visible'); cy.wait('@storageDetailsRequest'); cy.contains('Service storage details'); - cy.get('[data-test-subj="storageExplorerTimeseriesChart"]'); - cy.get('[data-test-subj="serviceStorageDetailsTable"]'); + cy.getByTestSubj('storageExplorerTimeseriesChart'); + cy.getByTestSubj('serviceStorageDetailsTable'); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts index cfcabe85b5b2a..00b842f3265c7 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts @@ -11,7 +11,7 @@ describe('APM deep links', () => { }); it('navigates to apm links on search elastic', () => { cy.visitKibana('/'); - cy.get('[data-test-subj="nav-search-input"]').type('APM'); + cy.getByTestSubj('nav-search-input').type('APM'); cy.contains('APM'); cy.contains('APM / Services'); cy.contains('APM / Traces'); @@ -23,17 +23,17 @@ describe('APM deep links', () => { cy.contains('APM').click({ force: true }); cy.url().should('include', '/apm/services'); - cy.get('[data-test-subj="nav-search-input"]').type('APM'); + cy.getByTestSubj('nav-search-input').type('APM'); // navigates to services page cy.contains('APM / Services').click({ force: true }); cy.url().should('include', '/apm/services'); - cy.get('[data-test-subj="nav-search-input"]').type('APM'); + cy.getByTestSubj('nav-search-input').type('APM'); // navigates to traces page cy.contains('APM / Traces').click({ force: true }); cy.url().should('include', '/apm/traces'); - cy.get('[data-test-subj="nav-search-input"]').type('APM'); + cy.getByTestSubj('nav-search-input').type('APM'); // navigates to service maps cy.contains('APM / Service Map').click({ force: true }); cy.url().should('include', '/apm/service-map'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/dependencies.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/dependencies.cy.ts index 653809a8e04d3..2ef3ae42b1aac 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/dependencies.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/dependencies.cy.ts @@ -66,9 +66,9 @@ describe('Dependencies', () => { })}` ); - cy.get('[data-test-subj="latencyChart"]'); - cy.get('[data-test-subj="throughputChart"]'); - cy.get('[data-test-subj="errorRateChart"]'); + cy.getByTestSubj('latencyChart'); + cy.getByTestSubj('throughputChart'); + cy.getByTestSubj('errorRateChart'); cy.contains('opbeans-java').click({ force: true }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts index 19de523c7ab1f..d00d8036df3bb 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts @@ -68,13 +68,13 @@ describe('Error details', () => { it('shows errors distribution chart', () => { cy.visitKibana(errorDetailsPageHref); cy.contains('Error group 00000'); - cy.get('[data-test-subj="errorDistribution"]').contains('Occurrences'); + cy.getByTestSubj('errorDistribution').contains('Occurrences'); }); it('shows top erroneous transactions table', () => { cy.visitKibana(errorDetailsPageHref); cy.contains('Top 5 affected transactions'); - cy.get('[data-test-subj="topErroneousTransactionsTable"]') + cy.getByTestSubj('topErroneousTransactionsTable') .contains('a', 'GET /apple 🍎') .click(); cy.url().should('include', 'opbeans-java/transactions/view'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts index 301b3384ee2eb..8ac95d509d0bd 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts @@ -81,14 +81,14 @@ describe('Errors page', () => { it('clicking on type adds a filter in the kuerybar', () => { cy.visitKibana(javaServiceErrorsPageHref); - cy.get('[data-test-subj="headerFilterKuerybar"]') + cy.getByTestSubj('headerFilterKuerybar') .invoke('val') .should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('exception 0').click({ force: true, }); - cy.get('[data-test-subj="headerFilterKuerybar"]') + cy.getByTestSubj('headerFilterKuerybar') .its('length') .should('be.gt', 0); cy.get('table') @@ -158,7 +158,7 @@ describe('Check detailed statistics API with multiple errors', () => { ]) ); }); - cy.get('[data-test-subj="pagination-button-1"]').click(); + cy.getByTestSubj('pagination-button-1').click(); cy.wait('@errorsDetailedStatistics').then((payload) => { expect(payload.request.body.groupIds).eql( JSON.stringify([ diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/home.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/home.cy.ts index 2ee2f4f019b12..e0c4a3aedd2b3 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/home.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/home.cy.ts @@ -69,7 +69,7 @@ describe('Home page', () => { cy.contains('Services'); cy.contains('opbeans-rum').click({ force: true }); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'page-load' ); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts index c4e87ac15fbe1..4f72e968d81f8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts @@ -44,7 +44,7 @@ describe('Service inventory - header filters', () => { cy.contains('Services'); cy.contains('opbeans-node'); cy.contains('service 1'); - cy.get('[data-test-subj="headerFilterKuerybar"]') + cy.getByTestSubj('headerFilterKuerybar') .type(`service.name: "${specialServiceName}"`) .type('{enter}'); cy.contains('service 1'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts index 015df91d792e9..2d40c690a8c92 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts @@ -93,7 +93,7 @@ describe('Service inventory', () => { it('with the correct environment when changing the environment', () => { cy.wait(mainAliasNames); - cy.get('[data-test-subj="environmentFilter"]').type('production'); + cy.getByTestSubj('environmentFilter').type('production'); cy.contains('button', 'production').click(); @@ -175,7 +175,7 @@ describe('Service inventory', () => { ]) ); }); - cy.get('[data-test-subj="pagination-button-1"]').click(); + cy.getByTestSubj('pagination-button-1').click(); cy.wait('@detailedStatisticsRequest').then((payload) => { expect(payload.request.body.serviceNames).eql( JSON.stringify([ diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/aws_lambda/aws_lamba.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/aws_lambda/aws_lambda.cy.ts similarity index 100% rename from x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/aws_lambda/aws_lamba.cy.ts rename to x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/aws_lambda/aws_lambda.cy.ts diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts index b175eb0430ed4..d693148010c7e 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts @@ -50,16 +50,12 @@ describe('Errors table', () => { it('clicking on type adds a filter in the kuerybar and navigates to errors page', () => { cy.visitKibana(serviceOverviewHref); - cy.get('[data-test-subj="headerFilterKuerybar"]') - .invoke('val') - .should('be.empty'); + cy.getByTestSubj('headerFilterKuerybar').invoke('val').should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('Exception').click({ force: true, }); - cy.get('[data-test-subj="headerFilterKuerybar"]') - .its('length') - .should('be.gt', 0); + cy.getByTestSubj('headerFilterKuerybar').its('length').should('be.gt', 0); cy.get('table').find('td:contains("Exception")').should('have.length', 1); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts index 6376d544821aa..8a25024506696 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts @@ -77,13 +77,13 @@ describe('Service overview - header filters', () => { cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-node'); cy.url().should('not.include', 'transactionType'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'request' ); - cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); + cy.getByTestSubj('headerFilterTransactionType').select('Worker'); cy.url().should('include', 'transactionType=Worker'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); @@ -94,7 +94,7 @@ describe('Service overview - header filters', () => { cy.intercept('GET', endpoint).as(name); }); cy.visitKibana(serviceOverviewHref); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'request' ); @@ -104,9 +104,9 @@ describe('Service overview - header filters', () => { value: 'transactionType=request', }); - cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); + cy.getByTestSubj('headerFilterTransactionType').select('Worker'); cy.url().should('include', 'transactionType=Worker'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); @@ -129,18 +129,12 @@ describe('Service overview - header filters', () => { }) ); cy.contains('opbeans-java'); - cy.get('[data-test-subj="headerFilterKuerybar"]').type('transaction.n'); + cy.getByTestSubj('headerFilterKuerybar').type('transaction.n'); cy.contains('transaction.name'); - cy.get('[data-test-subj="suggestionContainer"]') - .find('li') - .first() - .click(); - cy.get('[data-test-subj="headerFilterKuerybar"]').type(':'); - cy.get('[data-test-subj="suggestionContainer"]') - .find('li') - .first() - .click(); - cy.get('[data-test-subj="headerFilterKuerybar"]').type('{enter}'); + cy.getByTestSubj('suggestionContainer').find('li').first().click(); + cy.getByTestSubj('headerFilterKuerybar').type(':'); + cy.getByTestSubj('suggestionContainer').find('li').first().click(); + cy.getByTestSubj('headerFilterKuerybar').type('{enter}'); cy.url().should('include', '&kuery=transaction.name'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts index 03653df2b0bb6..578b116a10592 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts @@ -63,7 +63,7 @@ describe('Instances table', () => { it('shows empty message', () => { cy.visitKibana(testServiveHref); cy.contains('test-service'); - cy.get('[data-test-subj="serviceInstancesTableContainer"]').contains( + cy.getByTestSubj('serviceInstancesTableContainer').contains( 'No instances found' ); }); @@ -77,9 +77,7 @@ describe('Instances table', () => { it('hides instances table', () => { cy.visitKibana(serviceRumOverviewHref); cy.contains('opbeans-rum'); - cy.get('[data-test-subj="serviceInstancesTableContainer"]').should( - 'not.exist' - ); + cy.getByTestSubj('serviceInstancesTableContainer').should('not.exist'); }); }); @@ -109,10 +107,8 @@ describe('Instances table', () => { cy.contains(serviceNodeName); cy.wait('@instancesDetailsRequest'); - cy.get( - `[data-test-subj="instanceDetailsButton_${serviceNodeName}"]` - ).realClick(); - cy.get('[data-test-subj="loadingSpinner"]').should('be.visible'); + cy.getByTestSubj(`instanceDetailsButton_${serviceNodeName}`).realClick(); + cy.getByTestSubj('loadingSpinner').should('be.visible'); cy.wait('@instanceDetailsRequest').then(() => { cy.contains('Service'); }); @@ -130,9 +126,7 @@ describe('Instances table', () => { cy.contains(serviceNodeName); cy.wait('@instancesDetailsRequest'); - cy.get( - `[data-test-subj="instanceActionsButton_${serviceNodeName}"]` - ).click(); + cy.getByTestSubj(`instanceActionsButton_${serviceNodeName}`).click(); cy.contains('Pod logs'); cy.contains('Pod metrics'); // cy.contains('Container logs'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/service_overview.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/service_overview.cy.ts index e8319c8efafeb..8173e94557b29 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/service_overview.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/service_overview.cy.ts @@ -109,13 +109,13 @@ describe('Service Overview', () => { cy.contains('opbeans-node'); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); - cy.get('[data-test-subj="latencyChart"]'); - cy.get('[data-test-subj="throughput"]'); - cy.get('[data-test-subj="transactionsGroupTable"]'); - cy.get('[data-test-subj="serviceOverviewErrorsTable"]'); - cy.get('[data-test-subj="dependenciesTable"]'); - cy.get('[data-test-subj="instancesLatencyDistribution"]'); - cy.get('[data-test-subj="serviceOverviewInstancesTable"]'); + cy.getByTestSubj('latencyChart'); + cy.getByTestSubj('throughput'); + cy.getByTestSubj('transactionsGroupTable'); + cy.getByTestSubj('serviceOverviewErrorsTable'); + cy.getByTestSubj('dependenciesTable'); + cy.getByTestSubj('instancesLatencyDistribution'); + cy.getByTestSubj('serviceOverviewInstancesTable'); }); }); @@ -134,17 +134,17 @@ describe('Service Overview', () => { cy.wait('@transactionTypesRequest'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'request' ); - cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').select('Worker'); + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); cy.contains('Transactions').click(); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); @@ -159,18 +159,18 @@ describe('Service Overview', () => { cy.visitKibana(baseUrl); cy.wait('@transactionTypesRequest'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'request' ); - cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').select('Worker'); + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); cy.contains('View transactions').click(); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); @@ -226,7 +226,7 @@ describe('Service Overview', () => { 'suggestionsRequest' ); - cy.get('[data-test-subj="environmentFilter"] input').type('production', { + cy.getByTestSubj('environmentFilter').find('input').type('production', { force: true, }); @@ -235,9 +235,7 @@ describe('Service Overview', () => { value: 'fieldValue=production', }); - cy.get( - '[data-test-subj="comboBoxOptionsList environmentFilter-optionsList"]' - ) + cy.getByTestSubj('comboBoxOptionsList environmentFilter-optionsList') .contains('production') .click({ force: true }); @@ -271,11 +269,11 @@ describe('Service Overview', () => { }); it('when selecting a different comparison window', () => { - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1d'); // selects another comparison type - cy.get('[data-test-subj="comparisonSelect"]').select('1w'); - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1w'); + cy.getByTestSubj('comparisonSelect').select('1w'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1w'); cy.expectAPIsToHaveBeenCalledWith({ apisIntercepted: aliasNamesWithComparison, value: 'offset', diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts index 718a2a4a06cf7..bce3da42d5a3f 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts @@ -101,18 +101,18 @@ describe('Service overview: Time Comparison', () => { cy.visitKibana(serviceOverviewPath); cy.contains('opbeans-java'); // opens the page with "Day before" selected - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1d'); // selects another comparison type - cy.get('[data-test-subj="comparisonSelect"]').select('1w'); - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1w'); + cy.getByTestSubj('comparisonSelect').select('1w'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1w'); }); it('changes comparison type when a new time range is selected', () => { cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); // Time comparison default value - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1d'); cy.contains('Day before'); cy.contains('Week before'); @@ -121,17 +121,14 @@ describe('Service overview: Time Comparison', () => { '2021-10-20T00:00:00.000Z' ); - cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); + cy.getByTestSubj('superDatePickerApplyTimeButton').click(); - cy.get('[data-test-subj="comparisonSelect"]').should( - 'have.value', - '864000000ms' - ); - cy.get('[data-test-subj="comparisonSelect"]').should( + cy.getByTestSubj('comparisonSelect').should('have.value', '864000000ms'); + cy.getByTestSubj('comparisonSelect').should( 'not.contain.text', 'Day before' ); - cy.get('[data-test-subj="comparisonSelect"]').should( + cy.getByTestSubj('comparisonSelect').should( 'not.contain.text', 'Week before' ); @@ -141,17 +138,14 @@ describe('Service overview: Time Comparison', () => { cy.contains('Week before'); cy.changeTimeRange('Last 24 hours'); - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); + cy.getByTestSubj('comparisonSelect').should('have.value', '1d'); cy.contains('Day before'); cy.contains('Week before'); cy.changeTimeRange('Last 7 days'); - cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1w'); - cy.get('[data-test-subj="comparisonSelect"]').should( - 'contain.text', - 'Week before' - ); - cy.get('[data-test-subj="comparisonSelect"]').should( + cy.getByTestSubj('comparisonSelect').should('have.value', '1w'); + cy.getByTestSubj('comparisonSelect').should('contain.text', 'Week before'); + cy.getByTestSubj('comparisonSelect').should( 'not.contain.text', 'Day before' ); @@ -170,7 +164,7 @@ describe('Service overview: Time Comparison', () => { ); cy.contains('opbeans-java'); cy.wait('@throughputChartRequest'); - cy.get('[data-test-subj="throughput"]') + cy.getByTestSubj('throughput') .get('#echHighlighterClipPath__throughput') .realHover({ position: 'center' }); cy.contains('Week before'); @@ -186,17 +180,17 @@ describe('Service overview: Time Comparison', () => { cy.contains('opbeans-java'); // Comparison is enabled by default - cy.get('[data-test-subj="comparisonSelect"]').should('be.enabled'); + cy.getByTestSubj('comparisonSelect').should('be.enabled'); // toggles off comparison cy.contains('Comparison').click(); - cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); + cy.getByTestSubj('comparisonSelect').should('be.disabled'); }); it('calls APIs without comparison time range', () => { cy.visitKibana(serviceOverviewHref); - cy.get('[data-test-subj="comparisonSelect"]').should('be.enabled'); + cy.getByTestSubj('comparisonSelect').should('be.enabled'); const offset = `offset=1d`; // When the page loads it fetches all APIs with comparison time range @@ -212,7 +206,7 @@ describe('Service overview: Time Comparison', () => { // toggles off comparison cy.contains('Comparison').click(); - cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); + cy.getByTestSubj('comparisonSelect').should('be.disabled'); // When comparison is disabled APIs are called withou comparison time range cy.wait(apisToIntercept.map(({ name }) => `@${name}`)).then( (interceptions) => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts index cddba048e8a18..60b36b10ee4a3 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts @@ -50,8 +50,8 @@ describe('Span links', () => { ); cy.contains('Transaction A').click(); cy.contains('2 Span links'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerInternalOnlyIds.spanAId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerInternalOnlyIds.spanAId}` ).realHover(); cy.contains('2 Span links found'); cy.contains('2 incoming'); @@ -64,8 +64,8 @@ describe('Span links', () => { ); cy.contains('Transaction B').click(); cy.contains('2 Span links'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerExternalOnlyIds.spanBId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerExternalOnlyIds.spanBId}` ).realHover(); cy.contains('2 Span links found'); cy.contains('1 incoming'); @@ -78,8 +78,8 @@ describe('Span links', () => { ); cy.contains('Transaction C').click(); cy.contains('2 Span links'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerConsumerIds.transactionCId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerConsumerIds.transactionCId}` ).realHover(); cy.contains('2 Span links found'); cy.contains('1 incoming'); @@ -92,8 +92,8 @@ describe('Span links', () => { ); cy.contains('Transaction C').click(); cy.contains('1 Span link'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerConsumerIds.spanCId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerConsumerIds.spanCId}` ).realHover(); cy.contains('1 Span link found'); cy.contains('1 incoming'); @@ -106,8 +106,8 @@ describe('Span links', () => { ); cy.contains('Transaction D').click(); cy.contains('2 Span links'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerMultipleIds.transactionDId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerMultipleIds.transactionDId}` ).realHover(); cy.contains('2 Span links found'); cy.contains('0 incoming'); @@ -120,8 +120,8 @@ describe('Span links', () => { ); cy.contains('Transaction D').click(); cy.contains('2 Span links'); - cy.get( - `[data-test-subj="spanLinksBadge_${ids.producerMultipleIds.spanEId}"]` + cy.getByTestSubj( + `spanLinksBadge_${ids.producerMultipleIds.spanEId}` ).realHover(); cy.contains('2 Span links found'); cy.contains('0 incoming'); @@ -136,7 +136,7 @@ describe('Span links', () => { ); cy.contains('Transaction A').click(); cy.contains('Span A').click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('producer-consumer') .should('have.attr', 'href') .and('include', '/services/producer-consumer/overview'); @@ -155,7 +155,7 @@ describe('Span links', () => { 'include', `link-to/transaction/${ids.producerMultipleIds.transactionDId}?waterfallItemId=${ids.producerMultipleIds.transactionDId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').should( + cy.getByTestSubj('spanLinkTypeSelect').should( 'contain.text', 'Outgoing links (0)' ); @@ -167,7 +167,7 @@ describe('Span links', () => { ); cy.contains('Transaction B').click(); cy.contains('Span B').click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('consumer-multiple') .should('have.attr', 'href') @@ -178,9 +178,7 @@ describe('Span links', () => { 'include', `link-to/transaction/${ids.producerMultipleIds.transactionDId}?waterfallItemId=${ids.producerMultipleIds.spanEId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').select( - 'Outgoing links (1)' - ); + cy.getByTestSubj('spanLinkTypeSelect').select('Outgoing links (1)'); cy.contains('Unknown'); cy.contains('trace#1-span#1'); }); @@ -193,7 +191,7 @@ describe('Span links', () => { cy.get( `[aria-controls="${ids.producerConsumerIds.transactionCId}"]` ).click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('consumer-multiple') .should('have.attr', 'href') @@ -205,9 +203,7 @@ describe('Span links', () => { `link-to/transaction/${ids.producerMultipleIds.transactionDId}?waterfallItemId=${ids.producerMultipleIds.spanEId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').select( - 'Outgoing links (1)' - ); + cy.getByTestSubj('spanLinkTypeSelect').select('Outgoing links (1)'); cy.contains('producer-internal-only') .should('have.attr', 'href') .and('include', '/services/producer-internal-only/overview'); @@ -225,7 +221,7 @@ describe('Span links', () => { ); cy.contains('Transaction C').click(); cy.contains('Span C').click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('consumer-multiple') .should('have.attr', 'href') @@ -237,7 +233,7 @@ describe('Span links', () => { `link-to/transaction/${ids.producerMultipleIds.transactionDId}?waterfallItemId=${ids.producerMultipleIds.transactionDId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').should( + cy.getByTestSubj('spanLinkTypeSelect').should( 'contain.text', 'Outgoing links (0)' ); @@ -251,7 +247,7 @@ describe('Span links', () => { cy.get( `[aria-controls="${ids.producerMultipleIds.transactionDId}"]` ).click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('producer-consumer') .should('have.attr', 'href') @@ -273,7 +269,7 @@ describe('Span links', () => { `link-to/transaction/${ids.producerInternalOnlyIds.transactionAId}?waterfallItemId=${ids.producerInternalOnlyIds.spanAId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').should( + cy.getByTestSubj('spanLinkTypeSelect').should( 'contain.text', 'Incoming links (0)' ); @@ -285,7 +281,7 @@ describe('Span links', () => { ); cy.contains('Transaction D').click(); cy.contains('Span E').click(); - cy.get('[data-test-subj="spanLinksTab"]').click(); + cy.getByTestSubj('spanLinksTab').click(); cy.contains('producer-external-only') .should('have.attr', 'href') @@ -307,7 +303,7 @@ describe('Span links', () => { `link-to/transaction/${ids.producerConsumerIds.transactionCId}?waterfallItemId=${ids.producerConsumerIds.transactionCId}` ); - cy.get('[data-test-subj="spanLinkTypeSelect"]').should( + cy.getByTestSubj('spanLinkTypeSelect').should( 'contain.text', 'Incoming links (0)' ); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts index 5172a5f167fc9..09bd37f5b0b6c 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/transaction_details.cy.ts @@ -42,15 +42,15 @@ describe('Transaction details', () => { it('shows transaction name and transaction charts', () => { cy.contains('h2', 'GET /api/product'); - cy.get('[data-test-subj="latencyChart"]'); - cy.get('[data-test-subj="throughput"]'); - cy.get('[data-test-subj="transactionBreakdownChart"]'); - cy.get('[data-test-subj="errorRate"]'); + cy.getByTestSubj('latencyChart'); + cy.getByTestSubj('throughput'); + cy.getByTestSubj('transactionBreakdownChart'); + cy.getByTestSubj('errorRate'); }); it('shows top errors table', () => { cy.contains('Top 5 errors'); - cy.get('[data-test-subj="topErrorsForTransactionTable"]') + cy.getByTestSubj('topErrorsForTransactionTable') .contains('a', '[MockError] Foo') .click(); cy.url().should('include', 'opbeans-java/errors'); @@ -58,7 +58,7 @@ describe('Transaction details', () => { describe('when navigating to a trace sample', () => { it('keeps the same trace sample after reloading the page', () => { - cy.get('[data-test-subj="pagination-button-last"]').click(); + cy.getByTestSubj('pagination-button-last').click(); cy.url().then((url) => { cy.reload(); cy.url().should('eq', url); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transactions_overview/transactions_overview.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transactions_overview/transactions_overview.cy.ts index 83753b7fe2595..2e7e0d336cd5d 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transactions_overview/transactions_overview.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transactions_overview/transactions_overview.cy.ts @@ -49,17 +49,17 @@ describe('Transactions Overview', () => { it('persists transaction type selected when navigating to Overview tab', () => { cy.visitKibana(serviceTransactionsHref); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'request' ); - cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').select('Worker'); + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); cy.get('a[href*="/app/apm/services/opbeans-node/overview"]').click(); - cy.get('[data-test-subj="headerFilterTransactionType"]').should( + cy.getByTestSubj('headerFilterTransactionType').should( 'have.value', 'Worker' ); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 7830e791c3655..9e6e0189e636c 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -52,15 +52,19 @@ Cypress.Commands.add( } ); +Cypress.Commands.add('getByTestSubj', (selector: string) => { + return cy.get(`[data-test-subj="${selector}"]`); +}); + Cypress.Commands.add('changeTimeRange', (value: string) => { - cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); + cy.getByTestSubj('superDatePickerToggleQuickMenuButton').click(); cy.contains(value).click(); }); Cypress.Commands.add('visitKibana', (url: string) => { cy.visit(url); - cy.get('[data-test-subj="kbnLoadingMessage"]').should('exist'); - cy.get('[data-test-subj="kbnLoadingMessage"]').should('not.exist', { + cy.getByTestSubj('kbnLoadingMessage').should('exist'); + cy.getByTestSubj('kbnLoadingMessage').should('not.exist', { timeout: 50000, }); }); @@ -70,13 +74,13 @@ Cypress.Commands.add( (start: string, end: string) => { const format = 'MMM D, YYYY @ HH:mm:ss.SSS'; - cy.get('[data-test-subj="superDatePickerstartDatePopoverButton"]').click(); - cy.get('[data-test-subj="superDatePickerAbsoluteDateInput"]') + cy.getByTestSubj('superDatePickerstartDatePopoverButton').click(); + cy.getByTestSubj('superDatePickerAbsoluteDateInput') .eq(0) .clear({ force: true }) .type(moment(start).format(format), { force: true }); - cy.get('[data-test-subj="superDatePickerendDatePopoverButton"]').click(); - cy.get('[data-test-subj="superDatePickerAbsoluteDateInput"]') + cy.getByTestSubj('superDatePickerendDatePopoverButton').click(); + cy.getByTestSubj('superDatePickerAbsoluteDateInput') .eq(1) .clear({ force: true }) .type(moment(end).format(format), { force: true }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts index 2235847e584a4..5d59d4691820a 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts @@ -22,5 +22,6 @@ declare namespace Cypress { value: string; }): void; updateAdvancedSettings(settings: Record): void; + getByTestSubj(selector: string): Chainable>; } } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts b/x-pack/plugins/apm/ftr_e2e/setup_cypress_node_events.ts similarity index 62% rename from x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts rename to x-pack/plugins/apm/ftr_e2e/setup_cypress_node_events.ts index 8adaad0b71c63..0e3cd47966960 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts +++ b/x-pack/plugins/apm/ftr_e2e/setup_cypress_node_events.ts @@ -11,28 +11,13 @@ import { LogLevel, } from '@kbn/apm-synthtrace'; import { createEsClientForTesting } from '@kbn/test'; +import { some } from 'lodash'; +import del from 'del'; -// *********************************************************** -// This example plugins/index.ts can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ - -export const plugin: Cypress.PluginConfig = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - +export function setupNodeEvents( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions +) { const client = createEsClientForTesting({ esUrl: config.env.ES_NODE, requestTimeout: config.env.ES_REQUEST_TIMEOUT, @@ -65,4 +50,24 @@ export const plugin: Cypress.PluginConfig = (on, config) => { return null; }, }); -}; + + on('after:spec', (spec, results) => { + // Delete videos that have no failures or retries + if (results && results.video) { + const failures = some(results.tests, (test) => { + return some(test.attempts, { state: 'failed' }); + }); + if (!failures) { + del(results.video); + } + } + }); + + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'electron' && browser.isHeadless) { + launchOptions.preferences.width = 1440; + launchOptions.preferences.height = 1600; + } + return launchOptions; + }); +} diff --git a/x-pack/plugins/apm/public/components/app/alerts_overview/alerts_overview_table.test.tsx b/x-pack/plugins/apm/public/components/app/alerts_overview/alerts_overview_table.test.tsx new file mode 100644 index 0000000000000..711b7ff389fe3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/alerts_overview/alerts_overview_table.test.tsx @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, waitFor, act } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import React, { ReactNode } from 'react'; +import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import * as useApmParamsHooks from '../../../hooks/use_apm_params'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { CoreStart } from '@kbn/core/public'; +import { AlertsOverview } from '.'; + +const getAlertsStateTableMock = jest.fn(); + +function Wrapper({ children }: { children?: ReactNode }) { + const KibanaReactContext = createKibanaReactContext({ + triggersActionsUi: { + getAlertsStateTable: getAlertsStateTableMock.mockReturnValue( +
+ ), + alertsTableConfigurationRegistry: '', + }, + } as Partial); + + return ( + + + {children} + + + ); +} + +const renderOptions = { wrapper: Wrapper }; + +describe('AlertsTable', () => { + beforeEach(() => { + jest.spyOn(useApmParamsHooks as any, 'useApmParams').mockReturnValue({ + path: { + serviceName: 'opbeans', + }, + query: { + rangeFrom: 'now-24h', + rangeTo: 'now', + environment: 'testing', + }, + }); + jest.clearAllMocks(); + }); + + it('renders alerts table', async () => { + const { getByTestId } = render(, renderOptions); + + await waitFor(async () => { + expect(getByTestId('alerts-table')).toBeTruthy(); + }); + }); + it('should call alerts table with correct propts', async () => { + act(() => { + render(, renderOptions); + }); + + await waitFor(async () => { + expect(getAlertsStateTableMock).toHaveBeenCalledWith( + { + alertsTableConfigurationRegistry: '', + id: 'service-overview-alerts', + configurationId: 'observability', + featureIds: ['apm'], + query: { + bool: { + filter: [ + { + term: { 'service.name': 'opbeans' }, + }, + { + term: { 'service.environment': 'testing' }, + }, + ], + }, + }, + showExpandToDetails: false, + }, + {} + ); + }); + }); + + it('should call alerts table with active filter', async () => { + const { getByTestId } = render(, renderOptions); + + await act(async () => { + const inputEl = getByTestId('active'); + inputEl.click(); + }); + + await waitFor(async () => { + expect(getAlertsStateTableMock).toHaveBeenLastCalledWith( + { + alertsTableConfigurationRegistry: '', + id: 'service-overview-alerts', + configurationId: 'observability', + featureIds: ['apm'], + query: { + bool: { + filter: [ + { + term: { 'service.name': 'opbeans' }, + }, + { + term: { 'kibana.alert.status': 'active' }, + }, + { + term: { 'service.environment': 'testing' }, + }, + ], + }, + }, + showExpandToDetails: false, + }, + {} + ); + }); + }); + + it('should call alerts table with recovered filter', async () => { + const { getByTestId } = render(, renderOptions); + + await act(async () => { + const inputEl = getByTestId('recovered'); + inputEl.click(); + }); + + await waitFor(async () => { + expect(getAlertsStateTableMock).toHaveBeenLastCalledWith( + { + alertsTableConfigurationRegistry: '', + id: 'service-overview-alerts', + configurationId: 'observability', + featureIds: ['apm'], + query: { + bool: { + filter: [ + { + term: { 'service.name': 'opbeans' }, + }, + { + term: { 'kibana.alert.status': 'recovered' }, + }, + { + term: { 'service.environment': 'testing' }, + }, + ], + }, + }, + showExpandToDetails: false, + }, + {} + ); + }); + }); +}); 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/public/components/app/service_logs/index.tsx b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx index 1ea26c08e7fd6..4e48dadb2b921 100644 --- a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx @@ -5,13 +5,10 @@ * 2.0. */ -import { isEmpty } from 'lodash'; -import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; -import React, { useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; +import React from 'react'; import moment from 'moment'; import { LogStream } from '@kbn/infra-plugin/public'; -import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { useFetcher } from '../../../hooks/use_fetcher'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { APIReturnType } from '../../../services/rest/create_call_apm_api'; @@ -32,7 +29,7 @@ export function ServiceLogs() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { data, status } = useFetcher( + const { data } = useFetcher( (callApmApi) => { if (start && end) { return callApmApi( @@ -54,32 +51,6 @@ export function ServiceLogs() { [environment, kuery, serviceName, start, end] ); - const noInfrastructureData = useMemo(() => { - return isEmpty(data?.containerIds) && isEmpty(data?.hostNames); - }, [data]); - - if (status === FETCH_STATUS.LOADING) { - return ( -
- -
- ); - } - - if (status === FETCH_STATUS.SUCCESS && noInfrastructureData) { - return ( - - {i18n.translate('xpack.apm.serviceLogs.noInfrastructureMessage', { - defaultMessage: 'There are no log messages to display.', - })} - - } - /> - ); - } - return ( - ) : undefined, + ), hidden: isMetricsTabHidden({ agentName, runtimeName, @@ -318,6 +318,9 @@ function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { label: i18n.translate('xpack.apm.home.serviceLogsTabLabel', { defaultMessage: 'Logs', }), + append: isServerlessAgent(runtimeName) && ( + + ), hidden: !agentName || isRumAgentName(agentName) || isMobileAgentName(agentName), }, 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..253c8ed9e44d2 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,102 @@ describe('data telemetry collection tasks', () => { }); }); }); + + describe('service groups', () => { + const task = tasks.find((t) => t.name === 'service_groups'); + const savedObjectsClient = savedObjectsClientMock.create(); + + it('returns service group stats', async () => { + savedObjectsClient.find.mockResolvedValueOnce({ + 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: ['space-1'], + attributes: { + color: '#5094C4', + kuery: 'agent.name: go', + groupName: 'agent', + }, + references: [], + score: 0, + }, + ], + }); + + expect(await task?.executor({ savedObjectsClient } as any)).toEqual({ + service_groups: { + kuery_fields: ['service.environment', 'agent.name'], + total: 2, + }, + }); + }); + + it('should return stats from all spaces', () => { + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + type: 'apm-service-group', + page: 1, + perPage: 500, + sortField: 'updated_at', + sortOrder: 'desc', + namespaces: ['*'], + }); + }); + + it('returns unique fields', async () => { + savedObjectsClient.find.mockResolvedValueOnce({ + 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: 'service.environment: production and agent.name: go', + groupName: 'agent', + }, + references: [], + score: 0, + }, + ], + }); + + 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..2235de6c0be9f 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 @@ -5,7 +5,7 @@ * 2.0. */ import { fromKueryExpression } from '@kbn/es-query'; -import { flatten, merge, sortBy, sum, pickBy } from 'lodash'; +import { flatten, merge, sortBy, sum, pickBy, uniq } from 'lodash'; import { createHash } from 'crypto'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; @@ -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,9 +1135,10 @@ 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', + namespaces: ['*'], }); const kueryNodes = response.saved_objects.map( @@ -1147,7 +1149,8 @@ export const tasks: TelemetryTask[] = [ return { service_groups: { - kuery_fields: kueryFields, + kuery_fields: uniq(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/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts index 4cd9df6bf2137..5cc9f8bb427cf 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts @@ -9,7 +9,10 @@ import { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types'; import { APMRouteHandlerResources } from '../../../../routes/typings'; import { getInfraMetricIndices } from '../../get_infra_metric_indices'; -type InfraMetricsSearchParams = Omit; +type InfraMetricsSearchParams = Omit & { + size: number; + track_total_hits: boolean | number; +}; export type InfraMetricsClient = ReturnType; 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/infrastructure/get_host_names.ts b/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts index 8f47c2d1664bf..257233fada9cb 100644 --- a/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts +++ b/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts @@ -29,6 +29,7 @@ export async function getContainerHostNames({ const response = await infraMetricsClient.search({ size: 0, + track_total_hits: false, query: { bool: { filter: [ diff --git a/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/cold_start_duration.ts b/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/cold_start_duration.ts index e66d855d1be99..5a78f5d97d5e8 100644 --- a/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/cold_start_duration.ts +++ b/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/cold_start_duration.ts @@ -11,6 +11,7 @@ import { FAAS_COLDSTART_DURATION } from '../../../../../common/elasticsearch_fie import { Setup } from '../../../../lib/helpers/setup_request'; import { fetchAndTransformMetrics } from '../../fetch_and_transform_metrics'; import { ChartBase } from '../../types'; +import { isFiniteNumber } from '../../../../../common/utils/is_finite_number'; const chartBase: ChartBase = { title: i18n.translate('xpack.apm.agentMetrics.serverless.coldStartDuration', { @@ -37,7 +38,7 @@ const chartBase: ChartBase = { ), }; -export function getColdStartDuration({ +export async function getColdStartDuration({ environment, kuery, setup, @@ -52,7 +53,7 @@ export function getColdStartDuration({ start: number; end: number; }) { - return fetchAndTransformMetrics({ + const coldStartDurationMetric = await fetchAndTransformMetrics({ environment, kuery, setup, @@ -64,4 +65,24 @@ export function getColdStartDuration({ additionalFilters: [{ exists: { field: FAAS_COLDSTART_DURATION } }], operationName: 'get_cold_start_duration', }); + + const [series] = coldStartDurationMetric.series; + + const data = series.data.map(({ x, y }) => ({ + x, + // Cold start duration duration is stored in ms, convert it to microseconds so it uses the same unit as the other charts + y: isFiniteNumber(y) ? y * 1000 : y, + })); + + return { + ...coldStartDurationMetric, + series: [ + { + ...series, + // Cold start duration duration is stored in ms, convert it to microseconds + overallValue: series.overallValue * 1000, + data, + }, + ], + }; } diff --git a/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/serverless_function_latency.ts b/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/serverless_function_latency.ts index 0db9385615d2a..0a27c66ef036b 100644 --- a/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/serverless_function_latency.ts +++ b/x-pack/plugins/apm/server/routes/metrics/by_agent/serverless/serverless_function_latency.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { euiLightVars as theme } from '@kbn/ui-theme'; import { FAAS_BILLED_DURATION } from '../../../../../common/elasticsearch_fieldnames'; import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; +import { isFiniteNumber } from '../../../../../common/utils/is_finite_number'; import { getVizColorForIndex } from '../../../../../common/viz_colors'; import { Setup } from '../../../../lib/helpers/setup_request'; import { getLatencyTimeseries } from '../../../transactions/get_latency_charts'; @@ -123,8 +124,23 @@ export async function getServerlessFunctionLatency({ getServerlessLantecySeries({ ...options, searchAggregatedTransactions }), ]); + const [series] = billedDurationMetrics.series; + const data = series.data.map(({ x, y }) => ({ + x, + // Billed duration is stored in ms, convert it to microseconds so it uses the same unit as the other chart + y: isFiniteNumber(y) ? y * 1000 : y, + })); + return { ...billedDurationMetrics, - series: [...billedDurationMetrics.series, ...serverlessDurationSeries], + series: [ + { + ...series, + // Billed duration is stored in ms, convert it to microseconds + overallValue: series.overallValue * 1000, + data, + }, + ...serverlessDurationSeries, + ], }; } 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/apm/server/routes/services/get_service_instance_container_metadata.ts b/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts index ebce4f9338714..b5b98890ac636 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts @@ -51,6 +51,7 @@ export const getServiceInstanceContainerMetadata = async ({ const response = await infraMetricsClient.search({ size: 1, + track_total_hits: false, query: { bool: { filter: [ diff --git a/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts b/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts index c1dcfe97e208b..4b8d979a7dd85 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts @@ -43,6 +43,7 @@ export const getServiceOverviewContainerMetadata = async ({ const response = await infraMetricsClient.search({ size: 0, + track_total_hits: false, query: { bool: { filter: [ 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/common/mock/permissions.ts b/x-pack/plugins/cases/public/common/mock/permissions.ts index 1166dbed8ca88..7395e966037ea 100644 --- a/x-pack/plugins/cases/public/common/mock/permissions.ts +++ b/x-pack/plugins/cases/public/common/mock/permissions.ts @@ -17,6 +17,8 @@ export const noUpdateCasesPermissions = () => buildCasesPermissions({ update: fa export const noPushCasesPermissions = () => buildCasesPermissions({ push: false }); export const noDeleteCasesPermissions = () => buildCasesPermissions({ delete: false }); export const writeCasesPermissions = () => buildCasesPermissions({ read: false }); +export const onlyDeleteCasesPermission = () => + buildCasesPermissions({ read: false, create: false, update: false, delete: true, push: false }); export const buildCasesPermissions = (overrides: Partial> = {}) => { const create = overrides.create ?? true; diff --git a/x-pack/plugins/cases/public/components/actions/delete/translations.ts b/x-pack/plugins/cases/public/components/actions/delete/translations.ts new file mode 100644 index 0000000000000..f875ea2c000c4 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/delete/translations.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 { i18n } from '@kbn/i18n'; +export { DELETED_CASES } from '../../../common/translations'; + +export const BULK_ACTION_DELETE_LABEL = i18n.translate('xpack.cases.actions.deleteMultipleCases', { + defaultMessage: 'Delete cases', +}); + +export const DELETE_ACTION_LABEL = i18n.translate('xpack.cases.actions.deleteSingleCase', { + defaultMessage: 'Delete case', +}); diff --git a/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.test.tsx b/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.test.tsx new file mode 100644 index 0000000000000..747aa0e84e1d7 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.test.tsx @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useDeleteAction } from './use_delete_action'; + +import * as api from '../../../containers/api'; +import { basicCase } from '../../../containers/mock'; + +jest.mock('../../../containers/api'); + +describe('useDeleteAction', () => { + let appMockRender: AppMockRenderer; + const onAction = jest.fn(); + const onActionSuccess = jest.fn(); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('renders an action with one case', async () => { + const { result } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current.getAction([basicCase])).toMatchInlineSnapshot(` + Object { + "data-test-subj": "cases-bulk-action-delete", + "disabled": false, + "icon": , + "key": "cases-bulk-action-delete", + "name": + Delete case + , + "onClick": [Function], + } + `); + }); + + it('renders an action with multiple cases', async () => { + const { result } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current.getAction([basicCase, basicCase])).toMatchInlineSnapshot(` + Object { + "data-test-subj": "cases-bulk-action-delete", + "disabled": false, + "icon": , + "key": "cases-bulk-action-delete", + "name": + Delete cases + , + "onClick": [Function], + } + `); + }); + + it('deletes the selected cases', async () => { + const deleteSpy = jest.spyOn(api, 'deleteCases'); + + const { result, waitFor } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const action = result.current.getAction([basicCase]); + + act(() => { + action.onClick(); + }); + + expect(onAction).toHaveBeenCalled(); + expect(result.current.isModalVisible).toBe(true); + + act(() => { + result.current.onConfirmDeletion(); + }); + + await waitFor(() => { + expect(result.current.isModalVisible).toBe(false); + expect(onActionSuccess).toHaveBeenCalled(); + expect(deleteSpy).toHaveBeenCalledWith(['basic-case-id'], expect.anything()); + }); + }); + + it('closes the modal', async () => { + const { result, waitFor } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const action = result.current.getAction([basicCase]); + + act(() => { + action.onClick(); + }); + + expect(result.current.isModalVisible).toBe(true); + + act(() => { + result.current.onCloseModal(); + }); + + await waitFor(() => { + expect(result.current.isModalVisible).toBe(false); + }); + }); + + it('shows the success toaster correctly when delete one case', async () => { + const { result, waitFor } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const action = result.current.getAction([basicCase]); + + act(() => { + action.onClick(); + }); + + act(() => { + result.current.onConfirmDeletion(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + 'Deleted case' + ); + }); + }); + + it('shows the success toaster correctly when delete multiple case', async () => { + const { result, waitFor } = renderHook( + () => useDeleteAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const action = result.current.getAction([basicCase, basicCase]); + + act(() => { + action.onClick(); + }); + + act(() => { + result.current.onConfirmDeletion(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + 'Deleted 2 cases' + ); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.tsx b/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.tsx new file mode 100644 index 0000000000000..92184dbd64648 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/delete/use_delete_action.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useState } from 'react'; +import { EuiIcon, EuiTextColor, useEuiTheme } from '@elastic/eui'; +import { Case } from '../../../../common'; +import { useDeleteCases } from '../../../containers/use_delete_cases'; + +import * as i18n from './translations'; +import { UseActionProps } from '../types'; +import { useCasesContext } from '../../cases_context/use_cases_context'; + +const getDeleteActionTitle = (totalCases: number): string => + totalCases > 1 ? i18n.BULK_ACTION_DELETE_LABEL : i18n.DELETE_ACTION_LABEL; + +export const useDeleteAction = ({ onAction, onActionSuccess, isDisabled }: UseActionProps) => { + const euiTheme = useEuiTheme(); + const { permissions } = useCasesContext(); + const [isModalVisible, setIsModalVisible] = useState(false); + const [caseToBeDeleted, setCaseToBeDeleted] = useState([]); + const canDelete = permissions.delete; + const isActionDisabled = isDisabled || !canDelete; + + const onCloseModal = useCallback(() => setIsModalVisible(false), []); + const openModal = useCallback( + (selectedCases: Case[]) => { + onAction(); + setIsModalVisible(true); + setCaseToBeDeleted(selectedCases); + }, + [onAction] + ); + + const { mutate: deleteCases } = useDeleteCases(); + + const onConfirmDeletion = useCallback(() => { + onCloseModal(); + deleteCases( + { + caseIds: caseToBeDeleted.map(({ id }) => id), + successToasterTitle: i18n.DELETED_CASES(caseToBeDeleted.length), + }, + { onSuccess: onActionSuccess } + ); + }, [deleteCases, onActionSuccess, onCloseModal, caseToBeDeleted]); + + const color = isActionDisabled ? euiTheme.euiTheme.colors.disabled : 'danger'; + + const getAction = (selectedCases: Case[]) => { + return { + name: {getDeleteActionTitle(selectedCases.length)}, + onClick: () => openModal(selectedCases), + disabled: isActionDisabled, + 'data-test-subj': 'cases-bulk-action-delete', + icon: , + key: 'cases-bulk-action-delete', + }; + }; + + return { getAction, isModalVisible, onConfirmDeletion, onCloseModal, canDelete }; +}; + +export type UseDeleteAction = ReturnType; diff --git a/x-pack/plugins/cases/public/components/actions/severity/translations.ts b/x-pack/plugins/cases/public/components/actions/severity/translations.ts new file mode 100644 index 0000000000000..81e0caf848d15 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/severity/translations.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { CaseSeverity } from '../../../../common/api'; +import { severities } from '../../severity/config'; + +const SET_SEVERITY = ({ + totalCases, + severity, + caseTitle, +}: { + totalCases: number; + severity: string; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.actions.severity', { + values: { caseTitle, totalCases, severity }, + defaultMessage: + '{totalCases, plural, =1 {Case "{caseTitle}" was} other {{totalCases} cases were}} set to {severity}', + }); + +export const SET_SEVERITY_LOW = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + SET_SEVERITY({ + totalCases, + caseTitle, + severity: severities[CaseSeverity.LOW].label, + }); + +export const SET_SEVERITY_MEDIUM = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + SET_SEVERITY({ + totalCases, + caseTitle, + severity: severities[CaseSeverity.MEDIUM].label, + }); + +export const SET_SEVERITY_HIGH = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + SET_SEVERITY({ + totalCases, + caseTitle, + severity: severities[CaseSeverity.HIGH].label, + }); + +export const SET_SEVERITY_CRITICAL = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + SET_SEVERITY({ + totalCases, + caseTitle, + severity: severities[CaseSeverity.CRITICAL].label, + }); diff --git a/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.test.tsx b/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.test.tsx new file mode 100644 index 0000000000000..9705d5ffeb2b2 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.test.tsx @@ -0,0 +1,210 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useSeverityAction } from './use_severity_action'; + +import * as api from '../../../containers/api'; +import { basicCase } from '../../../containers/mock'; +import { CaseSeverity } from '../../../../common/api'; + +jest.mock('../../../containers/api'); + +describe('useSeverityAction', () => { + let appMockRender: AppMockRenderer; + const onAction = jest.fn(); + const onActionSuccess = jest.fn(); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('renders an action', async () => { + const { result } = renderHook( + () => + useSeverityAction({ + onAction, + onActionSuccess, + isDisabled: false, + }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current.getActions([basicCase])).toMatchInlineSnapshot(` + Array [ + Object { + "data-test-subj": "cases-bulk-action-severity-low", + "disabled": true, + "icon": "empty", + "key": "cases-bulk-action-severity-low", + "name": "Low", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-medium", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-medium", + "name": "Medium", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-high", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-high", + "name": "High", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-critical", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-critical", + "name": "Critical", + "onClick": [Function], + }, + ] + `); + }); + + it('update the severity cases', async () => { + const updateSpy = jest.spyOn(api, 'updateCases'); + + const { result, waitFor } = renderHook( + () => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + + for (const [index, severity] of [ + CaseSeverity.LOW, + CaseSeverity.MEDIUM, + CaseSeverity.HIGH, + CaseSeverity.CRITICAL, + ].entries()) { + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(onAction).toHaveBeenCalled(); + expect(onActionSuccess).toHaveBeenCalled(); + expect(updateSpy).toHaveBeenCalledWith( + [{ severity, id: basicCase.id, version: basicCase.version }], + expect.anything() + ); + }); + } + }); + + const singleCaseTests = [ + [CaseSeverity.LOW, 0, 'Case "Another horrible breach!!" was set to Low'], + [CaseSeverity.MEDIUM, 1, 'Case "Another horrible breach!!" was set to Medium'], + [CaseSeverity.HIGH, 2, 'Case "Another horrible breach!!" was set to High'], + [CaseSeverity.CRITICAL, 3, 'Case "Another horrible breach!!" was set to Critical'], + ]; + + it.each(singleCaseTests)( + 'shows the success toaster correctly when updating the severity of the case: %s', + async (_, index, expectedMessage) => { + const { result, waitFor } = renderHook( + () => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + expectedMessage + ); + }); + } + ); + + const multipleCasesTests: Array<[CaseSeverity, number, string]> = [ + [CaseSeverity.LOW, 0, '2 cases were set to Low'], + [CaseSeverity.MEDIUM, 1, '2 cases were set to Medium'], + [CaseSeverity.HIGH, 2, '2 cases were set to High'], + [CaseSeverity.CRITICAL, 3, '2 cases were set to Critical'], + ]; + + it.each(multipleCasesTests)( + 'shows the success toaster correctly when updating the severity of the case: %s', + async (_, index, expectedMessage) => { + const { result, waitFor } = renderHook( + () => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase, basicCase]); + + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + expectedMessage + ); + }); + } + ); + + const disabledTests: Array<[CaseSeverity, number]> = [ + [CaseSeverity.LOW, 0], + [CaseSeverity.MEDIUM, 1], + [CaseSeverity.HIGH, 2], + [CaseSeverity.CRITICAL, 3], + ]; + + it.each(disabledTests)('disables the severity button correctly: %s', async (severity, index) => { + const { result } = renderHook( + () => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([{ ...basicCase, severity }]); + expect(actions[index].disabled).toBe(true); + }); + + it.each(disabledTests)( + 'disables the severity button correctly if isDisabled=true: %s', + async (severity, index) => { + const { result } = renderHook( + () => useSeverityAction({ onAction, onActionSuccess, isDisabled: true }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + expect(actions[index].disabled).toBe(true); + } + ); +}); diff --git a/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.tsx b/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.tsx new file mode 100644 index 0000000000000..a4651cee82612 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/severity/use_severity_action.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; +import { CaseSeverity } from '../../../../common/api'; +import { useUpdateCases } from '../../../containers/use_bulk_update_case'; +import { Case } from '../../../../common'; + +import * as i18n from './translations'; +import { UseActionProps } from '../types'; +import { useCasesContext } from '../../cases_context/use_cases_context'; +import { severities } from '../../severity/config'; + +const getSeverityToasterMessage = (severity: CaseSeverity, cases: Case[]): string => { + const totalCases = cases.length; + const caseTitle = totalCases === 1 ? cases[0].title : ''; + + switch (severity) { + case CaseSeverity.LOW: + return i18n.SET_SEVERITY_LOW({ totalCases, caseTitle }); + case CaseSeverity.MEDIUM: + return i18n.SET_SEVERITY_MEDIUM({ totalCases, caseTitle }); + case CaseSeverity.HIGH: + return i18n.SET_SEVERITY_HIGH({ totalCases, caseTitle }); + case CaseSeverity.CRITICAL: + return i18n.SET_SEVERITY_CRITICAL({ totalCases, caseTitle }); + + default: + return ''; + } +}; + +interface UseSeverityActionProps extends UseActionProps { + selectedSeverity?: CaseSeverity; +} + +const shouldDisableSeverity = (cases: Case[], severity: CaseSeverity) => + cases.every((theCase) => theCase.severity === severity); + +export const useSeverityAction = ({ + onAction, + onActionSuccess, + isDisabled, + selectedSeverity, +}: UseSeverityActionProps) => { + const { mutate: updateCases } = useUpdateCases(); + const { permissions } = useCasesContext(); + const canUpdateSeverity = permissions.update; + const isActionDisabled = isDisabled || !canUpdateSeverity; + + const handleUpdateCaseSeverity = useCallback( + (selectedCases: Case[], severity: CaseSeverity) => { + onAction(); + const casesToUpdate = selectedCases.map((theCase) => ({ + severity, + id: theCase.id, + version: theCase.version, + })); + + updateCases( + { + cases: casesToUpdate, + successToasterTitle: getSeverityToasterMessage(severity, selectedCases), + }, + { onSuccess: onActionSuccess } + ); + }, + [onAction, updateCases, onActionSuccess] + ); + + const getSeverityIcon = (severity: CaseSeverity): string => + selectedSeverity && selectedSeverity === severity ? 'check' : 'empty'; + + const getActions = (selectedCases: Case[]): EuiContextMenuPanelItemDescriptor[] => { + return [ + { + name: severities[CaseSeverity.LOW].label, + icon: getSeverityIcon(CaseSeverity.LOW), + onClick: () => handleUpdateCaseSeverity(selectedCases, CaseSeverity.LOW), + disabled: isActionDisabled || shouldDisableSeverity(selectedCases, CaseSeverity.LOW), + 'data-test-subj': 'cases-bulk-action-severity-low', + key: 'cases-bulk-action-severity-low', + }, + { + name: severities[CaseSeverity.MEDIUM].label, + icon: getSeverityIcon(CaseSeverity.MEDIUM), + onClick: () => handleUpdateCaseSeverity(selectedCases, CaseSeverity.MEDIUM), + disabled: isActionDisabled || shouldDisableSeverity(selectedCases, CaseSeverity.MEDIUM), + 'data-test-subj': 'cases-bulk-action-severity-medium', + key: 'cases-bulk-action-severity-medium', + }, + { + name: severities[CaseSeverity.HIGH].label, + icon: getSeverityIcon(CaseSeverity.HIGH), + onClick: () => handleUpdateCaseSeverity(selectedCases, CaseSeverity.HIGH), + disabled: isActionDisabled || shouldDisableSeverity(selectedCases, CaseSeverity.HIGH), + 'data-test-subj': 'cases-bulk-action-severity-high', + key: 'cases-bulk-action-severity-high', + }, + { + name: severities[CaseSeverity.CRITICAL].label, + icon: getSeverityIcon(CaseSeverity.CRITICAL), + onClick: () => handleUpdateCaseSeverity(selectedCases, CaseSeverity.CRITICAL), + disabled: isActionDisabled || shouldDisableSeverity(selectedCases, CaseSeverity.CRITICAL), + 'data-test-subj': 'cases-bulk-action-severity-critical', + key: 'cases-bulk-action-severity-critical', + }, + ]; + }; + + return { getActions, canUpdateSeverity }; +}; + +export type UseSeverityAction = ReturnType; diff --git a/x-pack/plugins/cases/public/components/actions/status/translations.ts b/x-pack/plugins/cases/public/components/actions/status/translations.ts new file mode 100644 index 0000000000000..67fa3544906da --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/status/translations.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +export { MARK_CASE_IN_PROGRESS, OPEN_CASE, CLOSE_CASE } from '../../../common/translations'; + +export const CLOSED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.actions.closedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const REOPENED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.actions.reopenedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Opened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const MARK_IN_PROGRESS_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.actions.markInProgressCases', { + values: { caseTitle, totalCases }, + defaultMessage: + 'Marked {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}} as in progress', + }); + +export const BULK_ACTION_STATUS_CLOSE = i18n.translate('xpack.cases.actions.status.close', { + defaultMessage: 'Close selected', +}); + +export const BULK_ACTION_STATUS_OPEN = i18n.translate('xpack.cases.actions.status.open', { + defaultMessage: 'Open selected', +}); + +export const BULK_ACTION_STATUS_IN_PROGRESS = i18n.translate( + 'xpack.cases.actions.status.inProgress', + { + defaultMessage: 'Mark in progress', + } +); diff --git a/x-pack/plugins/cases/public/components/actions/status/use_status_action.test.tsx b/x-pack/plugins/cases/public/components/actions/status/use_status_action.test.tsx new file mode 100644 index 0000000000000..a0f16dbaa1760 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/status/use_status_action.test.tsx @@ -0,0 +1,198 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useStatusAction } from './use_status_action'; + +import * as api from '../../../containers/api'; +import { basicCase } from '../../../containers/mock'; +import { CaseStatuses } from '../../../../common'; + +jest.mock('../../../containers/api'); + +describe('useStatusAction', () => { + let appMockRender: AppMockRenderer; + const onAction = jest.fn(); + const onActionSuccess = jest.fn(); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('renders an action', async () => { + const { result } = renderHook( + () => + useStatusAction({ + onAction, + onActionSuccess, + isDisabled: false, + }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current.getActions([basicCase])).toMatchInlineSnapshot(` + Array [ + Object { + "data-test-subj": "cases-bulk-action-status-open", + "disabled": true, + "icon": "empty", + "key": "cases-bulk-action-status-open", + "name": "Open", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-status-in-progress", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-status-in-progress", + "name": "In progress", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-status-closed", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-status-action", + "name": "Closed", + "onClick": [Function], + }, + ] + `); + }); + + it('update the status cases', async () => { + const updateSpy = jest.spyOn(api, 'updateCases'); + + const { result, waitFor } = renderHook( + () => useStatusAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + + for (const [index, status] of [ + CaseStatuses.open, + CaseStatuses['in-progress'], + CaseStatuses.closed, + ].entries()) { + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(onAction).toHaveBeenCalled(); + expect(onActionSuccess).toHaveBeenCalled(); + expect(updateSpy).toHaveBeenCalledWith( + [{ status, id: basicCase.id, version: basicCase.version }], + expect.anything() + ); + }); + } + }); + + const singleCaseTests = [ + [CaseStatuses.open, 0, 'Opened "Another horrible breach!!"'], + [CaseStatuses['in-progress'], 1, 'Marked "Another horrible breach!!" as in progress'], + [CaseStatuses.closed, 2, 'Closed "Another horrible breach!!"'], + ]; + + it.each(singleCaseTests)( + 'shows the success toaster correctly when updating the status of the case: %s', + async (_, index, expectedMessage) => { + const { result, waitFor } = renderHook( + () => useStatusAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + expectedMessage + ); + }); + } + ); + + const multipleCasesTests: Array<[CaseStatuses, number, string]> = [ + [CaseStatuses.open, 0, 'Opened 2 cases'], + [CaseStatuses['in-progress'], 1, 'Marked 2 cases as in progress'], + [CaseStatuses.closed, 2, 'Closed 2 cases'], + ]; + + it.each(multipleCasesTests)( + 'shows the success toaster correctly when updating the status of the case: %s', + async (_, index, expectedMessage) => { + const { result, waitFor } = renderHook( + () => useStatusAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase, basicCase]); + + act(() => { + // @ts-expect-error: onClick expects a MouseEvent argument + actions[index]!.onClick(); + }); + + await waitFor(() => { + expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith( + expectedMessage + ); + }); + } + ); + + const disabledTests: Array<[CaseStatuses, number]> = [ + [CaseStatuses.open, 0], + [CaseStatuses['in-progress'], 1], + [CaseStatuses.closed, 2], + ]; + + it.each(disabledTests)('disables the status button correctly: %s', async (status, index) => { + const { result } = renderHook( + () => useStatusAction({ onAction, onActionSuccess, isDisabled: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([{ ...basicCase, status }]); + expect(actions[index].disabled).toBe(true); + }); + + it.each(disabledTests)( + 'disables the status button correctly if isDisabled=true: %s', + async (status, index) => { + const { result } = renderHook( + () => useStatusAction({ onAction, onActionSuccess, isDisabled: true }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const actions = result.current.getActions([basicCase]); + expect(actions[index].disabled).toBe(true); + } + ); +}); diff --git a/x-pack/plugins/cases/public/components/actions/status/use_status_action.tsx b/x-pack/plugins/cases/public/components/actions/status/use_status_action.tsx new file mode 100644 index 0000000000000..8f1100aaab90d --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/status/use_status_action.tsx @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; +import { useUpdateCases } from '../../../containers/use_bulk_update_case'; +import { Case, CaseStatuses } from '../../../../common'; + +import * as i18n from './translations'; +import { UseActionProps } from '../types'; +import { statuses } from '../../status'; +import { useCasesContext } from '../../cases_context/use_cases_context'; + +const getStatusToasterMessage = (status: CaseStatuses, cases: Case[]): string => { + const totalCases = cases.length; + const caseTitle = totalCases === 1 ? cases[0].title : ''; + + if (status === CaseStatuses.open) { + return i18n.REOPENED_CASES({ totalCases, caseTitle }); + } else if (status === CaseStatuses['in-progress']) { + return i18n.MARK_IN_PROGRESS_CASES({ totalCases, caseTitle }); + } else if (status === CaseStatuses.closed) { + return i18n.CLOSED_CASES({ totalCases, caseTitle }); + } + + return ''; +}; + +interface UseStatusActionProps extends UseActionProps { + selectedStatus?: CaseStatuses; +} + +const shouldDisableStatus = (cases: Case[], status: CaseStatuses) => + cases.every((theCase) => theCase.status === status); + +export const useStatusAction = ({ + onAction, + onActionSuccess, + isDisabled, + selectedStatus, +}: UseStatusActionProps) => { + const { mutate: updateCases } = useUpdateCases(); + const { permissions } = useCasesContext(); + const canUpdateStatus = permissions.update; + const isActionDisabled = isDisabled || !canUpdateStatus; + + const handleUpdateCaseStatus = useCallback( + (selectedCases: Case[], status: CaseStatuses) => { + onAction(); + const casesToUpdate = selectedCases.map((theCase) => ({ + status, + id: theCase.id, + version: theCase.version, + })); + + updateCases( + { + cases: casesToUpdate, + successToasterTitle: getStatusToasterMessage(status, selectedCases), + }, + { onSuccess: onActionSuccess } + ); + }, + [onAction, updateCases, onActionSuccess] + ); + + const getStatusIcon = (status: CaseStatuses): string => + selectedStatus && selectedStatus === status ? 'check' : 'empty'; + + const getActions = (selectedCases: Case[]): EuiContextMenuPanelItemDescriptor[] => { + return [ + { + name: statuses[CaseStatuses.open].label, + icon: getStatusIcon(CaseStatuses.open), + onClick: () => handleUpdateCaseStatus(selectedCases, CaseStatuses.open), + disabled: isActionDisabled || shouldDisableStatus(selectedCases, CaseStatuses.open), + 'data-test-subj': 'cases-bulk-action-status-open', + key: 'cases-bulk-action-status-open', + }, + { + name: statuses[CaseStatuses['in-progress']].label, + icon: getStatusIcon(CaseStatuses['in-progress']), + onClick: () => handleUpdateCaseStatus(selectedCases, CaseStatuses['in-progress']), + disabled: + isActionDisabled || shouldDisableStatus(selectedCases, CaseStatuses['in-progress']), + 'data-test-subj': 'cases-bulk-action-status-in-progress', + key: 'cases-bulk-action-status-in-progress', + }, + { + name: statuses[CaseStatuses.closed].label, + icon: getStatusIcon(CaseStatuses.closed), + onClick: () => handleUpdateCaseStatus(selectedCases, CaseStatuses.closed), + disabled: isActionDisabled || shouldDisableStatus(selectedCases, CaseStatuses.closed), + 'data-test-subj': 'cases-bulk-action-status-closed', + key: 'cases-bulk-status-action', + }, + ]; + }; + + return { getActions, canUpdateStatus }; +}; + +export type UseStatusAction = ReturnType; diff --git a/x-pack/plugins/cases/public/components/actions/types.ts b/x-pack/plugins/cases/public/components/actions/types.ts new file mode 100644 index 0000000000000..20a566a20c670 --- /dev/null +++ b/x-pack/plugins/cases/public/components/actions/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface UseActionProps { + onAction: () => void; + onActionSuccess: () => void; + isDisabled: boolean; +} diff --git a/x-pack/plugins/cases/public/components/all_cases/actions.tsx b/x-pack/plugins/cases/public/components/all_cases/actions.tsx deleted file mode 100644 index f978cd1b3f205..0000000000000 --- a/x-pack/plugins/cases/public/components/all_cases/actions.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; - -import { Case } from '../../containers/types'; -import * as i18n from './translations'; - -interface GetActions { - deleteCaseOnClick: (deleteCase: Case) => void; -} - -export const getActions = ({ - deleteCaseOnClick, -}: GetActions): Array> => [ - { - description: i18n.DELETE_CASE(), - icon: 'trash', - color: 'danger', - name: i18n.DELETE_CASE(), - onClick: deleteCaseOnClick, - type: 'icon', - 'data-test-subj': 'action-delete', - }, -]; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 1cc3a5f0263e5..63ef59d695d80 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -18,6 +18,7 @@ import { AppMockRenderer, createAppMockRenderer, noDeleteCasesPermissions, + readCasesPermissions, TestProviders, } from '../../common/mock'; import { useGetCasesMockState, connectorsMock } from '../../containers/mock'; @@ -28,7 +29,7 @@ import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; import { useKibana } from '../../common/lib/kibana'; import { AllCasesList } from './all_cases_list'; -import { CasesColumns, GetCasesColumn, useCasesColumns } from './columns'; +import { GetCasesColumn, useCasesColumns, UseCasesColumnsReturnValue } from './use_cases_columns'; import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; import { registerConnectorsToMockActionRegistry } from '../../common/mock/register_connectors'; import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; @@ -97,10 +98,6 @@ describe('AllCasesListGeneric', () => { }; const defaultColumnArgs = { - caseDetailsNavigation: { - href: jest.fn(), - onClick: jest.fn(), - }, filterStatus: CaseStatuses.open, handleIsLoading: jest.fn(), isLoadingCases: [], @@ -161,10 +158,6 @@ describe('AllCasesListGeneric', () => { .prop('value') ).toBe(useGetCasesMockState.data.cases[0].createdAt); - expect( - wrapper.find(`[data-test-subj="case-table-column-severity"]`).first().text().toLowerCase() - ).toBe(useGetCasesMockState.data.cases[0].severity); - expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( 'Showing 10 cases' ); @@ -236,7 +229,7 @@ describe('AllCasesListGeneric', () => { expect(column.find('span').text()).toEqual(emptyTag); }; - const { result } = renderHook( + const { result } = renderHook( () => useCasesColumns(defaultColumnArgs), { wrapper: ({ children }) => {children}, @@ -244,26 +237,12 @@ describe('AllCasesListGeneric', () => { ); await waitFor(() => { - result.current.map( - (i, key) => - i.name != null && - !Object.prototype.hasOwnProperty.call(i, 'actions') && - checkIt(`${i.name}`, key) + result.current.columns.map( + (i, key) => i.name != null && i.name !== 'Actions' && checkIt(`${i.name}`, key) ); }); }); - it('should render delete actions for case', async () => { - const wrapper = mount( - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="action-delete"]').first().props().disabled).toBeFalsy(); - }); - }); - it('should tableHeaderSortButton AllCasesList', async () => { const wrapper = mount( @@ -285,163 +264,25 @@ describe('AllCasesListGeneric', () => { }); }); - it('Updates status when status context menu is updated', async () => { - const wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="case-view-status-dropdown"] button`).first().simulate('click'); - wrapper - .find(`[data-test-subj="case-view-status-dropdown-closed"] button`) - .first() - .simulate('click'); + it('renders the status column', async () => { + const res = appMockRenderer.render(); - await waitFor(() => { - const firstCase = useGetCasesMockState.data.cases[0]; - expect(updateCaseProperty).toHaveBeenCalledWith({ - caseData: firstCase, - updateKey: 'status', - updateValue: CaseStatuses.closed, - onSuccess: expect.anything(), - }); - }); + expect(res.getByTestId('tableHeaderCell_Status_6')).toBeInTheDocument(); }); - it('should render the case stats', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('[data-test-subj="cases-count-stats"]')).toBeTruthy(); - }); - - it('Bulk delete', async () => { - const deleteCasesSpy = jest.spyOn(api, 'deleteCases'); - const result = appMockRenderer.render(); - - act(() => { - userEvent.click(result.getByTestId('checkboxSelectAll')); - }); - - act(() => { - userEvent.click(result.getByText('Bulk actions')); - }); - - await waitForEuiPopoverOpen(); - - act(() => { - userEvent.click(result.getByTestId('cases-bulk-delete-button'), undefined, { - skipPointerEventsCheck: true, - }); - }); - - await waitFor(() => { - expect(result.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); - }); - - act(() => { - userEvent.click(result.getByTestId('confirmModalConfirmButton')); - }); + it('renders the severity column', async () => { + const res = appMockRenderer.render(); - await waitFor(() => { - expect(deleteCasesSpy).toHaveBeenCalledWith( - [ - 'basic-case-id', - '1', - '2', - '3', - '4', - 'case-with-alerts-id', - 'case-with-alerts-syncoff-id', - 'case-with-registered-attachment', - ], - expect.anything() - ); - }); + expect(res.getByTestId('tableHeaderCell_Severity_7')).toBeInTheDocument(); }); - it('Renders only bulk delete on status all', async () => { + it('should render the case stats', () => { const wrapper = mount( ); - - wrapper.find('[data-test-subj*="checkboxSelectRow-"]').first().simulate('click'); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - - await waitFor(() => { - expect(wrapper.find('[data-test-subj="cases-bulk-open-button"]').exists()).toEqual(false); - expect(wrapper.find('[data-test-subj="cases-bulk-in-progress-button"]').exists()).toEqual( - false - ); - expect(wrapper.find('[data-test-subj="cases-bulk-close-button"]').exists()).toEqual(false); - expect( - wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().props().disabled - ).toEqual(true); - }); - }); - - it('Bulk close status update', async () => { - const updateCasesSpy = jest.spyOn(api, 'updateCases'); - - const result = appMockRenderer.render(); - const theCase = useGetCasesMockState.data.cases[0]; - userEvent.click(result.getByTestId('case-status-filter')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('case-status-filter-in-progress')); - userEvent.click(result.getByTestId(`checkboxSelectRow-${theCase.id}`)); - userEvent.click(result.getByText('Bulk actions')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('cases-bulk-close-button')); - await waitFor(() => {}); - - expect(updateCasesSpy).toBeCalledWith( - [{ id: theCase.id, version: theCase.version, status: CaseStatuses.closed }], - expect.anything() - ); - }); - - it('Bulk open status update', async () => { - const updateCasesSpy = jest.spyOn(api, 'updateCases'); - - const result = appMockRenderer.render(); - const theCase = useGetCasesMockState.data.cases[0]; - userEvent.click(result.getByTestId('case-status-filter')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('case-status-filter-closed')); - userEvent.click(result.getByTestId(`checkboxSelectRow-${theCase.id}`)); - userEvent.click(result.getByText('Bulk actions')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('cases-bulk-open-button')); - await waitFor(() => {}); - - expect(updateCasesSpy).toBeCalledWith( - [{ id: theCase.id, version: theCase.version, status: CaseStatuses.open }], - expect.anything() - ); - }); - - it('Bulk in-progress status update', async () => { - const updateCasesSpy = jest.spyOn(api, 'updateCases'); - - const result = appMockRenderer.render(); - const theCase = useGetCasesMockState.data.cases[0]; - userEvent.click(result.getByTestId('case-status-filter')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('case-status-filter-closed')); - userEvent.click(result.getByTestId(`checkboxSelectRow-${theCase.id}`)); - userEvent.click(result.getByText('Bulk actions')); - await waitForEuiPopoverOpen(); - userEvent.click(result.getByTestId('cases-bulk-in-progress-button')); - await waitFor(() => {}); - - expect(updateCasesSpy).toBeCalledWith( - [{ id: theCase.id, version: theCase.version, status: CaseStatuses['in-progress'] }], - expect.anything() - ); + expect(wrapper.find('[data-test-subj="cases-count-stats"]')).toBeTruthy(); }); it('should not render table utility bar when isSelectorView=true', async () => { @@ -522,60 +363,21 @@ describe('AllCasesListGeneric', () => { }); it('should call onRowClick when clicking a case with modal=true', async () => { + const theCase = defaultGetCases.data.cases[0]; + const wrapper = mount( ); - wrapper.find('[data-test-subj="cases-table-row-select-1"]').first().simulate('click'); + wrapper + .find(`[data-test-subj="cases-table-row-select-${theCase.id}"]`) + .first() + .simulate('click'); + await waitFor(() => { - expect(onRowClick).toHaveBeenCalledWith({ - assignees: [{ uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0' }], - closedAt: null, - closedBy: null, - comments: [], - connector: { fields: null, id: '123', name: 'My Connector', type: '.jira' }, - createdAt: '2020-02-19T23:06:33.798Z', - createdBy: { - email: 'leslie.knope@elastic.co', - fullName: 'Leslie Knope', - username: 'lknope', - }, - description: 'Security banana Issue', - severity: CaseSeverity.LOW, - duration: null, - externalService: { - connectorId: '123', - connectorName: 'connector name', - externalId: 'external_id', - externalTitle: 'external title', - externalUrl: 'basicPush.com', - pushedAt: '2020-02-20T15:02:57.995Z', - pushedBy: { - email: 'leslie.knope@elastic.co', - fullName: 'Leslie Knope', - username: 'lknope', - }, - }, - id: '1', - owner: SECURITY_SOLUTION_OWNER, - status: 'open', - tags: ['coke', 'pepsi'], - title: 'Another horrible breach!!', - totalAlerts: 0, - totalComment: 0, - updatedAt: '2020-02-20T15:02:57.995Z', - updatedBy: { - email: 'leslie.knope@elastic.co', - fullName: 'Leslie Knope', - username: 'lknope', - }, - version: 'WzQ3LDFd', - settings: { - syncAlerts: true, - }, - }); + expect(onRowClick).toHaveBeenCalledWith(theCase); }); }); @@ -591,7 +393,7 @@ describe('AllCasesListGeneric', () => { }); }); - it('should change the status to closed', async () => { + it('should filter by status: closed', async () => { const result = appMockRenderer.render(); userEvent.click(result.getByTestId('case-status-filter')); await waitForEuiPopoverOpen(); @@ -610,7 +412,7 @@ describe('AllCasesListGeneric', () => { }); }); - it('should change the status to in-progress', async () => { + it('should filter by status: in-progress', async () => { const result = appMockRenderer.render(); userEvent.click(result.getByTestId('case-status-filter')); await waitForEuiPopoverOpen(); @@ -629,7 +431,7 @@ describe('AllCasesListGeneric', () => { }); }); - it('should change the status to open', async () => { + it('should filter by status: open', async () => { const result = appMockRenderer.render(); userEvent.click(result.getByTestId('case-status-filter')); await waitForEuiPopoverOpen(); @@ -668,34 +470,7 @@ describe('AllCasesListGeneric', () => { }); }); - it('should not render status when isSelectorView=true', async () => { - const wrapper = mount( - - - - ); - - const { result } = renderHook( - () => - useCasesColumns({ - ...defaultColumnArgs, - isSelectorView: true, - }), - { - wrapper: ({ children }) => {children}, - } - ); - - expect(result.current.find((i) => i.name === 'Status')).toBeFalsy(); - - await waitFor(() => { - expect(wrapper.find('[data-test-subj="cases-table"]').exists()).toBeTruthy(); - }); - - expect(wrapper.find('[data-test-subj="case-view-status-dropdown"]').exists()).toBeFalsy(); - }); - - it.skip('renders the first available status when hiddenStatus is given', async () => { + it('renders the first available status when hiddenStatus is given', async () => { const wrapper = mount( @@ -926,6 +701,338 @@ describe('AllCasesListGeneric', () => { }); }); }); + + describe('Actions', () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + const deleteCasesSpy = jest.spyOn(api, 'deleteCases'); + + describe('Bulk actions', () => { + it('Renders bulk action', async () => { + const result = appMockRenderer.render(); + + act(() => { + userEvent.click(result.getByTestId('checkboxSelectAll')); + }); + + act(() => { + userEvent.click(result.getByText('Bulk actions')); + }); + + await waitForEuiPopoverOpen(); + + expect(result.getByTestId('case-bulk-action-status')).toBeInTheDocument(); + expect(result.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + }); + + it.each([[CaseStatuses.open], [CaseStatuses['in-progress']], [CaseStatuses.closed]])( + 'Bulk update status: %s', + async (status) => { + const result = appMockRenderer.render(); + + act(() => { + userEvent.click(result.getByTestId('checkboxSelectAll')); + }); + + act(() => { + userEvent.click(result.getByText('Bulk actions')); + }); + + await waitForEuiPopoverOpen(); + + act(() => { + userEvent.click(result.getByTestId('case-bulk-action-status')); + }); + + await waitFor(() => { + expect(result.getByTestId(`cases-bulk-action-status-${status}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(result.getByTestId(`cases-bulk-action-status-${status}`)); + }); + + await waitForComponentToUpdate(); + + expect(updateCasesSpy).toBeCalledWith( + useGetCasesMockState.data.cases.map(({ id, version }) => ({ + id, + version, + status, + })), + expect.anything() + ); + } + ); + + it.each([ + [CaseSeverity.LOW], + [CaseSeverity.MEDIUM], + [CaseSeverity.HIGH], + [CaseSeverity.CRITICAL], + ])('Bulk update severity: %s', async (severity) => { + const result = appMockRenderer.render(); + + act(() => { + userEvent.click(result.getByTestId('checkboxSelectAll')); + }); + + act(() => { + userEvent.click(result.getByText('Bulk actions')); + }); + + await waitForEuiPopoverOpen(); + + act(() => { + userEvent.click(result.getByTestId('case-bulk-action-severity')); + }); + + await waitFor(() => { + expect(result.getByTestId(`cases-bulk-action-severity-${severity}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(result.getByTestId(`cases-bulk-action-severity-${severity}`)); + }); + + await waitForComponentToUpdate(); + + expect(updateCasesSpy).toBeCalledWith( + useGetCasesMockState.data.cases.map(({ id, version }) => ({ + id, + version, + severity, + })), + expect.anything() + ); + }); + + it('Bulk delete', async () => { + const result = appMockRenderer.render(); + + act(() => { + userEvent.click(result.getByTestId('checkboxSelectAll')); + }); + + act(() => { + userEvent.click(result.getByText('Bulk actions')); + }); + + await waitForEuiPopoverOpen(); + + act(() => { + userEvent.click(result.getByTestId('cases-bulk-action-delete'), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(result.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(result.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(deleteCasesSpy).toHaveBeenCalledWith( + [ + 'basic-case-id', + '1', + '2', + '3', + '4', + 'case-with-alerts-id', + 'case-with-alerts-syncoff-id', + 'case-with-registered-attachment', + ], + expect.anything() + ); + }); + }); + + it('should disable the checkboxes when the user has read only permissions', async () => { + appMockRenderer = createAppMockRenderer({ permissions: readCasesPermissions() }); + const res = appMockRenderer.render(); + + expect(res.getByTestId('checkboxSelectAll')).toBeDisabled(); + + await waitFor(() => { + for (const theCase of defaultGetCases.data.cases) { + expect(res.getByTestId(`checkboxSelectRow-${theCase.id}`)).toBeDisabled(); + } + }); + }); + }); + + describe('Row actions', () => { + const statusTests = [ + [CaseStatuses.open], + [CaseStatuses['in-progress']], + [CaseStatuses.closed], + ]; + + const severityTests = [ + [CaseSeverity.LOW], + [CaseSeverity.MEDIUM], + [CaseSeverity.HIGH], + [CaseSeverity.CRITICAL], + ]; + + it('should render row actions', async () => { + const res = appMockRenderer.render(); + + await waitFor(() => { + for (const theCase of defaultGetCases.data.cases) { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); + } + }); + }); + + it.each(statusTests)('update the status of a case: %s', async (status) => { + const res = appMockRenderer.render(); + const openCase = useGetCasesMockState.data.cases[0]; + const inProgressCase = useGetCasesMockState.data.cases[1]; + const theCase = status === CaseStatuses.open ? inProgressCase : openCase; + + await waitFor(() => { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${theCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-status-panel-${theCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-status-panel-${theCase.id}`), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId(`cases-bulk-action-status-${status}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`cases-bulk-action-status-${status}`)); + }); + + await waitFor(() => { + expect(updateCasesSpy).toHaveBeenCalledWith( + [{ id: theCase.id, status, version: theCase.version }], + expect.anything() + ); + }); + }); + + it.each(severityTests)('update the status of a case: %s', async (severity) => { + const res = appMockRenderer.render(); + const lowCase = useGetCasesMockState.data.cases[0]; + const mediumCase = useGetCasesMockState.data.cases[1]; + const theCase = severity === CaseSeverity.LOW ? mediumCase : lowCase; + + await waitFor(() => { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${theCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-severity-panel-${theCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-severity-panel-${theCase.id}`), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId(`cases-bulk-action-severity-${severity}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`cases-bulk-action-severity-${severity}`)); + }); + + await waitFor(() => { + expect(updateCasesSpy).toHaveBeenCalledWith( + [{ id: theCase.id, severity, version: theCase.version }], + expect.anything() + ); + }); + }); + + it('should delete a case', async () => { + const res = appMockRenderer.render(); + const theCase = defaultGetCases.data.cases[0]; + + await waitFor(() => { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${theCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-delete'), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(deleteCasesSpy).toHaveBeenCalledWith(['basic-case-id'], expect.anything()); + }); + }); + + it('should disable row actions when bulk selecting all cases', async () => { + const res = appMockRenderer.render(); + + act(() => { + userEvent.click(res.getByTestId('checkboxSelectAll')); + }); + + await waitFor(() => { + for (const theCase of defaultGetCases.data.cases) { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeDisabled(); + } + }); + }); + + it('should disable row actions when selecting a case', async () => { + const res = appMockRenderer.render(); + const caseToSelect = defaultGetCases.data.cases[0]; + + act(() => { + userEvent.click(res.getByTestId(`checkboxSelectRow-${caseToSelect.id}`)); + }); + + await waitFor(() => { + for (const theCase of defaultGetCases.data.cases) { + expect(res.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeDisabled(); + } + }); + }); + }); + }); }); describe('Assignees', () => { diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index c7b2d4895f94a..0d2cff95c4919 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -21,7 +21,7 @@ import { import { CaseStatuses, caseStatuses } from '../../../common/api'; import { useAvailableCasesOwners } from '../app/use_available_owners'; -import { useCasesColumns } from './columns'; +import { useCasesColumns } from './use_cases_columns'; import { CasesTableFilters } from './table_filters'; import { EuiBasicTableOnChange } from './types'; @@ -37,7 +37,7 @@ import { } from '../../containers/use_get_cases'; import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles'; import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile'; -import { getAllPermissionsExceptFrom } from '../../utils/permissions'; +import { getAllPermissionsExceptFrom, isReadOnlyPermissions } from '../../utils/permissions'; import { useIsLoadingCases } from './use_is_loading_cases'; const ProgressLoader = styled(EuiProgress)` @@ -196,13 +196,7 @@ export const AllCasesList = React.memo( [deselectCases, hasOwner, availableSolutions, owner] ); - /** - * At the time of changing this from all to delete the only bulk action we have is to delete. When we add more - * actions we'll need to revisit this to allow more granular checks around the bulk actions. - */ - const showActions = permissions.delete && !isSelectorView; - - const columns = useCasesColumns({ + const { columns } = useCasesColumns({ filterStatus: filterOptions.status ?? StatusAll, userProfiles: userProfiles ?? new Map(), currentUserProfile, @@ -210,6 +204,7 @@ export const AllCasesList = React.memo( connectors, onRowClick, showSolutionColumn: !hasOwner && availableSolutions.length > 1, + disableActions: selectedCases.length > 0, }); const pagination = useMemo( @@ -226,8 +221,9 @@ export const AllCasesList = React.memo( () => ({ onSelectionChange: setSelectedCases, initialSelected: selectedCases, + selectable: () => !isReadOnlyPermissions(permissions), }), - [selectedCases, setSelectedCases] + [permissions, selectedCases] ); const isDataEmpty = useMemo(() => data.total === 0, [data]); @@ -272,7 +268,6 @@ export const AllCasesList = React.memo( ( pagination={pagination} selectedCases={selectedCases} selection={euiBasicTableSelectionProps} - showActions={showActions} sorting={sorting} tableRef={tableRef} tableRowProps={tableRowProps} diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.test.tsx deleted file mode 100644 index 7dec0d7937913..0000000000000 --- a/x-pack/plugins/cases/public/components/all_cases/columns.test.tsx +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import '../../common/mock/match_media'; -import { ExternalServiceColumn } from './columns'; -import { useGetCasesMockState } from '../../containers/mock'; -import { connectors } from '../configure_cases/__mock__'; -import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; - -describe('ExternalServiceColumn ', () => { - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRender = createAppMockRenderer(); - }); - - it('Not pushed render', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="case-table-column-external-notPushed"]`).last().exists() - ).toBeTruthy(); - }); - - it('Up to date', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="case-table-column-external-upToDate"]`).last().exists() - ).toBeTruthy(); - }); - - it('Needs update', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="case-table-column-external-requiresUpdate"]`).last().exists() - ).toBeTruthy(); - }); - - it('it does not throw when accessing the icon if the connector type is not registered', () => { - // If the component throws the test will fail - expect(() => - mount( - - - - ) - ).not.toThrowError(); - }); - - it('shows the connectors icon if the user has read access to actions', async () => { - const result = appMockRender.render( - - ); - - expect(result.getByTestId('cases-table-connector-icon')).toBeInTheDocument(); - }); - - it('hides the connectors icon if the user does not have read access to actions', async () => { - appMockRender.coreStart.application.capabilities = { - ...appMockRender.coreStart.application.capabilities, - actions: { save: false, show: false }, - }; - - const result = appMockRender.render( - - ); - - expect(result.queryByTestId('cases-table-connector-icon')).toBe(null); - }); -}); diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx index 98c89215aac21..e26831266d65f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx @@ -6,13 +6,14 @@ */ import React from 'react'; -import { mount } from 'enzyme'; import { AllCasesSelectorModal } from '.'; -import { TestProviders } from '../../../common/mock'; -import { AllCasesList } from '../all_cases_list'; +import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock'; +import userEvent from '@testing-library/user-event'; +import { act, waitFor } from '@testing-library/react'; -jest.mock('../all_cases_list', () => ({ AllCasesList: jest.fn().mockReturnValue(<>) })); +jest.mock('../../../containers/api'); +jest.mock('../../../containers/user_profiles/api'); const onRowClick = jest.fn(); const defaultProps = { @@ -20,48 +21,73 @@ const defaultProps = { }; describe('AllCasesSelectorModal', () => { + let appMockRenderer: AppMockRenderer; + beforeEach(() => { jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); }); it('renders', () => { - const wrapper = mount( - - - - ); + const res = appMockRenderer.render(); + + expect(res.getByTestId('all-cases-modal')).toBeInTheDocument(); + }); + + it('Closing modal when pressing the x icon', () => { + const res = appMockRenderer.render(); + + act(() => { + userEvent.click(res.getByLabelText('Closes this modal window')); + }); + + expect(res.queryByTestId('all-cases-modal')).toBeFalsy(); + }); + + it('Closing modal when pressing the cancel button', () => { + const res = appMockRenderer.render(); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); + act(() => { + userEvent.click(res.getByTestId('all-cases-modal-cancel-button')); + }); + + expect(res.queryByTestId('all-cases-modal')).toBeFalsy(); + }); + + it('should not show bulk actions and row actions on the modal', async () => { + const res = appMockRenderer.render(); + await waitFor(() => { + expect(res.getByTestId('cases-table')).toBeInTheDocument(); + }); + + expect(res.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); + expect(res.queryByText('Actions')).toBeFalsy(); }); - it('Closing modal calls onCloseCaseModal', () => { - const wrapper = mount( - - - - ); + it('should show the select button', async () => { + const res = appMockRenderer.render(); + await waitFor(() => { + expect(res.getByTestId('cases-table')).toBeInTheDocument(); + }); - wrapper.find('.euiModal__closeIcon').first().simulate('click'); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); + expect(res.getAllByTestId(/cases-table-row-select/).length).toBeGreaterThan(0); }); - it('pass the correct props to getAllCases method', () => { - const fullProps = { - ...defaultProps, - hiddenStatuses: [], - }; - - mount( - - - - ); - - expect((AllCasesList as unknown as jest.Mock).mock.calls[0][0]).toEqual( - expect.objectContaining({ - hiddenStatuses: fullProps.hiddenStatuses, - isSelectorView: true, - }) - ); + it('should hide the metrics', async () => { + const res = appMockRenderer.render(); + await waitFor(() => { + expect(res.getByTestId('cases-table')).toBeInTheDocument(); + }); + + expect(res.queryByTestId('cases-metrics-stats')).toBeFalsy(); + }); + + it('should show the create case button', async () => { + const res = appMockRenderer.render(); + await waitFor(() => { + expect(res.getByTestId('cases-table')).toBeInTheDocument(); + }); + + expect(res.getByTestId('cases-table-add-case-filter-bar')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx index fccdf76abd2c4..f5007fcd5a092 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx @@ -68,7 +68,11 @@ export const AllCasesSelectorModal = React.memo( /> - + {i18n.CANCEL} 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 208c26e47648c..1f7382351e802 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table.tsx @@ -19,7 +19,7 @@ import styled from 'styled-components'; import { CasesTableUtilityBar } from './utility_bar'; import { LinkButton } from '../links'; -import { Cases, Case, FilterOptions } from '../../../common/ui/types'; +import { Cases, Case } from '../../../common/ui/types'; import * as i18n from './translations'; import { useCreateCaseNavigation } from '../../common/navigation'; import { useCasesContext } from '../cases_context/use_cases_context'; @@ -27,7 +27,6 @@ import { useCasesContext } from '../cases_context/use_cases_context'; interface CasesTableProps { columns: EuiBasicTableProps['columns']; data: Cases; - filterOptions: FilterOptions; goToCreateCase?: () => void; isCasesLoading: boolean; isCommentUpdating: boolean; @@ -37,7 +36,6 @@ interface CasesTableProps { pagination: Pagination; selectedCases: Case[]; selection: EuiTableSelectionType; - showActions: boolean; sorting: EuiBasicTableProps['sorting']; tableRef: MutableRefObject; tableRowProps: EuiBasicTableProps['rowProps']; @@ -51,7 +49,6 @@ const Div = styled.div` export const CasesTable: FunctionComponent = ({ columns, data, - filterOptions, goToCreateCase, isCasesLoading, isCommentUpdating, @@ -61,7 +58,6 @@ export const CasesTable: FunctionComponent = ({ pagination, selectedCases, selection, - showActions, sorting, tableRef, tableRowProps, @@ -86,11 +82,10 @@ export const CasesTable: FunctionComponent = ({
) : ( -
+ <> @@ -98,7 +93,7 @@ export const CasesTable: FunctionComponent = ({ className={classnames({ isSelectorView })} columns={columns} data-test-subj="cases-table" - isSelectable={showActions} + isSelectable={!isSelectorView} itemId="id" items={data.cases} loading={isCommentUpdating} @@ -128,10 +123,11 @@ export const CasesTable: FunctionComponent = ({ pagination={pagination} ref={tableRef} rowProps={tableRowProps} - selection={showActions ? selection : undefined} + selection={!isSelectorView ? selection : undefined} sorting={sorting} + hasActions={false} /> -
+ ); }; CasesTable.displayName = 'CasesTable'; diff --git a/x-pack/plugins/cases/public/components/all_cases/translations.ts b/x-pack/plugins/cases/public/components/all_cases/translations.ts index 96a683aee5077..332c0d493101b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -130,40 +130,3 @@ export const TOTAL_ASSIGNEES_FILTERED = (total: number) => defaultMessage: '{total, plural, one {# assignee} other {# assignees}} filtered', values: { total }, }); - -export const CLOSED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.closedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const REOPENED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.reopenedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Opened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const MARK_IN_PROGRESS_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.markInProgressCases', { - values: { caseTitle, totalCases }, - defaultMessage: - 'Marked {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}} as in progress', - }); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx new file mode 100644 index 0000000000000..bf2994a0cad58 --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx @@ -0,0 +1,318 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { waitFor } from '@testing-library/dom'; +import { act, renderHook } from '@testing-library/react-hooks'; + +import { useActions } from './use_actions'; +import { basicCase } from '../../containers/mock'; +import * as api from '../../containers/api'; +import { + AppMockRenderer, + createAppMockRenderer, + noDeleteCasesPermissions, + onlyDeleteCasesPermission, + allCasesPermissions, + readCasesPermissions, +} from '../../common/mock'; + +jest.mock('../../containers/api'); + +describe('useActions', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('renders column actions', async () => { + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "actions": Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + } + `); + }); + + it('renders the popover', async () => { + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + expect(res.getByTestId(`case-action-popover-${basicCase.id}`)).toBeInTheDocument(); + }); + + it('open the action popover', async () => { + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByText('Actions')).toBeInTheDocument(); + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + }); + }); + + it('change the status of the case', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-status-panel-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-status-open')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-in-progress')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-closed')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-status-in-progress')); + }); + + await waitFor(() => { + expect(updateCasesSpy).toHaveBeenCalled(); + }); + }); + + it('change the severity of the case', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId(`case-action-severity-panel-${basicCase.id}`), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-severity-low')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-medium')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-high')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-critical')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-severity-medium')); + }); + + await waitFor(() => { + expect(updateCasesSpy).toHaveBeenCalled(); + }); + }); + + describe('Modals', () => { + it('delete a case', async () => { + const deleteSpy = jest.spyOn(api, 'deleteCases'); + + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-delete'), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(deleteSpy).toHaveBeenCalled(); + }); + }); + + it('closes the modal', async () => { + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-delete'), undefined, { + skipPointerEventsCheck: true, + }); + }); + + await waitFor(() => { + expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('confirmModalCancelButton'), undefined, { + skipPointerEventsCheck: true, + }); + }); + + expect(res.queryByTestId('confirm-delete-case-modal')).toBeFalsy(); + }); + }); + + describe('Permissions', () => { + it('shows the correct actions with all permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: allCasesPermissions() }); + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.getByTestId(`actions-separator-${basicCase.id}`)).toBeInTheDocument(); + }); + }); + + it('shows the correct actions with no delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: noDeleteCasesPermissions() }); + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.queryByTestId('cases-bulk-action-delete')).toBeFalsy(); + expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); + }); + }); + + it('shows the correct actions with only delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: onlyDeleteCasesPermission() }); + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + act(() => { + userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + }); + + await waitFor(() => { + expect(res.queryByTestId(`case-action-status-panel-${basicCase.id}`)).toBeFalsy(); + expect(res.queryByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeFalsy(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); + }); + }); + + it('returns null if the user does not have update or delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: readCasesPermissions() }); + const { result } = renderHook(() => useActions({ disableActions: false }), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current.actions).toBe(null); + }); + + it('disables the action correctly', async () => { + appMockRender = createAppMockRenderer({ permissions: onlyDeleteCasesPermission() }); + const { result } = renderHook(() => useActions({ disableActions: true }), { + wrapper: appMockRender.AppWrapper, + }); + + const comp = result.current.actions!.render(basicCase) as React.ReactElement; + const res = appMockRender.render(comp); + + await waitFor(() => { + expect(res.getByTestId(`case-action-popover-button-${basicCase.id}`)).toBeDisabled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx new file mode 100644 index 0000000000000..1fe7450835e8d --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx @@ -0,0 +1,195 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo, useState } from 'react'; +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiContextMenuPanelItemDescriptor, + EuiPopover, + EuiTableComputedColumnType, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { Case } from '../../containers/types'; +import { useDeleteAction } from '../actions/delete/use_delete_action'; +import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; +import { useStatusAction } from '../actions/status/use_status_action'; +import { useRefreshCases } from './use_on_refresh_cases'; +import * as i18n from './translations'; +import { statuses } from '../status'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import { useSeverityAction } from '../actions/severity/use_severity_action'; +import { severities } from '../severity/config'; + +const ActionColumnComponent: React.FC<{ theCase: Case; disableActions: boolean }> = ({ + theCase, + disableActions, +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const tooglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); + const closePopover = useCallback(() => setIsPopoverOpen(false), []); + const refreshCases = useRefreshCases(); + + const deleteAction = useDeleteAction({ + isDisabled: false, + onAction: closePopover, + onActionSuccess: refreshCases, + }); + + const statusAction = useStatusAction({ + isDisabled: false, + onAction: closePopover, + onActionSuccess: refreshCases, + selectedStatus: theCase.status, + }); + + const severityAction = useSeverityAction({ + isDisabled: false, + onAction: closePopover, + onActionSuccess: refreshCases, + selectedSeverity: theCase.severity, + }); + + const canDelete = deleteAction.canDelete; + const canUpdate = statusAction.canUpdateStatus; + + const panels = useMemo((): EuiContextMenuPanelDescriptor[] => { + const mainPanelItems: EuiContextMenuPanelItemDescriptor[] = []; + const panelsToBuild: EuiContextMenuPanelDescriptor[] = [ + { id: 0, items: mainPanelItems, title: i18n.ACTIONS }, + ]; + + if (canUpdate) { + mainPanelItems.push({ + name: ( + {statuses[theCase.status]?.label ?? '-'} }} + /> + ), + panel: 1, + disabled: !canUpdate, + key: `case-action-status-panel-${theCase.id}`, + 'data-test-subj': `case-action-status-panel-${theCase.id}`, + }); + + mainPanelItems.push({ + name: ( + {severities[theCase.severity]?.label ?? '-'} }} + /> + ), + panel: 2, + disabled: !canUpdate, + key: `case-action-severity-panel-${theCase.id}`, + 'data-test-subj': `case-action-severity-panel-${theCase.id}`, + }); + } + + /** + * A separator is added if a) there is one item above + * and b) there is an item below. For this to happen the + * user has to have delete and update permissions + */ + if (canUpdate && canDelete) { + mainPanelItems.push({ + isSeparator: true, + key: `actions-separator-${theCase.id}`, + 'data-test-subj': `actions-separator-${theCase.id}`, + }); + } + + if (canDelete) { + mainPanelItems.push(deleteAction.getAction([theCase])); + } + + if (canUpdate) { + panelsToBuild.push({ + id: 1, + title: i18n.STATUS, + items: statusAction.getActions([theCase]), + }); + + panelsToBuild.push({ + id: 2, + title: i18n.SEVERITY, + items: severityAction.getActions([theCase]), + }); + } + + return panelsToBuild; + }, [canDelete, canUpdate, deleteAction, severityAction, statusAction, theCase]); + + return ( + <> + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + {deleteAction.isModalVisible ? ( + + ) : null} + + ); +}; + +ActionColumnComponent.displayName = 'ActionColumnComponent'; + +const ActionColumn = React.memo(ActionColumnComponent); + +interface UseBulkActionsReturnValue { + actions: EuiTableComputedColumnType | null; +} + +interface UseBulkActionsProps { + disableActions: boolean; +} + +export const useActions = ({ disableActions }: UseBulkActionsProps): UseBulkActionsReturnValue => { + const { permissions } = useCasesContext(); + const shouldShowActions = permissions.update || permissions.delete; + + return { + actions: shouldShowActions + ? { + name: i18n.ACTIONS, + align: 'right', + render: (theCase: Case) => { + return ( + + ); + }, + } + : null, + }; +}; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.test.tsx new file mode 100644 index 0000000000000..cb4c473204ab0 --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.test.tsx @@ -0,0 +1,419 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. 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 { EuiContextMenu } from '@elastic/eui'; +import userEvent from '@testing-library/user-event'; +import { waitFor } from '@testing-library/react'; +import { act, renderHook } from '@testing-library/react-hooks'; + +import { + allCasesPermissions, + AppMockRenderer, + createAppMockRenderer, + noDeleteCasesPermissions, + onlyDeleteCasesPermission, +} from '../../common/mock'; +import { useBulkActions } from './use_bulk_actions'; +import * as api from '../../containers/api'; +import { basicCase } from '../../containers/mock'; + +jest.mock('../../containers/api'); + +describe('useBulkActions', () => { + let appMockRender: AppMockRenderer; + const onAction = jest.fn(); + const onActionSuccess = jest.fn(); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + describe('Panels', () => { + it('renders bulk actions', async () => { + const { result } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "modals": , + "panels": Array [ + Object { + "id": 0, + "items": Array [ + Object { + "data-test-subj": "case-bulk-action-status", + "disabled": false, + "key": "case-bulk-action-status", + "name": "Status", + "panel": 1, + }, + Object { + "data-test-subj": "case-bulk-action-severity", + "disabled": false, + "key": "case-bulk-action-severity", + "name": "Severity", + "panel": 2, + }, + Object { + "data-test-subj": "bulk-actions-separator", + "isSeparator": true, + "key": "bulk-actions-separator", + }, + Object { + "data-test-subj": "cases-bulk-action-delete", + "disabled": false, + "icon": , + "key": "cases-bulk-action-delete", + "name": + Delete case + , + "onClick": [Function], + }, + ], + "title": "Actions", + }, + Object { + "id": 1, + "items": Array [ + Object { + "data-test-subj": "cases-bulk-action-status-open", + "disabled": true, + "icon": "empty", + "key": "cases-bulk-action-status-open", + "name": "Open", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-status-in-progress", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-status-in-progress", + "name": "In progress", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-status-closed", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-status-action", + "name": "Closed", + "onClick": [Function], + }, + ], + "title": "Status", + }, + Object { + "id": 2, + "items": Array [ + Object { + "data-test-subj": "cases-bulk-action-severity-low", + "disabled": true, + "icon": "empty", + "key": "cases-bulk-action-severity-low", + "name": "Low", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-medium", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-medium", + "name": "Medium", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-high", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-high", + "name": "High", + "onClick": [Function], + }, + Object { + "data-test-subj": "cases-bulk-action-severity-critical", + "disabled": false, + "icon": "empty", + "key": "cases-bulk-action-severity-critical", + "name": "Critical", + "onClick": [Function], + }, + ], + "title": "Severity", + }, + ], + } + `); + }); + + it('change the status of cases', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + act(() => { + userEvent.click(res.getByTestId('case-bulk-action-status')); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-status-open')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-in-progress')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-closed')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-status-in-progress')); + }); + + await waitForHook(() => { + expect(updateCasesSpy).toHaveBeenCalled(); + }); + }); + + it('change the severity of cases', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + act(() => { + userEvent.click(res.getByTestId('case-bulk-action-severity')); + }); + + await waitFor(() => { + expect(res.getByTestId('cases-bulk-action-severity-low')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-medium')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-high')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-critical')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-severity-medium')); + }); + + await waitForHook(() => { + expect(updateCasesSpy).toHaveBeenCalled(); + }); + }); + + describe('Modals', () => { + it('delete a case', async () => { + const deleteSpy = jest.spyOn(api, 'deleteCases'); + + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + let modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-delete')); + }); + + modals = result.current.modals; + res.rerender( + <> + + {modals} + + ); + + await waitFor(() => { + expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('confirmModalConfirmButton')); + }); + + await waitForHook(() => { + expect(deleteSpy).toHaveBeenCalled(); + }); + }); + + it('closes the modal', async () => { + const { result } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + let modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + act(() => { + userEvent.click(res.getByTestId('cases-bulk-action-delete')); + }); + + modals = result.current.modals; + res.rerender( + <> + + {modals} + + ); + + await waitFor(() => { + expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); + + act(() => { + userEvent.click(res.getByTestId('confirmModalCancelButton')); + }); + + modals = result.current.modals; + res.rerender( + <> + + {modals} + + ); + + expect(res.queryByTestId('confirm-delete-case-modal')).toBeFalsy(); + }); + }); + }); + + describe('Permissions', () => { + it('shows the correct actions with all permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: allCasesPermissions() }); + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + await waitForHook(() => { + expect(res.getByTestId('case-bulk-action-status')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.getByTestId('bulk-actions-separator')).toBeInTheDocument(); + }); + }); + + it('shows the correct actions with no delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: noDeleteCasesPermissions() }); + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + await waitForHook(() => { + expect(res.getByTestId('case-bulk-action-status')).toBeInTheDocument(); + expect(res.queryByTestId('cases-bulk-action-delete')).toBeFalsy(); + expect(res.queryByTestId('bulk-actions-separator')).toBeFalsy(); + }); + }); + + it('shows the correct actions with only delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: onlyDeleteCasesPermission() }); + const { result, waitFor: waitForHook } = renderHook( + () => useBulkActions({ onAction, onActionSuccess, selectedCases: [basicCase] }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + const modals = result.current.modals; + const panels = result.current.panels; + + const res = appMockRender.render( + <> + + {modals} + + ); + + await waitForHook(() => { + expect(res.queryByTestId('case-bulk-action-status')).toBeFalsy(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.queryByTestId('bulk-actions-separator')).toBeFalsy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.tsx b/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.tsx new file mode 100644 index 0000000000000..dce7d136148de --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_bulk_actions.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; +import React, { useMemo } from 'react'; + +import { Case } from '../../containers/types'; +import { useDeleteAction } from '../actions/delete/use_delete_action'; +import { useSeverityAction } from '../actions/severity/use_severity_action'; +import { useStatusAction } from '../actions/status/use_status_action'; +import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; +import * as i18n from './translations'; + +interface UseBulkActionsProps { + selectedCases: Case[]; + onAction: () => void; + onActionSuccess: () => void; +} + +interface UseBulkActionsReturnValue { + panels: EuiContextMenuPanelDescriptor[]; + modals: JSX.Element; +} + +export const useBulkActions = ({ + selectedCases, + onAction, + onActionSuccess, +}: UseBulkActionsProps): UseBulkActionsReturnValue => { + const isDisabled = selectedCases.length === 0; + + const deleteAction = useDeleteAction({ + isDisabled, + onAction, + onActionSuccess, + }); + + const statusAction = useStatusAction({ + isDisabled, + onAction, + onActionSuccess, + }); + + const severityAction = useSeverityAction({ + isDisabled, + onAction, + onActionSuccess, + }); + + const canDelete = deleteAction.canDelete; + const canUpdate = statusAction.canUpdateStatus; + + const panels = useMemo((): EuiContextMenuPanelDescriptor[] => { + const mainPanelItems: EuiContextMenuPanelItemDescriptor[] = []; + const panelsToBuild: EuiContextMenuPanelDescriptor[] = [ + { id: 0, items: mainPanelItems, title: i18n.ACTIONS }, + ]; + + if (canUpdate) { + mainPanelItems.push({ + name: i18n.STATUS, + panel: 1, + disabled: isDisabled, + 'data-test-subj': 'case-bulk-action-status', + key: 'case-bulk-action-status', + }); + + mainPanelItems.push({ + name: i18n.SEVERITY, + panel: 2, + disabled: isDisabled, + 'data-test-subj': 'case-bulk-action-severity', + key: 'case-bulk-action-severity', + }); + } + + /** + * A separator is added if a) there is one item above + * and b) there is an item below. For this to happen the + * user has to have delete and update permissions + */ + if (canUpdate && canDelete) { + mainPanelItems.push({ + isSeparator: true as const, + key: 'bulk-actions-separator', + 'data-test-subj': 'bulk-actions-separator', + }); + } + + if (canDelete) { + mainPanelItems.push(deleteAction.getAction(selectedCases)); + } + + if (canUpdate) { + panelsToBuild.push({ + id: 1, + title: i18n.STATUS, + items: statusAction.getActions(selectedCases), + }); + + panelsToBuild.push({ + id: 2, + title: i18n.SEVERITY, + items: severityAction.getActions(selectedCases), + }); + } + + return panelsToBuild; + }, [canDelete, canUpdate, deleteAction, isDisabled, selectedCases, severityAction, statusAction]); + + return { + modals: ( + <> + {deleteAction.isModalVisible ? ( + + ) : null} + + ), + panels, + }; +}; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx new file mode 100644 index 0000000000000..85caa0b0348dc --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx @@ -0,0 +1,679 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; + +import '../../common/mock/match_media'; +import { ExternalServiceColumn, GetCasesColumn, useCasesColumns } from './use_cases_columns'; +import { useGetCasesMockState } from '../../containers/mock'; +import { connectors } from '../configure_cases/__mock__'; +import { + AppMockRenderer, + createAppMockRenderer, + readCasesPermissions, + TestProviders, +} from '../../common/mock'; +import { renderHook } from '@testing-library/react-hooks'; +import { CaseStatuses } from '../../../common'; +import { userProfilesMap, userProfiles } from '../../containers/user_profiles/api.mock'; + +describe('useCasesColumns ', () => { + let appMockRender: AppMockRenderer; + const useCasesColumnsProps: GetCasesColumn = { + filterStatus: CaseStatuses.open, + userProfiles: userProfilesMap, + currentUserProfile: userProfiles[0], + isSelectorView: false, + showSolutionColumn: true, + }; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('return all columns correctly', async () => { + const license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + + appMockRender = createAppMockRenderer({ license }); + + const { result } = renderHook(() => useCasesColumns(useCasesColumnsProps), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "assignees", + "name": "Assignees", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + ], + } + `); + }); + + it('does not render the solution columns', async () => { + const license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + + appMockRender = createAppMockRenderer({ license }); + + const { result } = renderHook( + () => useCasesColumns({ ...useCasesColumnsProps, showSolutionColumn: false }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "assignees", + "name": "Assignees", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + ], + } + `); + }); + + it('does not return the alerts column', async () => { + const license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + + appMockRender = createAppMockRenderer({ license, features: { alerts: { enabled: false } } }); + + const { result } = renderHook(() => useCasesColumns(useCasesColumnsProps), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "assignees", + "name": "Assignees", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + ], + } + `); + }); + + it('does not return the assignees column', async () => { + const { result } = renderHook(() => useCasesColumns(useCasesColumnsProps), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + ], + } + `); + }); + + it('shows the closedAt column if the filterStatus=closed', async () => { + appMockRender = createAppMockRenderer(); + + const { result } = renderHook( + () => useCasesColumns({ ...useCasesColumnsProps, filterStatus: CaseStatuses.closed }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "closedAt", + "name": "Closed on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "name": "Actions", + "render": [Function], + }, + ], + } + `); + }); + + it('shows the select button if isSelectorView=true', async () => { + const { result } = renderHook( + () => useCasesColumns({ ...useCasesColumnsProps, isSelectorView: true }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "render": [Function], + }, + ], + } + `); + }); + + it('does not shows the actions if isSelectorView=true', async () => { + const { result } = renderHook( + () => useCasesColumns({ ...useCasesColumnsProps, isSelectorView: true }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "render": [Function], + }, + ], + } + `); + }); + + it('does not shows the actions if the user does not have the right permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: readCasesPermissions() }); + + const { result } = renderHook(() => useCasesColumns(useCasesColumnsProps), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "truncateText": true, + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + ], + } + `); + }); + + describe('ExternalServiceColumn ', () => { + it('Not pushed render', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="case-table-column-external-notPushed"]`).last().exists() + ).toBeTruthy(); + }); + + it('Up to date', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="case-table-column-external-upToDate"]`).last().exists() + ).toBeTruthy(); + }); + + it('Needs update', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="case-table-column-external-requiresUpdate"]`).last().exists() + ).toBeTruthy(); + }); + + it('it does not throw when accessing the icon if the connector type is not registered', () => { + // If the component throws the test will fail + expect(() => + mount( + + + + ) + ).not.toThrowError(); + }); + + it('shows the connectors icon if the user has read access to actions', async () => { + const result = appMockRender.render( + + ); + + expect(result.getByTestId('cases-table-connector-icon')).toBeInTheDocument(); + }); + + it('hides the connectors icon if the user does not have read access to actions', async () => { + appMockRender.coreStart.application.capabilities = { + ...appMockRender.coreStart.application.capabilities, + actions: { save: false, show: false }, + }; + + const result = appMockRender.render( + + ); + + expect(result.queryByTestId('cases-table-connector-icon')).toBe(null); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx similarity index 50% rename from x-pack/plugins/cases/public/components/all_cases/columns.tsx rename to x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx index 948abdbbcd2f3..dc90fcfe6c4a5 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback } from 'react'; import { EuiBadgeGroup, EuiBadge, @@ -24,7 +24,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { UserProfileWithAvatar } from '@kbn/user-profile-components'; -import { Case, UpdateByKey } from '../../../common/ui/types'; +import { Case } from '../../../common/ui/types'; import { CaseStatuses, ActionConnector, CaseSeverity } from '../../../common/api'; import { OWNER_INFO } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; @@ -32,26 +32,21 @@ import { FormattedRelativePreferenceDate } from '../formatted_date'; import { CaseDetailsLink } from '../links'; import * as i18n from './translations'; import { ALERTS } from '../../common/translations'; -import { getActions } from './actions'; -import { useDeleteCases } from '../../containers/use_delete_cases'; -import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; +import { useActions } from './use_actions'; import { useApplicationCapabilities, useKibana } from '../../common/lib/kibana'; -import { StatusContextMenu } from '../case_action_bar/status_context_menu'; import { TruncatedText } from '../truncated_text'; import { getConnectorIcon } from '../utils'; import type { CasesOwners } from '../../client/helpers/can_use_cases'; import { severities } from '../severity/config'; -import { useUpdateCase } from '../../containers/use_update_case'; -import { useCasesContext } from '../cases_context/use_cases_context'; import { UserToolTip } from '../user_profiles/user_tooltip'; import { useAssignees } from '../../containers/user_profiles/use_assignees'; import { getUsernameDataTestSubj } from '../user_profiles/data_test_subject'; import { CurrentUserProfile } from '../types'; import { SmallUserAvatar } from '../user_profiles/small_user_avatar'; import { useCasesFeatures } from '../../common/use_cases_features'; -import { useRefreshCases } from './use_on_refresh_cases'; +import { Status } from '../status'; -export type CasesColumns = +type CasesColumns = | EuiTableActionsColumnType | EuiTableComputedColumnType | EuiTableFieldDataColumnType; @@ -107,9 +102,14 @@ export interface GetCasesColumn { isSelectorView: boolean; connectors?: ActionConnector[]; onRowClick?: (theCase: Case) => void; - showSolutionColumn?: boolean; + disableActions?: boolean; +} + +export interface UseCasesColumnsReturnValue { + columns: CasesColumns[]; } + export const useCasesColumns = ({ filterStatus, userProfiles, @@ -118,57 +118,10 @@ export const useCasesColumns = ({ connectors = [], onRowClick, showSolutionColumn, -}: GetCasesColumn): CasesColumns[] => { - const [isModalVisible, setIsModalVisible] = useState(false); - const { mutate: deleteCases } = useDeleteCases(); - const refreshCases = useRefreshCases(); + disableActions = false, +}: GetCasesColumn): UseCasesColumnsReturnValue => { const { isAlertsEnabled, caseAssignmentAuthorized } = useCasesFeatures(); - const { permissions } = useCasesContext(); - const [caseToBeDeleted, setCaseToBeDeleted] = useState(); - const { updateCaseProperty, isLoading: isLoadingUpdateCase } = useUpdateCase(); - - const closeModal = useCallback(() => setIsModalVisible(false), []); - const openModal = useCallback(() => setIsModalVisible(true), []); - - const onDeleteAction = useCallback( - (theCase: Case) => { - openModal(); - setCaseToBeDeleted(theCase.id); - }, - [openModal] - ); - - const onConfirmDeletion = useCallback(() => { - closeModal(); - if (caseToBeDeleted) { - deleteCases({ - caseIds: [caseToBeDeleted], - successToasterTitle: i18n.DELETED_CASES(1), - }); - } - }, [caseToBeDeleted, closeModal, deleteCases]); - - const handleDispatchUpdate = useCallback( - ({ updateKey, updateValue, caseData }: UpdateByKey) => { - updateCaseProperty({ - updateKey, - updateValue, - caseData, - onSuccess: () => { - refreshCases(); - }, - }); - }, - [refreshCases, updateCaseProperty] - ); - - const actions = useMemo( - () => - getActions({ - deleteCaseOnClick: onDeleteAction, - }), - [onDeleteAction] - ); + const { actions } = useActions({ disableActions }); const assignCaseAction = useCallback( async (theCase: Case) => { @@ -179,7 +132,7 @@ export const useCasesColumns = ({ [onRowClick] ); - return [ + const columns: CasesColumns[] = [ { name: i18n.NAME, render: (theCase: Case) => { @@ -205,129 +158,134 @@ export const useCasesColumns = ({ return getEmptyTagValue(); }, }, - ...(caseAssignmentAuthorized - ? [ - { - field: 'assignees', - name: i18n.ASSIGNEES, - render: (assignees: Case['assignees']) => ( - - ), - }, - ] - : []), - { - field: 'tags', - name: i18n.TAGS, - render: (tags: Case['tags']) => { - if (tags != null && tags.length > 0) { - const badges = ( - - {tags.map((tag: string, i: number) => ( - - {tag} - - ))} - - ); + ]; + + if (caseAssignmentAuthorized) { + columns.push({ + field: 'assignees', + name: i18n.ASSIGNEES, + render: (assignees: Case['assignees']) => ( + + ), + }); + } + + columns.push({ + field: 'tags', + name: i18n.TAGS, + render: (tags: Case['tags']) => { + if (tags != null && tags.length > 0) { + const badges = ( + + {tags.map((tag: string, i: number) => ( + + {tag} + + ))} + + ); + + return ( + + {badges} + + ); + } + return getEmptyTagValue(); + }, + truncateText: true, + }); + + if (isAlertsEnabled) { + columns.push({ + align: RIGHT_ALIGNMENT, + field: 'totalAlerts', + name: ALERTS, + render: (totalAlerts: Case['totalAlerts']) => + totalAlerts != null + ? renderStringField(`${totalAlerts}`, `case-table-column-alertsCount`) + : getEmptyTagValue(), + }); + } + + if (showSolutionColumn) { + columns.push({ + align: RIGHT_ALIGNMENT, + field: 'owner', + name: i18n.SOLUTION, + render: (caseOwner: CasesOwners) => { + const ownerInfo = OWNER_INFO[caseOwner]; + return ownerInfo ? ( + + ) : ( + getEmptyTagValue() + ); + }, + }); + } + columns.push({ + align: RIGHT_ALIGNMENT, + field: 'totalComment', + name: i18n.COMMENTS, + render: (totalComment: Case['totalComment']) => + totalComment != null + ? renderStringField(`${totalComment}`, `case-table-column-commentCount`) + : getEmptyTagValue(), + }); + + if (filterStatus === CaseStatuses.closed) { + columns.push({ + field: 'closedAt', + name: i18n.CLOSED_ON, + sortable: true, + render: (closedAt: Case['closedAt']) => { + if (closedAt != null) { return ( - - {badges} - + + + ); } return getEmptyTagValue(); }, - truncateText: true, - }, - ...(isAlertsEnabled - ? [ - { - align: RIGHT_ALIGNMENT, - field: 'totalAlerts', - name: ALERTS, - render: (totalAlerts: Case['totalAlerts']) => - totalAlerts != null - ? renderStringField(`${totalAlerts}`, `case-table-column-alertsCount`) - : getEmptyTagValue(), - }, - ] - : []), - ...(showSolutionColumn - ? [ - { - align: RIGHT_ALIGNMENT, - field: 'owner', - name: i18n.SOLUTION, - render: (caseOwner: CasesOwners) => { - const ownerInfo = OWNER_INFO[caseOwner]; - return ownerInfo ? ( - - ) : ( - getEmptyTagValue() - ); - }, - }, - ] - : []), - { - align: RIGHT_ALIGNMENT, - field: 'totalComment', - name: i18n.COMMENTS, - render: (totalComment: Case['totalComment']) => - totalComment != null - ? renderStringField(`${totalComment}`, `case-table-column-commentCount`) - : getEmptyTagValue(), - }, - filterStatus === CaseStatuses.closed - ? { - field: 'closedAt', - name: i18n.CLOSED_ON, - sortable: true, - render: (closedAt: Case['closedAt']) => { - if (closedAt != null) { - return ( - - - - ); - } - return getEmptyTagValue(); - }, + }); + } else { + columns.push({ + field: 'createdAt', + name: i18n.CREATED_ON, + sortable: true, + render: (createdAt: Case['createdAt']) => { + if (createdAt != null) { + return ( + + + + ); } - : { - field: 'createdAt', - name: i18n.CREATED_ON, - sortable: true, - render: (createdAt: Case['createdAt']) => { - if (createdAt != null) { - return ( - - - - ); - } - return getEmptyTagValue(); - }, - }, + return getEmptyTagValue(); + }, + }); + } + + columns.push( { name: i18n.EXTERNAL_INCIDENT, render: (theCase: Case) => { @@ -337,91 +295,63 @@ export const useCasesColumns = ({ return getEmptyTagValue(); }, }, - ...(!isSelectorView - ? [ - { - name: i18n.STATUS, - render: (theCase: Case) => { - if (theCase.status === null || theCase.status === undefined) { - return getEmptyTagValue(); - } - - return ( - - handleDispatchUpdate({ - updateKey: 'status', - updateValue: status, - caseData: theCase, - }) - } - /> - ); - }, - }, - ] - : []), + { + name: i18n.STATUS, + render: (theCase: Case) => { + if (theCase.status === null || theCase.status === undefined) { + return getEmptyTagValue(); + } + + return ; + }, + }, { name: i18n.SEVERITY, render: (theCase: Case) => { if (theCase.severity != null) { const severityData = severities[theCase.severity ?? CaseSeverity.LOW]; return ( - + {severityData.label} ); } return getEmptyTagValue(); }, - }, + } + ); - ...(isSelectorView - ? [ - { - align: RIGHT_ALIGNMENT, - render: (theCase: Case) => { - if (theCase.id != null) { - return ( - { - assignCaseAction(theCase); - }} - size="s" - fill={true} - > - {i18n.SELECT} - - ); - } - return getEmptyTagValue(); - }, - }, - ] - : []), - ...(permissions.delete && !isSelectorView - ? [ - { - name: ( - <> - {i18n.ACTIONS} - {isModalVisible ? ( - - ) : null} - - ), - actions, - }, - ] - : []), - ]; + if (isSelectorView) { + columns.push({ + align: RIGHT_ALIGNMENT, + render: (theCase: Case) => { + if (theCase.id != null) { + return ( + { + assignCaseAction(theCase); + }} + size="s" + fill={true} + > + {i18n.SELECT} + + ); + } + return getEmptyTagValue(); + }, + }); + } + + if (!isSelectorView && actions) { + columns.push(actions); + } + + return { columns }; }; interface Props { diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx new file mode 100644 index 0000000000000..3a8769460656d --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { + noCasesPermissions, + onlyDeleteCasesPermission, + AppMockRenderer, + createAppMockRenderer, + writeCasesPermissions, +} from '../../common/mock'; +import { casesQueriesKeys } from '../../containers/constants'; +import { basicCase } from '../../containers/mock'; +import { CasesTableUtilityBar } from './utility_bar'; + +describe('Severity form field', () => { + let appMockRender: AppMockRenderer; + const deselectCases = jest.fn(); + + const props = { + totalCases: 5, + selectedCases: [basicCase], + deselectCases, + }; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + + it('renders', async () => { + const result = appMockRender.render(); + expect(result.getByText('Showing 5 cases')).toBeInTheDocument(); + expect(result.getByText('Selected 1 case')).toBeInTheDocument(); + expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + expect(result.getByTestId('all-cases-refresh-link-icon')).toBeInTheDocument(); + }); + + it('opens the bulk actions correctly', async () => { + const result = appMockRender.render(); + + act(() => { + userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); + }); + + await waitFor(() => { + expect(result.getByTestId('case-table-bulk-actions-context-menu')); + }); + }); + + it('closes the bulk actions correctly', async () => { + const result = appMockRender.render(); + + act(() => { + userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); + }); + + await waitFor(() => { + expect(result.getByTestId('case-table-bulk-actions-context-menu')); + }); + + act(() => { + userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); + }); + + await waitFor(() => { + expect(result.queryByTestId('case-table-bulk-actions-context-menu')).toBeFalsy(); + }); + }); + + it('refresh correctly', async () => { + const result = appMockRender.render(); + const queryClientSpy = jest.spyOn(appMockRender.queryClient, 'invalidateQueries'); + + act(() => { + userEvent.click(result.getByTestId('all-cases-refresh-link-icon')); + }); + + await waitFor(() => { + expect(deselectCases).toHaveBeenCalled(); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.casesList()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.userProfiles()); + }); + }); + + it('does not show the bulk actions without update & delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: noCasesPermissions() }); + const result = appMockRender.render(); + + expect(result.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); + }); + + it('does show the bulk actions with only delete permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: onlyDeleteCasesPermission() }); + const result = appMockRender.render(); + + expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + }); + + it('does show the bulk actions with update permissions', async () => { + appMockRender = createAppMockRenderer({ permissions: writeCasesPermissions() }); + const result = appMockRender.render(); + + expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + }); + + it('does not show the bulk actions if there are not selected cases', async () => { + const result = appMockRender.render(); + + expect(result.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); + expect(result.queryByText('Showing 0 cases')).toBeFalsy(); + }); +}); 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 fdfcdc17d472c..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,152 +6,137 @@ */ import React, { FunctionComponent, useCallback, useState } from 'react'; -import { EuiContextMenuPanel } from '@elastic/eui'; -import { CaseStatuses } from '../../../common'; 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 { Cases, Case, FilterOptions } from '../../../common/ui/types'; -import { getBulkItems } from '../bulk_actions'; -import { useDeleteCases } from '../../containers/use_delete_cases'; -import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; -import { useUpdateCases } from '../../containers/use_bulk_update_case'; +import { Case } from '../../../common/ui/types'; import { useRefreshCases } from './use_on_refresh_cases'; +import { useBulkActions } from './use_bulk_actions'; +import { useCasesContext } from '../cases_context/use_cases_context'; interface Props { - data: Cases; - enableBulkActions: boolean; - filterOptions: FilterOptions; + isSelectorView?: boolean; + totalCases: number; selectedCases: Case[]; deselectCases: () => void; } -export const getStatusToasterMessage = (status: CaseStatuses, cases: Case[]): string => { - const totalCases = cases.length; - const caseTitle = totalCases === 1 ? cases[0].title : ''; +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), []); + const refreshCases = useRefreshCases(); + const { permissions } = useCasesContext(); - if (status === CaseStatuses.open) { - return i18n.REOPENED_CASES({ totalCases, caseTitle }); - } else if (status === CaseStatuses['in-progress']) { - return i18n.MARK_IN_PROGRESS_CASES({ totalCases, caseTitle }); - } else if (status === CaseStatuses.closed) { - return i18n.CLOSED_CASES({ totalCases, caseTitle }); - } - - return ''; -}; - -export const CasesTableUtilityBar: FunctionComponent = ({ - data, - enableBulkActions = false, - filterOptions, - selectedCases, - deselectCases, -}) => { - const [isModalVisible, setIsModalVisible] = useState(false); - const onCloseModal = useCallback(() => setIsModalVisible(false), []); - const refreshCases = useRefreshCases(); - - const { mutate: deleteCases } = useDeleteCases(); - const { mutate: updateCases } = useUpdateCases(); - - const toggleBulkDeleteModal = useCallback((cases: Case[]) => { - setIsModalVisible(true); - }, []); - - const handleUpdateCaseStatus = useCallback( - (status: CaseStatuses) => { - const casesToUpdate = selectedCases.map((theCase) => ({ - status, - id: theCase.id, - version: theCase.version, - })); + const onRefresh = useCallback(() => { + deselectCases(); + refreshCases(); + }, [deselectCases, refreshCases]); - updateCases({ - cases: casesToUpdate, - successToasterTitle: getStatusToasterMessage(status, selectedCases), - }); - }, - [selectedCases, updateCases] - ); - - const getBulkItemsPopoverContent = useCallback( - (closePopover: () => void) => ( - - ), - [selectedCases, filterOptions.status, toggleBulkDeleteModal, handleUpdateCaseStatus] - ); - - const onConfirmDeletion = useCallback(() => { - setIsModalVisible(false); - deleteCases({ - caseIds: selectedCases.map(({ id }) => id), - successToasterTitle: i18n.DELETED_CASES(selectedCases.length), + const { panels, modals } = useBulkActions({ + selectedCases, + onAction: closePopover, + onActionSuccess: onRefresh, }); - }, [deleteCases, selectedCases]); - - const onRefresh = useCallback(() => { - deselectCases(); - refreshCases(); - }, [deselectCases, refreshCases]); - return ( - - - - - {i18n.SHOWING_CASES(data.total ?? 0)} - - - - {enableBulkActions && ( - <> - - {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} - + /** + * At least update or delete permissions needed to show bulk actions. + * Granular permission check for each action is performed + * in the useBulkActions hook. + */ + const showBulkActions = (permissions.update || permissions.delete) && selectedCases.length > 0; - - {i18n.BULK_ACTIONS} - - - )} - + + - {i18n.REFRESH} - - - - {isModalVisible ? ( - - ) : null} - - ); -}; + + {i18n.SHOWING_CASES(totalCases)} + + + + + {!isSelectorView && showBulkActions && ( + <> + + + {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} + + + + + {i18n.BULK_ACTIONS} + + } + > + + + + + )} + + + {i18n.REFRESH} + + + + + + {modals} + + ); + } +); + CasesTableUtilityBar.displayName = 'CasesTableUtilityBar'; diff --git a/x-pack/plugins/cases/public/components/bulk_actions/index.tsx b/x-pack/plugins/cases/public/components/bulk_actions/index.tsx deleted file mode 100644 index fcf2002f8882c..0000000000000 --- a/x-pack/plugins/cases/public/components/bulk_actions/index.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiContextMenuItem } from '@elastic/eui'; - -import { CaseStatusWithAllStatus } from '../../../common/ui/types'; -import { CaseStatuses } from '../../../common/api'; -import { statuses } from '../status'; -import * as i18n from './translations'; -import { Case } from '../../containers/types'; - -interface GetBulkItems { - caseStatus: CaseStatusWithAllStatus; - closePopover: () => void; - deleteCasesAction: (cases: Case[]) => void; - selectedCases: Case[]; - updateCaseStatus: (status: CaseStatuses) => void; -} - -export const getBulkItems = ({ - caseStatus, - closePopover, - deleteCasesAction, - selectedCases, - updateCaseStatus, -}: GetBulkItems) => { - let statusMenuItems: JSX.Element[] = []; - - const openMenuItem = ( - { - closePopover(); - updateCaseStatus(CaseStatuses.open); - }} - > - {statuses[CaseStatuses.open].actions.bulk.title} - - ); - - const inProgressMenuItem = ( - { - closePopover(); - updateCaseStatus(CaseStatuses['in-progress']); - }} - > - {statuses[CaseStatuses['in-progress']].actions.bulk.title} - - ); - - const closeMenuItem = ( - { - closePopover(); - updateCaseStatus(CaseStatuses.closed); - }} - > - {statuses[CaseStatuses.closed].actions.bulk.title} - - ); - - switch (caseStatus) { - case CaseStatuses.open: - statusMenuItems = [inProgressMenuItem, closeMenuItem]; - break; - - case CaseStatuses['in-progress']: - statusMenuItems = [openMenuItem, closeMenuItem]; - break; - - case CaseStatuses.closed: - statusMenuItems = [openMenuItem, inProgressMenuItem]; - break; - - default: - break; - } - - return [ - ...statusMenuItems, - { - closePopover(); - deleteCasesAction(selectedCases); - }} - > - {i18n.BULK_ACTION_DELETE_SELECTED} - , - ]; -}; diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_alerts.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_alerts.tsx index f492240a8f9b8..0bd582e1cef62 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_alerts.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_alerts.tsx @@ -28,6 +28,7 @@ export const CaseViewAlerts = ({ caseData }: CaseViewAlertsProps) => { }), [caseData.comments] ); + const alertRegistrationContexts = useMemo( () => getRegistrationContextFromAlerts(caseData.comments), [caseData.comments] diff --git a/x-pack/plugins/cases/public/components/link_icon/index.tsx b/x-pack/plugins/cases/public/components/link_icon/index.tsx index b33529399db90..6285eceed0dd4 100644 --- a/x-pack/plugins/cases/public/components/link_icon/index.tsx +++ b/x-pack/plugins/cases/public/components/link_icon/index.tsx @@ -79,6 +79,7 @@ export const LinkIcon = React.memo( } return theChild != null && Object.keys(theChild).length > 0 ? (theChild as string) : ''; }, []); + const aria = useMemo(() => { if (ariaLabel) { return ariaLabel; diff --git a/x-pack/plugins/cases/public/components/status/config.ts b/x-pack/plugins/cases/public/components/status/config.ts index 6c5ff18ad977a..520759991605b 100644 --- a/x-pack/plugins/cases/public/components/status/config.ts +++ b/x-pack/plugins/cases/public/components/status/config.ts @@ -19,9 +19,6 @@ export const statuses: Statuses = { label: i18n.OPEN, icon: 'folderOpen' as const, actions: { - bulk: { - title: i18n.BULK_ACTION_OPEN_SELECTED, - }, single: { title: i18n.OPEN_CASE, }, @@ -41,9 +38,6 @@ export const statuses: Statuses = { label: i18n.IN_PROGRESS, icon: 'folderExclamation' as const, actions: { - bulk: { - title: i18n.BULK_ACTION_MARK_IN_PROGRESS, - }, single: { title: i18n.MARK_CASE_IN_PROGRESS, }, @@ -63,9 +57,6 @@ export const statuses: Statuses = { label: i18n.CLOSED, icon: 'folderCheck' as const, actions: { - bulk: { - title: i18n.BULK_ACTION_CLOSE_SELECTED, - }, single: { title: i18n.CLOSE_CASE, }, diff --git a/x-pack/plugins/cases/public/components/status/translations.ts b/x-pack/plugins/cases/public/components/status/translations.ts index 4fe75bbcfac7a..9401209c51c08 100644 --- a/x-pack/plugins/cases/public/components/status/translations.ts +++ b/x-pack/plugins/cases/public/components/status/translations.ts @@ -40,30 +40,9 @@ export const CASE_CLOSED = i18n.translate('xpack.cases.caseView.caseClosed', { defaultMessage: 'Case closed', }); -export const BULK_ACTION_CLOSE_SELECTED = i18n.translate( - 'xpack.cases.caseTable.bulkActions.closeSelectedTitle', - { - defaultMessage: 'Close selected', - } -); - -export const BULK_ACTION_OPEN_SELECTED = i18n.translate( - 'xpack.cases.caseTable.bulkActions.openSelectedTitle', - { - defaultMessage: 'Open selected', - } -); - export const BULK_ACTION_DELETE_SELECTED = i18n.translate( 'xpack.cases.caseTable.bulkActions.deleteSelectedTitle', { defaultMessage: 'Delete selected', } ); - -export const BULK_ACTION_MARK_IN_PROGRESS = i18n.translate( - 'xpack.cases.caseTable.bulkActions.markInProgressTitle', - { - defaultMessage: 'Mark in progress', - } -); diff --git a/x-pack/plugins/cases/public/components/status/types.ts b/x-pack/plugins/cases/public/components/status/types.ts index 0b4a1184633e1..1df8eb781ecc0 100644 --- a/x-pack/plugins/cases/public/components/status/types.ts +++ b/x-pack/plugins/cases/public/components/status/types.ts @@ -18,9 +18,6 @@ export type Statuses = Record< label: string; icon: EuiIconType; actions: { - bulk: { - title: string; - }; single: { title: string; description?: string; 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 f082dc4023e7a..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar.test.tsx.snap +++ /dev/null @@ -1,30 +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_action.test.tsx.snap b/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap deleted file mode 100644 index eb20ac217b300..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UtilityBarAction it renders 1`] = ` - - 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 62988a7a9dd76..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar.test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; 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 popover'}

}> - {'Test action'} -
-
-
- - - - {'Test action'} - - -
-
- ); - - expect(wrapper.find('UtilityBar')).toMatchSnapshot(); - }); - - test('it applies border styles when border is true', () => { - const wrapper = mount( - - - - - {'Test text'} - - - -

{'Test popover'}

}> - {'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 popover'}

}> - {'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 88977fa9bc587..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount, shallow } from 'enzyme'; -import React from 'react'; - -import { TestProviders } from '../../common/mock'; -import { UtilityBarAction } from '.'; - -describe('UtilityBarAction', () => { - test('it renders', () => { - const wrapper = shallow( - - {'Test action'} - - ); - - expect(wrapper.find('UtilityBarAction')).toMatchSnapshot(); - }); - - test('it renders a popover', () => { - const wrapper = mount( - -

{'Test popover'}

}> - {'Test action'} -
-
- ); - - expect(wrapper.find('.euiPopover').first().exists()).toBe(true); - }); -}); 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 e5bed87021491..0000000000000 --- a/x-pack/plugins/cases/public/components/utility_bar/utility_bar_action.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiPopover } from '@elastic/eui'; -import React, { useCallback, useState } from 'react'; - -import { LinkIcon, LinkIconProps } from '../link_icon'; -import { BarAction } from './styles'; - -const Popover = React.memo( - ({ children, color, iconSide, iconSize, iconType, popoverContent, disabled, ownFocus }) => { - const [popoverState, setPopoverState] = useState(false); - - const closePopover = useCallback(() => setPopoverState(false), [setPopoverState]); - - return ( - setPopoverState(!popoverState)} - disabled={disabled} - > - {children} - - } - closePopover={() => setPopoverState(false)} - isOpen={popoverState} - repositionOnScroll - > - {popoverContent?.(closePopover)} - - ); - } -); - -Popover.displayName = 'Popover'; - -export interface UtilityBarActionProps extends LinkIconProps { - popoverContent?: (closePopover: () => void) => React.ReactNode; - ownFocus?: boolean; - dataTestSubj?: string; -} - -export const UtilityBarAction = React.memo( - ({ - dataTestSubj, - children, - color, - disabled, - href, - iconSide, - iconSize, - iconType, - ownFocus, - onClick, - popoverContent, - }) => ( - - {popoverContent ? ( - - {children} - - ) : ( - - {children} - - )} - - ) -); - -UtilityBarAction.displayName = 'UtilityBarAction'; 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/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 2c0ee9bdc2b03..b7f1ab268a034 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -399,7 +399,14 @@ const basicAction = { export const cases: Case[] = [ basicCase, - { ...pushedCase, id: '1', totalComment: 0, comments: [] }, + { + ...pushedCase, + id: '1', + totalComment: 0, + comments: [], + status: CaseStatuses['in-progress'], + severity: CaseSeverity.MEDIUM, + }, { ...pushedCase, updatedAt: laterTime, id: '2', totalComment: 0, comments: [] }, { ...basicCase, id: '3', totalComment: 0, comments: [] }, { ...basicCase, id: '4', totalComment: 0, comments: [] }, @@ -557,7 +564,14 @@ export const pushedCaseSnake = { export const casesSnake: CasesResponse = [ basicCaseSnake, - { ...pushedCaseSnake, id: '1', totalComment: 0, comments: [] }, + { + ...pushedCaseSnake, + id: '1', + totalComment: 0, + comments: [], + status: CaseStatuses['in-progress'], + severity: CaseSeverity.MEDIUM, + }, { ...pushedCaseSnake, updated_at: laterTime, id: '2', totalComment: 0, comments: [] }, { ...basicCaseSnake, id: '3', totalComment: 0, comments: [] }, { ...basicCaseSnake, id: '4', totalComment: 0, comments: [] }, diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index 57eead3b80b10..10a4c1f6fd059 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -21,9 +21,13 @@ const uiMock: jest.Mocked = { getRecentCases: jest.fn(), }; +export const openAddToExistingCaseModalMock = jest.fn(); + const hooksMock: jest.Mocked = { getUseCasesAddToNewCaseFlyout: jest.fn(), - getUseCasesAddToExistingCaseModal: jest.fn(), + getUseCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({ + open: openAddToExistingCaseModalMock, + })), }; const helpersMock: jest.Mocked = { diff --git a/x-pack/plugins/cases/server/client/cases/types.ts b/x-pack/plugins/cases/server/client/cases/types.ts index c14cc66210614..6d56fa28dca59 100644 --- a/x-pack/plugins/cases/server/client/cases/types.ts +++ b/x-pack/plugins/cases/server/client/cases/types.ts @@ -17,7 +17,7 @@ import { PushToServiceApiParamsITSM as ServiceNowITSMPushToServiceApiParams, PushToServiceApiParamsSIR as ServiceNowSIRPushToServiceApiParams, ServiceNowITSMIncident, -} from '@kbn/stack-connectors-plugin/server/connector_types/cases/servicenow/types'; +} from '@kbn/stack-connectors-plugin/server/connector_types/lib/servicenow/types'; import { UserProfile } from '@kbn/security-plugin/common'; import { CaseResponse, ConnectorMappingsAttributes } from '../../../common/api'; diff --git a/x-pack/plugins/cloud/common/constants.ts b/x-pack/plugins/cloud/common/constants.ts index 09333e3773fe9..fc37906299d14 100644 --- a/x-pack/plugins/cloud/common/constants.ts +++ b/x-pack/plugins/cloud/common/constants.ts @@ -6,7 +6,6 @@ */ export const ELASTIC_SUPPORT_LINK = 'https://cloud.elastic.co/support'; -export const GET_CHAT_USER_DATA_ROUTE_PATH = '/internal/cloud/chat_user'; /** * This is the page for managing your snapshots on Cloud. diff --git a/x-pack/plugins/cloud/kibana.json b/x-pack/plugins/cloud/kibana.json index 51df5d20d81b9..85434abc87ede 100644 --- a/x-pack/plugins/cloud/kibana.json +++ b/x-pack/plugins/cloud/kibana.json @@ -7,7 +7,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "cloud"], - "optionalPlugins": ["cloudExperiments", "usageCollection", "home", "security"], + "optionalPlugins": ["usageCollection"], "server": true, "ui": true } diff --git a/x-pack/plugins/cloud/public/index.ts b/x-pack/plugins/cloud/public/index.ts index d50798cb15cd2..ee37f85dfb6a7 100644 --- a/x-pack/plugins/cloud/public/index.ts +++ b/x-pack/plugins/cloud/public/index.ts @@ -13,5 +13,3 @@ export type { CloudSetup, CloudConfigType, CloudStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new CloudPlugin(initializerContext); } - -export { Chat } from './components'; diff --git a/x-pack/plugins/cloud/public/mocks.tsx b/x-pack/plugins/cloud/public/mocks.tsx index f31596f3930f5..608e826657b73 100644 --- a/x-pack/plugins/cloud/public/mocks.tsx +++ b/x-pack/plugins/cloud/public/mocks.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { CloudStart } from '.'; -import { ServicesProvider } from './services'; function createSetupMock() { return { @@ -19,28 +18,22 @@ function createSetupMock() { deploymentUrl: 'deployment-url', profileUrl: 'profile-url', organizationUrl: 'organization-url', + registerCloudService: jest.fn(), }; } -const config = { - chat: { - enabled: true, - chatURL: 'chat-url', - user: { - id: 'user-id', - email: 'test-user@elastic.co', - jwt: 'identity-jwt', - }, - }, -}; - const getContextProvider: () => React.FC = () => ({ children }) => - {children}; + <>{children}; const createStartMock = (): jest.Mocked => ({ CloudContextProvider: jest.fn(getContextProvider()), + cloudId: 'mock-cloud-id', + isCloudEnabled: true, + deploymentUrl: 'deployment-url', + profileUrl: 'profile-url', + organizationUrl: 'organization-url', }); export const cloudMock = { diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index 599dee5e707b7..efb566761e22a 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -5,308 +5,18 @@ * 2.0. */ -import { firstValueFrom } from 'rxjs'; -import { Sha256 } from '@kbn/crypto-browser'; -import { nextTick } from '@kbn/test-jest-helpers'; import { coreMock } from '@kbn/core/public/mocks'; -import { homePluginMock } from '@kbn/home-plugin/public/mocks'; -import { securityMock } from '@kbn/security-plugin/public/mocks'; -import { CloudPlugin, type CloudConfigType } from './plugin'; -import { CloudExperimentsPluginSetup } from '@kbn/cloud-experiments-plugin/common'; -import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks'; +import { CloudPlugin } from './plugin'; const baseConfig = { base_url: 'https://cloud.elastic.co', deployment_url: '/abc123', profile_url: '/user/settings/', organization_url: '/account/', - full_story: { - enabled: false, - }, - chat: { - enabled: false, - }, }; describe('Cloud Plugin', () => { describe('#setup', () => { - describe('setupFullStory', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - const setupPlugin = async ({ config = {} }: { config?: Partial }) => { - const initContext = coreMock.createPluginInitializerContext({ - ...baseConfig, - id: 'cloudId', - ...config, - }); - - const plugin = new CloudPlugin(initContext); - - const coreSetup = coreMock.createSetup(); - - const setup = plugin.setup(coreSetup, {}); - - // Wait for FullStory dynamic import to resolve - await new Promise((r) => setImmediate(r)); - - return { initContext, plugin, setup, coreSetup }; - }; - - test('register the shipper FullStory with correct args when enabled and org_id are set', async () => { - const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, - }); - - expect(coreSetup.analytics.registerShipper).toHaveBeenCalled(); - expect(coreSetup.analytics.registerShipper).toHaveBeenCalledWith(expect.anything(), { - fullStoryOrgId: 'foo', - scriptUrl: '/internal/cloud/100/fullstory.js', - namespace: 'FSKibana', - }); - }); - - it('does not call initializeFullStory when enabled=false', async () => { - const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: false, org_id: 'foo' } }, - }); - expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); - }); - - it('does not call initializeFullStory when org_id is undefined', async () => { - const { coreSetup } = await setupPlugin({ config: { full_story: { enabled: true } } }); - expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); - }); - }); - - describe('setupTelemetryContext', () => { - const username = '1234'; - const expectedHashedPlainUsername = new Sha256().update(username, 'utf8').digest('hex'); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - const setupPlugin = async ({ - config = {}, - securityEnabled = true, - currentUserProps = {}, - }: { - config?: Partial; - securityEnabled?: boolean; - currentUserProps?: Record | Error; - }) => { - const initContext = coreMock.createPluginInitializerContext({ - ...baseConfig, - ...config, - }); - - const plugin = new CloudPlugin(initContext); - - const coreSetup = coreMock.createSetup(); - const securitySetup = securityMock.createSetup(); - if (currentUserProps instanceof Error) { - securitySetup.authc.getCurrentUser.mockRejectedValue(currentUserProps); - } else { - securitySetup.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser(currentUserProps) - ); - } - - const setup = plugin.setup(coreSetup, securityEnabled ? { security: securitySetup } : {}); - - return { initContext, plugin, setup, coreSetup }; - }; - - test('register the context provider for the cloud user with hashed user ID when security is available', async () => { - const { coreSetup } = await setupPlugin({ - config: { id: 'cloudId' }, - currentUserProps: { username }, - }); - - expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - - const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - await expect(firstValueFrom(context$)).resolves.toEqual({ - userId: '5ef112cfdae3dea57097bc276e275b2816e73ef2a398dc0ffaf5b6b4e3af2041', - isElasticCloudUser: false, - }); - }); - - it('user hash includes cloud id', async () => { - const { coreSetup: coreSetup1 } = await setupPlugin({ - config: { id: 'esOrg1' }, - currentUserProps: { username }, - }); - - const [{ context$: context1$ }] = - coreSetup1.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - const { userId: hashId1 } = (await firstValueFrom(context1$)) as { userId: string }; - expect(hashId1).not.toEqual(expectedHashedPlainUsername); - - const { coreSetup: coreSetup2 } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' }, id: 'esOrg2' }, - currentUserProps: { username }, - }); - - const [{ context$: context2$ }] = - coreSetup2.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - const { userId: hashId2 } = (await firstValueFrom(context2$)) as { userId: string }; - expect(hashId2).not.toEqual(expectedHashedPlainUsername); - - expect(hashId1).not.toEqual(hashId2); - }); - - test('user hash does not include cloudId when user is an Elastic Cloud user', async () => { - const { coreSetup } = await setupPlugin({ - config: { id: 'cloudDeploymentId' }, - currentUserProps: { username, elastic_cloud_user: true }, - }); - - expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - - const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - await expect(firstValueFrom(context$)).resolves.toEqual({ - userId: expectedHashedPlainUsername, - isElasticCloudUser: true, - }); - }); - - test('user hash does not include cloudId when not provided', async () => { - const { coreSetup } = await setupPlugin({ - config: {}, - currentUserProps: { username }, - }); - - expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - - const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - await expect(firstValueFrom(context$)).resolves.toEqual({ - userId: expectedHashedPlainUsername, - isElasticCloudUser: false, - }); - }); - - test('user hash is undefined when failed to fetch a user', async () => { - const { coreSetup } = await setupPlugin({ - currentUserProps: new Error('failed to fetch a user'), - }); - - expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - - const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - )!; - - await expect(firstValueFrom(context$)).resolves.toEqual({ - userId: undefined, - isElasticCloudUser: false, - }); - }); - }); - - describe('setupChat', () => { - let consoleMock: jest.SpyInstance; - - beforeEach(() => { - consoleMock = jest.spyOn(console, 'debug').mockImplementation(() => {}); - }); - - afterEach(() => { - consoleMock.mockRestore(); - }); - - const setupPlugin = async ({ - config = {}, - securityEnabled = true, - currentUserProps = {}, - isCloudEnabled = true, - failHttp = false, - }: { - config?: Partial; - securityEnabled?: boolean; - currentUserProps?: Record; - isCloudEnabled?: boolean; - failHttp?: boolean; - }) => { - const initContext = coreMock.createPluginInitializerContext({ - ...baseConfig, - id: isCloudEnabled ? 'cloud-id' : null, - ...config, - }); - - const plugin = new CloudPlugin(initContext); - - const coreSetup = coreMock.createSetup(); - const coreStart = coreMock.createStart(); - - if (failHttp) { - coreSetup.http.get.mockImplementation(() => { - throw new Error('HTTP request failed'); - }); - } - - coreSetup.getStartServices.mockResolvedValue([coreStart, {}, undefined]); - - const securitySetup = securityMock.createSetup(); - securitySetup.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser(currentUserProps) - ); - - const setup = plugin.setup(coreSetup, securityEnabled ? { security: securitySetup } : {}); - - return { initContext, plugin, setup, coreSetup }; - }; - - it('chatConfig is not retrieved if cloud is not enabled', async () => { - const { coreSetup } = await setupPlugin({ isCloudEnabled: false }); - expect(coreSetup.http.get).not.toHaveBeenCalled(); - }); - - it('chatConfig is not retrieved if security is not enabled', async () => { - const { coreSetup } = await setupPlugin({ securityEnabled: false }); - expect(coreSetup.http.get).not.toHaveBeenCalled(); - }); - - it('chatConfig is not retrieved if chat is enabled but url is not provided', async () => { - // @ts-expect-error 2741 - const { coreSetup } = await setupPlugin({ config: { chat: { enabled: true } } }); - expect(coreSetup.http.get).not.toHaveBeenCalled(); - }); - - it('chatConfig is not retrieved if internal API fails', async () => { - const { coreSetup } = await setupPlugin({ - config: { chat: { enabled: true, chatURL: 'http://chat.elastic.co' } }, - failHttp: true, - }); - expect(coreSetup.http.get).toHaveBeenCalled(); - expect(consoleMock).toHaveBeenCalled(); - }); - - it('chatConfig is retrieved if chat is enabled and url is provided', async () => { - const { coreSetup } = await setupPlugin({ - config: { chat: { enabled: true, chatURL: 'http://chat.elastic.co' } }, - }); - expect(coreSetup.http.get).toHaveBeenCalled(); - }); - }); - describe('interface', () => { const setupPlugin = () => { const initContext = coreMock.createPluginInitializerContext({ @@ -317,7 +27,7 @@ describe('Cloud Plugin', () => { const plugin = new CloudPlugin(initContext); const coreSetup = coreMock.createSetup(); - const setup = plugin.setup(coreSetup, {}); + const setup = plugin.setup(coreSetup); return { setup }; }; @@ -361,49 +71,10 @@ describe('Cloud Plugin', () => { const { setup } = setupPlugin(); expect(setup.cname).toBe('cloud.elastic.co'); }); - }); - - describe('Set up cloudExperiments', () => { - describe('when cloud ID is not provided in the config', () => { - let cloudExperiments: jest.Mocked; - beforeEach(() => { - const plugin = new CloudPlugin(coreMock.createPluginInitializerContext(baseConfig)); - cloudExperiments = cloudExperimentsMock.createSetupMock(); - plugin.setup(coreMock.createSetup(), { cloudExperiments }); - }); - test('does not call cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser).not.toHaveBeenCalled(); - }); - }); - - describe('when cloud ID is provided in the config', () => { - let cloudExperiments: jest.Mocked; - beforeEach(() => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext({ ...baseConfig, id: 'cloud test' }) - ); - cloudExperiments = cloudExperimentsMock.createSetupMock(); - plugin.setup(coreMock.createSetup(), { cloudExperiments }); - }); - - test('calls cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser).toHaveBeenCalledTimes(1); - }); - - test('the cloud ID is hashed when calling cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser.mock.calls[0][0]).toEqual( - '1acb4a1cc1c3d672a8d826055d897c2623ceb1d4fb07e46d97986751a36b06cf' - ); - }); - - test('specifies the Kibana version when calling cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser.mock.calls[0][1]).toEqual( - expect.objectContaining({ - kibanaVersion: 'version', - }) - ); - }); + it('exposes registerCloudService', () => { + const { setup } = setupPlugin(); + expect(setup.registerCloudService).toBeDefined(); }); }); }); @@ -426,9 +97,8 @@ describe('Cloud Plugin', () => { }) ); const coreSetup = coreMock.createSetup(); - const homeSetup = homePluginMock.createSetupContract(); - plugin.setup(coreSetup, { home: homeSetup }); + plugin.setup(coreSetup); return { coreSetup, plugin }; }; @@ -437,8 +107,7 @@ describe('Cloud Plugin', () => { const { plugin } = startPlugin(); const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - plugin.start(coreStart, { security: securityStart }); + plugin.start(coreStart); expect(coreStart.chrome.setHelpSupportUrl).toHaveBeenCalledTimes(1); expect(coreStart.chrome.setHelpSupportUrl.mock.calls[0]).toMatchInlineSnapshot(` @@ -447,177 +116,5 @@ describe('Cloud Plugin', () => { ] `); }); - - it('does not register custom nav links on anonymous pages', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true); - - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser({ - elastic_cloud_user: true, - }) - ); - - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(coreStart.chrome.setCustomNavLink).not.toHaveBeenCalled(); - expect(securityStart.authc.getCurrentUser).not.toHaveBeenCalled(); - }); - - it('registers a custom nav link for cloud users', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - - securityStart.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser({ - elastic_cloud_user: true, - }) - ); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(coreStart.chrome.setCustomNavLink).toHaveBeenCalledTimes(1); - expect(coreStart.chrome.setCustomNavLink.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "euiIconType": "logoCloud", - "href": "https://cloud.elastic.co/abc123", - "title": "Manage this deployment", - }, - ] - `); - }); - - it('registers a custom nav link when there is an error retrieving the current user', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockRejectedValue(new Error('something happened')); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(coreStart.chrome.setCustomNavLink).toHaveBeenCalledTimes(1); - expect(coreStart.chrome.setCustomNavLink.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "euiIconType": "logoCloud", - "href": "https://cloud.elastic.co/abc123", - "title": "Manage this deployment", - }, - ] - `); - }); - - it('does not register a custom nav link for non-cloud users', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser({ - elastic_cloud_user: false, - }) - ); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(coreStart.chrome.setCustomNavLink).not.toHaveBeenCalled(); - }); - - it('registers user profile links for cloud users', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser({ - elastic_cloud_user: true, - }) - ); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(securityStart.navControlService.addUserMenuLinks).toHaveBeenCalledTimes(1); - expect(securityStart.navControlService.addUserMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "href": "https://cloud.elastic.co/profile/alice", - "iconType": "user", - "label": "Edit profile", - "order": 100, - "setAsProfile": true, - }, - Object { - "href": "https://cloud.elastic.co/org/myOrg", - "iconType": "gear", - "label": "Account & Billing", - "order": 200, - }, - ], - ] - `); - }); - - it('registers profile links when there is an error retrieving the current user', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockRejectedValue(new Error('something happened')); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(securityStart.navControlService.addUserMenuLinks).toHaveBeenCalledTimes(1); - expect(securityStart.navControlService.addUserMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "href": "https://cloud.elastic.co/profile/alice", - "iconType": "user", - "label": "Edit profile", - "order": 100, - "setAsProfile": true, - }, - Object { - "href": "https://cloud.elastic.co/org/myOrg", - "iconType": "gear", - "label": "Account & Billing", - "order": 200, - }, - ], - ] - `); - }); - - it('does not register profile links for non-cloud users', async () => { - const { plugin } = startPlugin(); - - const coreStart = coreMock.createStart(); - const securityStart = securityMock.createStart(); - securityStart.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser({ - elastic_cloud_user: false, - }) - ); - plugin.start(coreStart, { security: securityStart }); - - await nextTick(); - - expect(securityStart.navControlService.addUserMenuLinks).not.toHaveBeenCalled(); - }); }); }); diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index c27668feb09bd..f50f41f3c79cd 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -6,34 +6,12 @@ */ import React, { FC } from 'react'; -import type { - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext, - HttpStart, - IBasePath, - AnalyticsServiceSetup, -} from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import useObservable from 'react-use/lib/useObservable'; -import { BehaviorSubject, catchError, from, map, of } from 'rxjs'; +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; -import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; -import { Sha256 } from '@kbn/crypto-browser'; -import type { CloudExperimentsPluginSetup } from '@kbn/cloud-experiments-plugin/common'; import { registerCloudDeploymentIdAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; -import { - ELASTIC_SUPPORT_LINK, - CLOUD_SNAPSHOTS_PATH, - GET_CHAT_USER_DATA_ROUTE_PATH, -} from '../common/constants'; -import type { GetChatUserDataResponseBody } from '../common/types'; -import { createUserMenuLinks } from './user_menu_links'; +import { ELASTIC_SUPPORT_LINK, CLOUD_SNAPSHOTS_PATH } from '../common/constants'; import { getFullCloudUrl } from './utils'; -import { ChatConfig, ServicesProvider } from './services'; export interface CloudConfigType { id?: string; @@ -47,23 +25,6 @@ export interface CloudConfigType { org_id?: string; eventTypesAllowlist?: string[]; }; - /** Configuration to enable live chat in Cloud-enabled instances of Kibana. */ - chat: { - /** Determines if chat is enabled. */ - enabled: boolean; - /** The URL to the remotely-hosted chat application. */ - chatURL: string; - }; -} - -interface CloudSetupDependencies { - home?: HomePublicPluginSetup; - security?: Pick; - cloudExperiments?: CloudExperimentsPluginSetup; -} - -interface CloudStartDependencies { - security?: SecurityPluginStart; } export interface CloudStart { @@ -71,6 +32,26 @@ export interface CloudStart { * A React component that provides a pre-wired `React.Context` which connects components to Cloud services. */ CloudContextProvider: FC<{}>; + /** + * `true` when Kibana is running on Elastic Cloud. + */ + isCloudEnabled: boolean; + /** + * Cloud ID. Undefined if not running on Cloud. + */ + cloudId?: string; + /** + * The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud. + */ + deploymentUrl?: string; + /** + * The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud. + */ + profileUrl?: string; + /** + * The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud. + */ + organizationUrl?: string; } export interface CloudSetup { @@ -82,268 +63,93 @@ export interface CloudSetup { organizationUrl?: string; snapshotsUrl?: string; isCloudEnabled: boolean; + registerCloudService: (contextProvider: FC) => void; } -interface SetupFullStoryDeps { - analytics: AnalyticsServiceSetup; - basePath: IBasePath; -} - -interface SetupChatDeps extends Pick { - http: CoreSetup['http']; +interface CloudUrls { + deploymentUrl?: string; + profileUrl?: string; + organizationUrl?: string; + snapshotsUrl?: string; } export class CloudPlugin implements Plugin { private readonly config: CloudConfigType; private readonly isCloudEnabled: boolean; - private chatConfig$ = new BehaviorSubject({ enabled: false }); + private readonly contextProviders: FC[] = []; constructor(private readonly initializerContext: PluginInitializerContext) { this.config = this.initializerContext.config.get(); this.isCloudEnabled = getIsCloudEnabled(this.config.id); } - public setup(core: CoreSetup, { cloudExperiments, home, security }: CloudSetupDependencies) { - this.setupTelemetryContext(core.analytics, security, this.config.id); - - this.setupFullStory({ analytics: core.analytics, basePath: core.http.basePath }).catch((e) => - // eslint-disable-next-line no-console - console.debug(`Error setting up FullStory: ${e.toString()}`) - ); + public setup(core: CoreSetup): CloudSetup { + registerCloudDeploymentIdAnalyticsContext(core.analytics, this.config.id); - const { - id, - cname, - profile_url: profileUrl, - organization_url: organizationUrl, - deployment_url: deploymentUrl, - base_url: baseUrl, - } = this.config; - - if (this.isCloudEnabled && id) { - // We use the Hashed Cloud Deployment ID as the userId in the Cloud Experiments - cloudExperiments?.identifyUser(sha256(id), { - kibanaVersion: this.initializerContext.env.packageInfo.version, - }); - } - - this.setupChat({ http: core.http, security }).catch((e) => - // eslint-disable-next-line no-console - console.debug(`Error setting up Chat: ${e.toString()}`) - ); - - if (home) { - home.environment.update({ cloud: this.isCloudEnabled }); - if (this.isCloudEnabled) { - home.tutorials.setVariable('cloud', { id, baseUrl, profileUrl, deploymentUrl }); - } - } - - const fullCloudDeploymentUrl = getFullCloudUrl(baseUrl, deploymentUrl); - const fullCloudProfileUrl = getFullCloudUrl(baseUrl, profileUrl); - const fullCloudOrganizationUrl = getFullCloudUrl(baseUrl, organizationUrl); - const fullCloudSnapshotsUrl = `${fullCloudDeploymentUrl}/${CLOUD_SNAPSHOTS_PATH}`; + const { id, cname, base_url: baseUrl } = this.config; return { cloudId: id, cname, baseUrl, - deploymentUrl: fullCloudDeploymentUrl, - profileUrl: fullCloudProfileUrl, - organizationUrl: fullCloudOrganizationUrl, - snapshotsUrl: fullCloudSnapshotsUrl, + ...this.getCloudUrls(), isCloudEnabled: this.isCloudEnabled, + registerCloudService: (contextProvider) => { + this.contextProviders.push(contextProvider); + }, }; } - public start(coreStart: CoreStart, { security }: CloudStartDependencies): CloudStart { - const { deployment_url: deploymentUrl, base_url: baseUrl } = this.config; + public start(coreStart: CoreStart): CloudStart { coreStart.chrome.setHelpSupportUrl(ELASTIC_SUPPORT_LINK); - const setLinks = (authorized: boolean) => { - if (!authorized) return; - - if (baseUrl && deploymentUrl) { - coreStart.chrome.setCustomNavLink({ - title: i18n.translate('xpack.cloud.deploymentLinkLabel', { - defaultMessage: 'Manage this deployment', - }), - euiIconType: 'logoCloud', - href: getFullCloudUrl(baseUrl, deploymentUrl), - }); - } - - if (security && this.isCloudEnabled) { - const userMenuLinks = createUserMenuLinks(this.config); - security.navControlService.addUserMenuLinks(userMenuLinks); - } - }; - - this.checkIfAuthorizedForLinks({ http: coreStart.http, security }) - .then(setLinks) - // In the event of an unexpected error, fail *open*. - // Cloud admin console will always perform the actual authorization checks. - .catch(() => setLinks(true)); - - // There's a risk that the request for chat config will take too much time to complete, and the provider - // will maintain a stale value. To avoid this, we'll use an Observable. + // Nest all the registered context providers under the Cloud Services Provider. + // This way, plugins only need to require Cloud's context provider to have all the enriched Cloud services. const CloudContextProvider: FC = ({ children }) => { - const chatConfig = useObservable(this.chatConfig$, { enabled: false }); - return {children}; + return ( + <> + {this.contextProviders.reduce( + (acc, ContextProvider) => ( + {acc} + ), + children + )} + + ); }; + const { deploymentUrl, profileUrl, organizationUrl } = this.getCloudUrls(); + return { CloudContextProvider, + isCloudEnabled: this.isCloudEnabled, + cloudId: this.config.id, + deploymentUrl, + profileUrl, + organizationUrl, }; } public stop() {} - /** - * Determines if the current user should see links back to Cloud. - * This isn't a true authorization check, but rather a heuristic to - * see if the current user is *likely* a cloud deployment administrator. - * - * At this point, we do not have enough information to reliably make this determination, - * but we do know that all cloud deployment admins are superusers by default. - */ - private async checkIfAuthorizedForLinks({ - http, - security, - }: { - http: HttpStart; - security?: SecurityPluginStart; - }) { - if (http.anonymousPaths.isAnonymous(window.location.pathname)) { - return false; - } - // Security plugin is disabled - if (!security) return true; - - // Otherwise check if user is a cloud user. - // If user is not defined due to an unexpected error, then fail *open*. - // Cloud admin console will always perform the actual authorization checks. - const user = await security.authc.getCurrentUser().catch(() => null); - return user?.elastic_cloud_user ?? true; - } - - /** - * If the right config is provided, register the FullStory shipper to the analytics client. - * @param analytics Core's Analytics service's setup contract. - * @param basePath Core's http.basePath helper. - * @private - */ - private async setupFullStory({ analytics, basePath }: SetupFullStoryDeps) { - const { enabled, org_id: fullStoryOrgId, eventTypesAllowlist } = this.config.full_story; - if (!enabled || !fullStoryOrgId) { - return; // do not load any FullStory code in the browser if not enabled - } - - // Keep this import async so that we do not load any FullStory code into the browser when it is disabled. - const { FullStoryShipper } = await import('@kbn/analytics-shippers-fullstory'); - analytics.registerShipper(FullStoryShipper, { - eventTypesAllowlist, - fullStoryOrgId, - // Load an Elastic-internally audited script. Ideally, it should be hosted on a CDN. - scriptUrl: basePath.prepend( - `/internal/cloud/${this.initializerContext.env.packageInfo.buildNum}/fullstory.js` - ), - namespace: 'FSKibana', - }); - } - - /** - * Set up the Analytics context providers. - * @param analytics Core's Analytics service. The Setup contract. - * @param security The security plugin. - * @param cloudId The Cloud Org ID. - * @private - */ - private setupTelemetryContext( - analytics: AnalyticsServiceSetup, - security?: Pick, - cloudId?: string - ) { - registerCloudDeploymentIdAnalyticsContext(analytics, cloudId); - - if (security) { - analytics.registerContextProvider({ - name: 'cloud_user_id', - context$: from(security.authc.getCurrentUser()).pipe( - map((user) => { - if (user.elastic_cloud_user) { - // If the user is managed by ESS, use the plain username as the user ID: - // The username is expected to be unique for these users, - // and it matches how users are identified in the Cloud UI, so it allows us to correlate them. - return { userId: user.username, isElasticCloudUser: true }; - } - - return { - // For the rest of the authentication providers, we want to add the cloud deployment ID to make it unique. - // Especially in the case of Elasticsearch-backed authentication, where users are commonly repeated - // across multiple deployments (i.e.: `elastic` superuser). - userId: cloudId ? `${cloudId}:${user.username}` : user.username, - isElasticCloudUser: false, - }; - }), - // The hashing here is to keep it at clear as possible in our source code that we do not send literal user IDs - map(({ userId, isElasticCloudUser }) => ({ userId: sha256(userId), isElasticCloudUser })), - catchError(() => of({ userId: undefined, isElasticCloudUser: false })) - ), - schema: { - userId: { - type: 'keyword', - _meta: { description: 'The user id scoped as seen by Cloud (hashed)' }, - }, - isElasticCloudUser: { - type: 'boolean', - _meta: { - description: '`true` if the user is managed by ESS.', - }, - }, - }, - }); - } - } - - private async setupChat({ http, security }: SetupChatDeps) { - if (!this.isCloudEnabled) { - return; - } - - const { enabled, chatURL } = this.config.chat; - - if (!security || !enabled || !chatURL) { - return; - } - - try { - const { - email, - id, - token: jwt, - } = await http.get(GET_CHAT_USER_DATA_ROUTE_PATH); + private getCloudUrls(): CloudUrls { + const { + profile_url: profileUrl, + organization_url: organizationUrl, + deployment_url: deploymentUrl, + base_url: baseUrl, + } = this.config; - if (!email || !id || !jwt) { - return; - } + const fullCloudDeploymentUrl = getFullCloudUrl(baseUrl, deploymentUrl); + const fullCloudProfileUrl = getFullCloudUrl(baseUrl, profileUrl); + const fullCloudOrganizationUrl = getFullCloudUrl(baseUrl, organizationUrl); + const fullCloudSnapshotsUrl = `${fullCloudDeploymentUrl}/${CLOUD_SNAPSHOTS_PATH}`; - this.chatConfig$.next({ - enabled, - chatURL, - user: { - email, - id, - jwt, - }, - }); - } catch (e) { - // eslint-disable-next-line no-console - console.debug(`[cloud.chat] Could not retrieve chat config: ${e.res.status} ${e.message}`, e); - } + return { + deploymentUrl: fullCloudDeploymentUrl, + profileUrl: fullCloudProfileUrl, + organizationUrl: fullCloudOrganizationUrl, + snapshotsUrl: fullCloudSnapshotsUrl, + }; } } - -function sha256(str: string) { - return new Sha256().update(str, 'utf8').digest('hex'); -} diff --git a/x-pack/plugins/cloud/server/config.ts b/x-pack/plugins/cloud/server/config.ts index aebbc65e50f18..512542c756798 100644 --- a/x-pack/plugins/cloud/server/config.ts +++ b/x-pack/plugins/cloud/server/config.ts @@ -18,32 +18,11 @@ const apmConfigSchema = schema.object({ ), }); -const fullStoryConfigSchema = schema.object({ - enabled: schema.boolean({ defaultValue: false }), - org_id: schema.conditional( - schema.siblingRef('enabled'), - true, - schema.string({ minLength: 1 }), - schema.maybe(schema.string()) - ), - eventTypesAllowlist: schema.arrayOf(schema.string(), { - defaultValue: ['Loaded Kibana'], - }), -}); - -const chatConfigSchema = schema.object({ - enabled: schema.boolean({ defaultValue: false }), - chatURL: schema.maybe(schema.string()), -}); - const configSchema = schema.object({ apm: schema.maybe(apmConfigSchema), base_url: schema.maybe(schema.string()), - chat: chatConfigSchema, - chatIdentitySecret: schema.maybe(schema.string()), cname: schema.maybe(schema.string()), deployment_url: schema.maybe(schema.string()), - full_story: fullStoryConfigSchema, id: schema.maybe(schema.string()), organization_url: schema.maybe(schema.string()), profile_url: schema.maybe(schema.string()), @@ -54,10 +33,8 @@ export type CloudConfigType = TypeOf; export const config: PluginConfigDescriptor = { exposeToBrowser: { base_url: true, - chat: true, cname: true, deployment_url: true, - full_story: true, id: true, organization_url: true, profile_url: true, diff --git a/x-pack/plugins/cloud/server/mocks.ts b/x-pack/plugins/cloud/server/mocks.ts new file mode 100644 index 0000000000000..557e64edf6cc1 --- /dev/null +++ b/x-pack/plugins/cloud/server/mocks.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CloudSetup } from '.'; + +function createSetupMock(): jest.Mocked { + return { + cloudId: 'mock-cloud-id', + instanceSizeMb: 1234, + deploymentId: 'deployment-id', + isCloudEnabled: true, + apm: { + url: undefined, + secretToken: undefined, + }, + }; +} + +export const cloudMock = { + createSetup: createSetupMock, +}; diff --git a/x-pack/plugins/cloud/server/plugin.test.ts b/x-pack/plugins/cloud/server/plugin.test.ts index 05109a4c54816..55be923e98cf8 100644 --- a/x-pack/plugins/cloud/server/plugin.test.ts +++ b/x-pack/plugins/cloud/server/plugin.test.ts @@ -7,111 +7,54 @@ import { coreMock } from '@kbn/core/server/mocks'; import { CloudPlugin } from './plugin'; -import { config } from './config'; -import { securityMock } from '@kbn/security-plugin/server/mocks'; -import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; -import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks'; -import { CloudExperimentsPluginSetup } from '@kbn/cloud-experiments-plugin/common'; + +const baseConfig = { + base_url: 'https://cloud.elastic.co', + deployment_url: '/abc123', + profile_url: '/user/settings/', + organization_url: '/account/', +}; describe('Cloud Plugin', () => { describe('#setup', () => { - describe('setupSecurity', () => { - it('properly handles missing optional Security dependency if Cloud ID is NOT set.', async () => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({})) - ); + describe('interface', () => { + const setupPlugin = () => { + const initContext = coreMock.createPluginInitializerContext({ + ...baseConfig, + id: 'cloudId', + cname: 'cloud.elastic.co', + }); + const plugin = new CloudPlugin(initContext); - expect(() => - plugin.setup(coreMock.createSetup(), { - usageCollection: usageCollectionPluginMock.createSetupContract(), - }) - ).not.toThrow(); - }); + const coreSetup = coreMock.createSetup(); + const setup = plugin.setup(coreSetup, {}); - it('properly handles missing optional Security dependency if Cloud ID is set.', async () => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({ id: 'my-cloud' })) - ); + return { setup }; + }; - expect(() => - plugin.setup(coreMock.createSetup(), { - usageCollection: usageCollectionPluginMock.createSetupContract(), - }) - ).not.toThrow(); + it('exposes isCloudEnabled', () => { + const { setup } = setupPlugin(); + expect(setup.isCloudEnabled).toBe(true); }); - it('does not notify Security plugin about Cloud environment if Cloud ID is NOT set.', async () => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({})) - ); - - const securityDependencyMock = securityMock.createSetup(); - plugin.setup(coreMock.createSetup(), { - security: securityDependencyMock, - usageCollection: usageCollectionPluginMock.createSetupContract(), - }); - - expect(securityDependencyMock.setIsElasticCloudDeployment).not.toHaveBeenCalled(); + it('exposes cloudId', () => { + const { setup } = setupPlugin(); + expect(setup.cloudId).toBe('cloudId'); }); - it('properly notifies Security plugin about Cloud environment if Cloud ID is set.', async () => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({ id: 'my-cloud' })) - ); - - const securityDependencyMock = securityMock.createSetup(); - plugin.setup(coreMock.createSetup(), { - security: securityDependencyMock, - usageCollection: usageCollectionPluginMock.createSetupContract(), - }); - - expect(securityDependencyMock.setIsElasticCloudDeployment).toHaveBeenCalledTimes(1); + it('exposes instanceSizeMb', () => { + const { setup } = setupPlugin(); + expect(setup.instanceSizeMb).toBeUndefined(); }); - }); - describe('Set up cloudExperiments', () => { - describe('when cloud ID is not provided in the config', () => { - let cloudExperiments: jest.Mocked; - beforeEach(() => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({})) - ); - cloudExperiments = cloudExperimentsMock.createSetupMock(); - plugin.setup(coreMock.createSetup(), { cloudExperiments }); - }); - - test('does not call cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser).not.toHaveBeenCalled(); - }); + it('exposes deploymentId', () => { + const { setup } = setupPlugin(); + expect(setup.deploymentId).toBe('abc123'); }); - describe('when cloud ID is provided in the config', () => { - let cloudExperiments: jest.Mocked; - beforeEach(() => { - const plugin = new CloudPlugin( - coreMock.createPluginInitializerContext(config.schema.validate({ id: 'cloud test' })) - ); - cloudExperiments = cloudExperimentsMock.createSetupMock(); - plugin.setup(coreMock.createSetup(), { cloudExperiments }); - }); - - test('calls cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser).toHaveBeenCalledTimes(1); - }); - - test('the cloud ID is hashed when calling cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser.mock.calls[0][0]).toEqual( - '1acb4a1cc1c3d672a8d826055d897c2623ceb1d4fb07e46d97986751a36b06cf' - ); - }); - - test('specifies the Kibana version when calling cloudExperiments.identifyUser', async () => { - expect(cloudExperiments.identifyUser.mock.calls[0][1]).toEqual( - expect.objectContaining({ - kibanaVersion: 'version', - }) - ); - }); + it('exposes apm', () => { + const { setup } = setupPlugin(); + expect(setup.apm).toStrictEqual({ url: undefined, secretToken: undefined }); }); }); }); diff --git a/x-pack/plugins/cloud/server/plugin.ts b/x-pack/plugins/cloud/server/plugin.ts index d38a57a4d3bab..9cf1a308800a0 100644 --- a/x-pack/plugins/cloud/server/plugin.ts +++ b/x-pack/plugins/cloud/server/plugin.ts @@ -5,24 +5,17 @@ * 2.0. */ -import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; -import { CoreSetup, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; -import type { CloudExperimentsPluginSetup } from '@kbn/cloud-experiments-plugin/common'; -import { createSHA256Hash } from '@kbn/crypto'; +import type { CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/server'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { registerCloudDeploymentIdAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context'; -import { CloudConfigType } from './config'; +import type { CloudConfigType } from './config'; import { registerCloudUsageCollector } from './collectors'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; import { parseDeploymentIdFromDeploymentUrl } from './utils'; -import { registerFullstoryRoute } from './routes/fullstory'; -import { registerChatRoute } from './routes/chat'; import { readInstanceSizeMb } from './env'; interface PluginsSetup { usageCollection?: UsageCollectionSetup; - security?: SecurityPluginSetup; - cloudExperiments?: CloudExperimentsPluginSetup; } export interface CloudSetup { @@ -37,52 +30,17 @@ export interface CloudSetup { } export class CloudPlugin implements Plugin { - private readonly logger: Logger; private readonly config: CloudConfigType; - private readonly isDev: boolean; constructor(private readonly context: PluginInitializerContext) { - this.logger = this.context.logger.get(); this.config = this.context.config.get(); - this.isDev = this.context.env.mode.dev; } - public setup( - core: CoreSetup, - { cloudExperiments, usageCollection, security }: PluginsSetup - ): CloudSetup { - this.logger.debug('Setting up Cloud plugin'); + public setup(core: CoreSetup, { usageCollection }: PluginsSetup): CloudSetup { const isCloudEnabled = getIsCloudEnabled(this.config.id); registerCloudDeploymentIdAnalyticsContext(core.analytics, this.config.id); registerCloudUsageCollector(usageCollection, { isCloudEnabled }); - if (isCloudEnabled) { - security?.setIsElasticCloudDeployment(); - } - - if (isCloudEnabled && this.config.id) { - // We use the Cloud ID as the userId in the Cloud Experiments - cloudExperiments?.identifyUser(createSHA256Hash(this.config.id), { - kibanaVersion: this.context.env.packageInfo.version, - }); - } - - if (this.config.full_story.enabled) { - registerFullstoryRoute({ - httpResources: core.http.resources, - packageInfo: this.context.env.packageInfo, - }); - } - - if (this.config.chat.enabled && this.config.chatIdentitySecret) { - registerChatRoute({ - router: core.http.createRouter(), - chatIdentitySecret: this.config.chatIdentitySecret, - security, - isDev: this.isDev, - }); - } - return { cloudId: this.config.id, instanceSizeMb: readInstanceSizeMb(), diff --git a/x-pack/plugins/cloud/tsconfig.json b/x-pack/plugins/cloud/tsconfig.json index d8c8a5c8eca44..ca9ba32ed10b0 100644 --- a/x-pack/plugins/cloud/tsconfig.json +++ b/x-pack/plugins/cloud/tsconfig.json @@ -16,8 +16,5 @@ "references": [ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/home/tsconfig.json" }, - { "path": "../cloud_integrations/cloud_experiments/tsconfig.json" }, - { "path": "../security/tsconfig.json" }, ] } diff --git a/x-pack/plugins/cloud/.storybook/decorator.tsx b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/decorator.tsx similarity index 89% rename from x-pack/plugins/cloud/.storybook/decorator.tsx rename to x-pack/plugins/cloud_integrations/cloud_chat/.storybook/decorator.tsx index 4489b58f75759..3af8d04a598eb 100644 --- a/x-pack/plugins/cloud/.storybook/decorator.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/decorator.tsx @@ -7,12 +7,11 @@ import React from 'react'; import { DecoratorFn } from '@storybook/react'; -import { ServicesProvider, CloudServices } from '../public/services'; +import { ServicesProvider, CloudChatServices } from '../public/services'; // TODO: move to a storybook implementation of the service using parameters. -const services: CloudServices = { +const services: CloudChatServices = { chat: { - enabled: true, chatURL: 'https://elasticcloud-production-chat-us-east-1.s3.amazonaws.com/drift-iframe.html', user: { id: 'user-id', diff --git a/x-pack/plugins/cloud/.storybook/index.ts b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/index.ts similarity index 100% rename from x-pack/plugins/cloud/.storybook/index.ts rename to x-pack/plugins/cloud_integrations/cloud_chat/.storybook/index.ts diff --git a/x-pack/plugins/cloud/.storybook/main.ts b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/main.ts similarity index 100% rename from x-pack/plugins/cloud/.storybook/main.ts rename to x-pack/plugins/cloud_integrations/cloud_chat/.storybook/main.ts diff --git a/x-pack/plugins/cloud/.storybook/manager.ts b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/manager.ts similarity index 100% rename from x-pack/plugins/cloud/.storybook/manager.ts rename to x-pack/plugins/cloud_integrations/cloud_chat/.storybook/manager.ts diff --git a/x-pack/plugins/cloud/.storybook/preview.ts b/x-pack/plugins/cloud_integrations/cloud_chat/.storybook/preview.ts similarity index 100% rename from x-pack/plugins/cloud/.storybook/preview.ts rename to x-pack/plugins/cloud_integrations/cloud_chat/.storybook/preview.ts diff --git a/x-pack/plugins/cloud_integrations/cloud_chat/README.md b/x-pack/plugins/cloud_integrations/cloud_chat/README.md new file mode 100755 index 0000000000000..cee3d9f5a6671 --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_chat/README.md @@ -0,0 +1,3 @@ +# Cloud Chat + +Integrates with DriftChat in order to provide live support to our Elastic Cloud users. This plugin should only run on Elastic Cloud. diff --git a/x-pack/plugins/cloud_integrations/cloud_chat/common/constants.ts b/x-pack/plugins/cloud_integrations/cloud_chat/common/constants.ts new file mode 100755 index 0000000000000..d7bd133e5b4f9 --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_chat/common/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const GET_CHAT_USER_DATA_ROUTE_PATH = '/internal/cloud/chat_user'; diff --git a/x-pack/plugins/cloud/common/types.ts b/x-pack/plugins/cloud_integrations/cloud_chat/common/types.ts similarity index 100% rename from x-pack/plugins/cloud/common/types.ts rename to x-pack/plugins/cloud_integrations/cloud_chat/common/types.ts diff --git a/x-pack/plugins/cloud_integrations/cloud_chat/jest.config.js b/x-pack/plugins/cloud_integrations/cloud_chat/jest.config.js new file mode 100644 index 0000000000000..44f6f241d44d0 --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_chat/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../', + roots: ['/x-pack/plugins/cloud_integrations/cloud_chat'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/cloud_integrations/cloud_chat', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/cloud_integrations/cloud_chat/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/x-pack/plugins/cloud_integrations/cloud_chat/kibana.json b/x-pack/plugins/cloud_integrations/cloud_chat/kibana.json new file mode 100755 index 0000000000000..76f7e34e71e56 --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_chat/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "cloudChat", + "version": "1.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "Kibana Core", + "githubTeam": "kibana-core" + }, + "description": "Chat available on Elastic Cloud deployments for quicker assistance.", + "server": true, + "ui": true, + "configPath": ["xpack", "cloud_integrations", "chat"], + "requiredPlugins": ["cloud"], + "optionalPlugins": ["security"] +} diff --git a/x-pack/plugins/cloud/public/components/chat/chat.stories.tsx b/x-pack/plugins/cloud_integrations/cloud_chat/public/components/chat/chat.stories.tsx similarity index 99% rename from x-pack/plugins/cloud/public/components/chat/chat.stories.tsx rename to x-pack/plugins/cloud_integrations/cloud_chat/public/components/chat/chat.stories.tsx index 7e673e341cec7..295750ee43039 100644 --- a/x-pack/plugins/cloud/public/components/chat/chat.stories.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_chat/public/components/chat/chat.stories.tsx @@ -68,7 +68,6 @@ export const Component = ({ id, email, chatURL, jwt }: Params) => { return ( {}, onReady, onResize }: Props) => { }} size="xs" > - {i18n.translate('xpack.cloud.chat.hideChatButtonLabel', { + {i18n.translate('xpack.cloudChat.hideChatButtonLabel', { defaultMessage: 'Hide chat', })} @@ -80,7 +80,7 @@ export const Chat = ({ onHide = () => {}, onReady, onResize }: Props) => { {button}