diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 47c6cab47b052..1d3ea8c5dd4d0 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -248,7 +248,7 @@ enabled: - x-pack/test/security_functional/login_selector.config.ts - x-pack/test/security_functional/oidc.config.ts - x-pack/test/security_functional/saml.config.ts - - x-pack/test/security_functional/unsecure_cluster_alert.config.ts + - x-pack/test/security_functional/insecure_cluster_warning.config.ts - x-pack/test/security_solution_endpoint_api_int/config.ts - x-pack/test/security_solution_endpoint/config.ts - x-pack/test/session_view/basic/config.ts diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index cb12517aeeb1e..54e5273eb4c68 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -7,13 +7,19 @@ source .buildkite/scripts/common/util.sh export KBN_NP_PLUGINS_BUILT=true echo "--- Build Kibana Distribution" + +BUILD_ARGS="" if is_pr_with_label "ci:build-all-platforms"; then - node scripts/build --all-platforms --skip-os-packages -elif is_pr_with_label "ci:build-os-packages"; then - node scripts/build --all-platforms --docker-cross-compile -else - node scripts/build + BUILD_ARGS="--all-platforms --skip-os-packages" +fi +if is_pr_with_label "ci:build-os-packages"; then + BUILD_ARGS="--all-platforms --docker-cross-compile" fi +if ! is_pr_with_label "ci:build-canvas-shareable-runtime"; then + BUILD_ARGS="$BUILD_ARGS --skip-canvas-shareable-runtime" +fi + +node scripts/build "$BUILD_ARGS" if is_pr_with_label "ci:build-cloud-image"; then echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co diff --git a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh index e50e23030bb3f..cb17393f5f423 100755 --- a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh +++ b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh @@ -13,7 +13,7 @@ GCS_BUCKET="gs://kibana-performance/scalability-tests" .buildkite/scripts/bootstrap.sh echo "--- Extract APM metrics" -scalabilityJourneys=("login" "promotion_tracking_dashboard") +scalabilityJourneys=("login" "ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard" "many_fields_discover") for i in "${scalabilityJourneys[@]}"; do JOURNEY_NAME="${i}" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d6365810cd41c..86c6553e89a2b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -248,6 +248,11 @@ /packages/kbn-ui-shared-deps-npm/ @elastic/kibana-operations /packages/kbn-ui-shared-deps-src/ @elastic/kibana-operations /packages/kbn-utils/ @elastic/kibana-operations +/packages/kbn-jsonc/ @elastic/kibana-operations +/packages/kbn-kibana-manifest-parser/ @elastic/kibana-operations +/packages/kbn-kibana-manifest-schema/ @elastic/kibana-operations +/packages/kbn-managed-vscode-config/ @elastic/kibana-operations +/packages/kbn-managed-vscode-config-cli/ @elastic/kibana-operations /src/cli/keystore/ @elastic/kibana-operations /.ci/es-snapshots/ @elastic/kibana-operations /.github/workflows/ @elastic/kibana-operations @@ -263,7 +268,6 @@ # Performance testing /packages/kbn-performance-testing-dataset-extractor/ @elastic/kibana-performance-testing -/packages/kbn-scalability-simulation-generator/ @elastic/kibana-performance-testing # Quality Assurance /src/dev/code_coverage @elastic/kibana-qa @@ -337,6 +341,7 @@ # Kibana Platform Security /packages/kbn-crypto/ @elastic/kibana-security /packages/kbn-handlebars/ @elastic/kibana-security +/packages/kbn-user-profile-components/ @elastic/kibana-security /packages/core/http/core-http-server-internal/src/csp/ @elastic/kibana-security @elastic/kibana-core /src/plugins/interactive_setup/ @elastic/kibana-security /src/plugins/telemetry/server/config/telemetry_labels.ts @elastic/kibana-security @@ -357,6 +362,7 @@ /x-pack/test/spaces_api_integration/ @elastic/kibana-security /x-pack/test/saved_object_api_integration/ @elastic/kibana-security /examples/preboot_example/ @elastic/kibana-security @elastic/kibana-core +/examples/user_profile_examples/ @elastic/kibana-security #CC# /x-pack/plugins/security/ @elastic/kibana-security # Response Ops team diff --git a/.i18nrc.json b/.i18nrc.json index 08f2ff151b4c2..b77d2c42508f1 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -77,6 +77,7 @@ "uiActionsEnhanced": "src/plugins/ui_actions_enhanced", "uiActionsExamples": "examples/ui_action_examples", "usageCollection": "src/plugins/usage_collection", + "userProfileComponents": "packages/kbn-user-profile-components", "utils": "packages/kbn-securitysolution-utils/src", "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeGauge": "src/plugins/vis_types/gauge", diff --git a/NOTICE.txt b/NOTICE.txt index 10ae7ea6686b3..b150cf5673b42 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -121,6 +121,20 @@ THE SOFTWARE. This product uses Noto fonts that are licensed under the SIL Open Font License, Version 1.1. +--- +Vendored copy of `strip-json-comments` so that we can use it when npm modules are not available. +https://github.com/sindresorhus/strip-json-comments/tree/34b79cb0f1129aa85ef4b5c3292e8bc546984ef9 + +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + --- Based on the scroll-into-view-if-necessary module from npm https://github.com/stipsan/compute-scroll-into-view/blob/master/src/index.ts#L269-L340 diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 53f0e7d43438a..94c50f9d9954d 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github summary: API docs for the actions plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index c2db91466b083..cfcdce5a2f26b 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github summary: API docs for the advancedSettings plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 4953d17a169c4..38ba6df5b485c 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github summary: API docs for the aiops plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 4ecbacf26584d..c0ea50254e064 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github summary: API docs for the alerting plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 1c5c2b751041d..c5a43840223fa 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github summary: API docs for the apm plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 3c2c1eed0babc..b7c35e2256ac4 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github summary: API docs for the banners plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index d6b38ed74e19b..e315ff2049b07 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github summary: API docs for the bfetch plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 392b9e440a777..80c2f52c7d546 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github summary: API docs for the canvas plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cases.devdocs.json b/api_docs/cases.devdocs.json index f333dff70af34..5f8bc2d01e9f1 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -466,6 +466,21 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "cases", + "id": "def-public.CaseAttachmentsWithoutOwner", + "type": "Type", + "tags": [], + "label": "CaseAttachmentsWithoutOwner", + "description": [], + "signature": [ + "CaseAttachmentWithoutOwner", + "[]" + ], + "path": "x-pack/plugins/cases/public/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "cases", "id": "def-public.DRAFT_COMMENT_STORAGE_ID", @@ -601,8 +616,8 @@ "pluginId": "cases", "scope": "public", "docId": "kibCasesPluginApi", - "section": "def-public.CaseAttachments", - "text": "CaseAttachments" + "section": "def-public.CaseAttachmentsWithoutOwner", + "text": "CaseAttachmentsWithoutOwner" }, " | undefined; }" ], @@ -725,7 +740,7 @@ }, " | undefined; severity?: ", "CaseSeverity", - " | undefined; reporters?: string | string[] | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; fields?: string | string[] | undefined; from?: string | undefined; page?: number | undefined; perPage?: number | undefined; search?: string | undefined; searchFields?: string | string[] | undefined; sortField?: string | undefined; sortOrder?: \"asc\" | \"desc\" | undefined; to?: string | undefined; owner?: string | string[] | undefined; }, signal?: AbortSignal | undefined) => Promise<", + " | undefined; assignees?: string | string[] | undefined; reporters?: string | string[] | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; fields?: string | string[] | undefined; from?: string | undefined; page?: number | undefined; perPage?: number | undefined; search?: string | undefined; searchFields?: string | string[] | undefined; sortField?: string | undefined; sortOrder?: \"asc\" | \"desc\" | undefined; to?: string | undefined; owner?: string | string[] | undefined; }, signal?: AbortSignal | undefined) => Promise<", "Cases", ">; getCasesStatus: (query: { from?: string | undefined; to?: string | undefined; owner?: string | string[] | undefined; }, signal?: AbortSignal | undefined) => Promise<{ countOpenCases: number; countInProgressCases: number; countClosedCases: number; }>; getCasesMetrics: (query: { features: string[]; } & { from?: string | undefined; to?: string | undefined; owner?: string | string[] | undefined; }, signal?: AbortSignal | undefined) => Promise<{ mttr?: number | null | undefined; }>; }; }" ], @@ -824,16 +839,16 @@ "pluginId": "cases", "scope": "public", "docId": "kibCasesPluginApi", - "section": "def-public.CaseAttachments", - "text": "CaseAttachments" + "section": "def-public.CaseAttachmentsWithoutOwner", + "text": "CaseAttachmentsWithoutOwner" }, " | undefined; }) => void; close: () => void; }; getUseCasesAddToExistingCaseModal: (props?: AddToExistingFlyoutProps) => { open: ({ attachments }?: { attachments?: ", { "pluginId": "cases", "scope": "public", "docId": "kibCasesPluginApi", - "section": "def-public.CaseAttachments", - "text": "CaseAttachments" + "section": "def-public.CaseAttachmentsWithoutOwner", + "text": "CaseAttachmentsWithoutOwner" }, " | undefined; }) => void; close: () => void; }; }" ], @@ -866,13 +881,13 @@ "section": "def-common.CasesPermissions", "text": "CasesPermissions" }, - "; getRuleIdFromEvent: (event: Event) => { id: string; name: string; }; groupAlertsByRule: (items: Event[], owner: string) => ", + "; getRuleIdFromEvent: (event: Event) => { id: string; name: string; }; groupAlertsByRule: (items: Event[]) => ", { "pluginId": "cases", "scope": "public", "docId": "kibCasesPluginApi", - "section": "def-public.CaseAttachments", - "text": "CaseAttachments" + "section": "def-public.CaseAttachmentsWithoutOwner", + "text": "CaseAttachmentsWithoutOwner" }, "; }" ], @@ -1425,7 +1440,7 @@ "ConnectorTypes", ".swimlane; fields: { caseId: string | null; } | null; name: string; }; settings: { syncAlerts: boolean; }; owner: string; severity: ", "CaseSeverity", - "; duration: number | null; closedAt: string | null; closedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; } | null; createdAt: string; createdBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; }; externalService: { connectorId: string; connectorName: string; externalId: string; externalTitle: string; externalUrl: string; pushedAt: string; pushedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; }; } | null; updatedAt: string | null; updatedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; } | null; id: string; totalComment: number; totalAlerts: number; version: string; comments?: ((({ comment: string; type: ", + "; assignees: { uid: string; }[]; duration: number | null; closedAt: string | null; closedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; } | null; createdAt: string; createdBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; }; externalService: { connectorId: string; connectorName: string; externalId: string; externalTitle: string; externalUrl: string; pushedAt: string; pushedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; }; } | null; updatedAt: string | null; updatedBy: { email: string | null | undefined; fullName: string | null | undefined; username: string | null | undefined; } | null; id: string; totalComment: number; totalAlerts: number; version: string; comments?: ((({ comment: string; type: ", { "pluginId": "cases", "scope": "common", diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index b18ea2f3b7ea7..35affa4f03f9d 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github summary: API docs for the cases plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) for qu | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 81 | 0 | 65 | 28 | +| 82 | 0 | 66 | 29 | ## Client diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index d56cbd6dae269..6bb34dd0cddc0 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github summary: API docs for the charts plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index c994decbee1d1..911a4342765bd 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloud plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 9460c809d4731..f2f550a7219f0 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloudSecurityPosture plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 13d9b7020c9b2..49e9b233afff2 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github summary: API docs for the console plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 2fe07186c4c62..7dd1f58a09b9f 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github summary: API docs for the controls plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index dc72756cac3a8..caa8db405aef7 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -475,7 +475,7 @@ "\nReports a telemetry event." ], "signature": [ - ">(eventType: string, eventData: EventTypeData) => void" + "(eventType: string, eventData: EventTypeData) => void" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -1031,7 +1031,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -1228,7 +1228,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -1334,13 +1334,7 @@ "{@link SavedObjectsStart}" ], "signature": [ - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - } + "SavedObjectsStart" ], "path": "src/core/public/index.ts", "deprecated": false @@ -1794,7 +1788,8 @@ "\nDefinition of the full event structure" ], "signature": [ - "Event" + "Event", + "" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -1826,14 +1821,14 @@ { "parentPluginId": "core", "id": "def-public.Event.properties", - "type": "Object", + "type": "Uncategorized", "tags": [], "label": "properties", "description": [ "\nThe specific properties of the event type." ], "signature": [ - "{ [x: string]: unknown; }" + "Properties" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false @@ -4165,7 +4160,7 @@ "signature": [ "(events: ", "Event", - "[]) => void" + ">[]) => void" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -4181,7 +4176,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -8674,6 +8669,37 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-public.SavedObjectsStart", + "type": "Interface", + "tags": [], + "label": "SavedObjectsStart", + "description": [], + "signature": [ + "SavedObjectsStart" + ], + "path": "node_modules/@types/kbn__core-saved-objects-browser/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-public.SavedObjectsStart.client", + "type": "Object", + "tags": [], + "label": "client", + "description": [ + "{@link SavedObjectsClientContract}" + ], + "signature": [ + "SavedObjectsClientContract" + ], + "path": "node_modules/@types/kbn__core-saved-objects-browser/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-public.SavedObjectsUpdateOptions", @@ -9659,7 +9685,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -9691,7 +9717,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -10538,7 +10564,7 @@ "\nReports a telemetry event." ], "signature": [ - ">(eventType: string, eventData: EventTypeData) => void" + "(eventType: string, eventData: EventTypeData) => void" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -12127,7 +12153,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -12316,7 +12342,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -12608,7 +12634,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -13955,7 +13981,8 @@ "\nDefinition of the full event structure" ], "signature": [ - "Event" + "Event", + "" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -13987,14 +14014,14 @@ { "parentPluginId": "core", "id": "def-server.Event.properties", - "type": "Object", + "type": "Uncategorized", "tags": [], "label": "properties", "description": [ "\nThe specific properties of the event type." ], "signature": [ - "{ [x: string]: unknown; }" + "Properties" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false @@ -23071,7 +23098,7 @@ "signature": [ "(events: ", "Event", - "[]) => void" + ">[]) => void" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -23087,7 +23114,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, @@ -36270,7 +36297,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -36302,7 +36329,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -36334,7 +36361,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 1fe20ec82567f..3d80bced0c0a9 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github summary: API docs for the core plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 297 | 6 | +| 2524 | 2 | 296 | 6 | ## Client diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index c88711c5edd10..6e287b4e1a1e6 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-application title: "core.application" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.application plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.application'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 297 | 6 | +| 2524 | 2 | 296 | 6 | ## Client diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index d8b79e0b1ea8c..d84c6bcc7aa18 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-chrome title: "core.chrome" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.chrome plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.chrome'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 297 | 6 | +| 2524 | 2 | 296 | 6 | ## Client diff --git a/api_docs/core_saved_objects.devdocs.json b/api_docs/core_saved_objects.devdocs.json index 02ab9528eb6ba..7c494cf0fad8e 100644 --- a/api_docs/core_saved_objects.devdocs.json +++ b/api_docs/core_saved_objects.devdocs.json @@ -3,36 +3,7 @@ "client": { "classes": [], "functions": [], - "interfaces": [ - { - "parentPluginId": "core", - "id": "def-public.SavedObjectsStart", - "type": "Interface", - "tags": [], - "label": "SavedObjectsStart", - "description": [], - "path": "src/core/public/saved_objects/saved_objects_service.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-public.SavedObjectsStart.client", - "type": "Object", - "tags": [], - "label": "client", - "description": [ - "{@link SavedObjectsClientContract}" - ], - "signature": [ - "SavedObjectsClientContract" - ], - "path": "src/core/public/saved_objects/saved_objects_service.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [], "objects": [] diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index 5f762f68d8946..2afb5d24b66a8 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-savedObjects title: "core.savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.savedObjects plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.savedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,12 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 297 | 6 | - -## Client - -### Interfaces - +| 2524 | 2 | 296 | 6 | ## Server diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 54ce10c44676a..b576443d8c71e 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github summary: API docs for the customIntegrations plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 662f6d0db964e..70ae98a1c4bc7 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboard plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 9320c1c05a2b1..82fdeb274e97c 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboardEnhanced plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index a218f7d6e6973..226fc6b9ef1fd 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -7102,13 +7102,7 @@ "label": "savedObjects", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - } + "SavedObjectsStart" ], "path": "src/plugins/data/public/types.ts", "deprecated": false @@ -22378,10 +22372,10 @@ }, { "parentPluginId": "data", - "id": "def-common.IDataViewsApiClient.hasUserIndexPattern", + "id": "def-common.IDataViewsApiClient.hasUserDataView", "type": "Function", "tags": [], - "label": "hasUserIndexPattern", + "label": "hasUserDataView", "description": [], "signature": [ "() => Promise" @@ -23584,38 +23578,21 @@ "tags": [], "label": "DEFAULT_ASSETS_TO_IGNORE", "description": [ - "\nUsed to determine if the instance has some user created index patterns by filtering index patterns\nthat are created and backed only by Fleet server data\nShould be revised after https://github.com/elastic/kibana/issues/82851 is fixed\nFor more background see: https://github.com/elastic/kibana/issues/107020" + "\nUsed to optimize on-boarding experience to determine if the instance has some user created data views or data indices/streams by filtering data sources\nthat are created by default by elastic in ese.\nWe should somehow prevent creating initial data for the users without their explicit action\ninstead of relying on these hardcoded assets" ], "path": "src/plugins/data_views/common/constants.ts", "deprecated": false, "children": [ { "parentPluginId": "data", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.LOGS_INDEX_PATTERN", - "type": "string", - "tags": [], - "label": "LOGS_INDEX_PATTERN", - "description": [], - "path": "src/plugins/data_views/common/constants.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE", - "type": "string", - "tags": [], - "label": "LOGS_DATA_STREAM_TO_IGNORE", - "description": [], - "path": "src/plugins/data_views/common/constants.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE", - "type": "string", + "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.DATA_STREAMS_TO_IGNORE", + "type": "Array", "tags": [], - "label": "ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE", + "label": "DATA_STREAMS_TO_IGNORE", "description": [], + "signature": [ + "string[]" + ], "path": "src/plugins/data_views/common/constants.ts", "deprecated": false } diff --git a/api_docs/data.mdx b/api_docs/data.mdx index ad41c0b685955..a918c0ff173af 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github summary: API docs for the data plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3097 | 34 | 2420 | 21 | +| 3098 | 34 | 2421 | 22 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 695f56e807b01..cab0ee9f1bc25 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.query plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3097 | 34 | 2420 | 21 | +| 3098 | 34 | 2421 | 22 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 8dde1cc91ae97..4694ad9c33926 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -12396,7 +12396,9 @@ "label": "getFilterBucketAgg", "description": [], "signature": [ - "({ getConfig }: { getConfig: (key: string) => any; }) => ", + "({ getConfig, calculateBounds, }: { getConfig: (key: string) => T; calculateBounds: ", + "CalculateBoundsFn", + "; }) => ", { "pluginId": "data", "scope": "common", @@ -12422,7 +12424,7 @@ "id": "def-common.getFilterBucketAgg.$1", "type": "Object", "tags": [], - "label": "{ getConfig }", + "label": "{\n getConfig,\n calculateBounds,\n}", "description": [], "path": "src/plugins/data/common/search/aggs/buckets/filter.ts", "deprecated": false, @@ -12435,7 +12437,7 @@ "label": "getConfig", "description": [], "signature": [ - "(key: string) => any" + "(key: string) => T" ], "path": "src/plugins/data/common/search/aggs/buckets/filter.ts", "deprecated": false, @@ -12456,6 +12458,44 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.getFilterBucketAgg.$1.calculateBounds", + "type": "Function", + "tags": [], + "label": "calculateBounds", + "description": [], + "signature": [ + "(timeRange: ", + "TimeRange", + ") => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + } + ], + "path": "src/plugins/data/common/search/aggs/buckets/filter.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-common.getFilterBucketAgg.$1.calculateBounds.$1", + "type": "Object", + "tags": [], + "label": "timeRange", + "description": [], + "signature": [ + "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" + ], + "path": "src/plugins/data/common/search/aggs/buckets/date_histogram.ts", + "deprecated": false + } + ] } ] } @@ -18444,6 +18484,19 @@ ], "path": "src/plugins/data/common/search/aggs/buckets/filter.ts", "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-common.AggParamsFilter.timeWindow", + "type": "string", + "tags": [], + "label": "timeWindow", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data/common/search/aggs/buckets/filter.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 84934965dd73f..f3b9927708a9f 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.search plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3097 | 34 | 2420 | 21 | +| 3098 | 34 | 2421 | 22 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 473368c99a6d1..2a2c12e4a8915 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewEditor plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 0cfdc54ecc366..907d738289e91 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewFieldEditor plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index f3fe08062d028..f7443130744ab 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewManagement plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 1cdb9bfdd01a8..9dfc6a17bbff4 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -2318,10 +2318,10 @@ }, { "parentPluginId": "dataViews", - "id": "def-public.DataViewsApiClient.hasUserIndexPattern", + "id": "def-public.DataViewsApiClient.hasUserDataView", "type": "Function", "tags": [], - "label": "hasUserIndexPattern", + "label": "hasUserDataView", "description": [ "\nDoes a user created data view exist?" ], @@ -17886,10 +17886,10 @@ }, { "parentPluginId": "dataViews", - "id": "def-common.IDataViewsApiClient.hasUserIndexPattern", + "id": "def-common.IDataViewsApiClient.hasUserDataView", "type": "Function", "tags": [], - "label": "hasUserIndexPattern", + "label": "hasUserDataView", "description": [], "signature": [ "() => Promise" @@ -20443,38 +20443,21 @@ "tags": [], "label": "DEFAULT_ASSETS_TO_IGNORE", "description": [ - "\nUsed to determine if the instance has some user created index patterns by filtering index patterns\nthat are created and backed only by Fleet server data\nShould be revised after https://github.com/elastic/kibana/issues/82851 is fixed\nFor more background see: https://github.com/elastic/kibana/issues/107020" + "\nUsed to optimize on-boarding experience to determine if the instance has some user created data views or data indices/streams by filtering data sources\nthat are created by default by elastic in ese.\nWe should somehow prevent creating initial data for the users without their explicit action\ninstead of relying on these hardcoded assets" ], "path": "src/plugins/data_views/common/constants.ts", "deprecated": false, "children": [ { "parentPluginId": "dataViews", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.LOGS_INDEX_PATTERN", - "type": "string", - "tags": [], - "label": "LOGS_INDEX_PATTERN", - "description": [], - "path": "src/plugins/data_views/common/constants.ts", - "deprecated": false - }, - { - "parentPluginId": "dataViews", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE", - "type": "string", - "tags": [], - "label": "LOGS_DATA_STREAM_TO_IGNORE", - "description": [], - "path": "src/plugins/data_views/common/constants.ts", - "deprecated": false - }, - { - "parentPluginId": "dataViews", - "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE", - "type": "string", + "id": "def-common.DEFAULT_ASSETS_TO_IGNORE.DATA_STREAMS_TO_IGNORE", + "type": "Array", "tags": [], - "label": "ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE", + "label": "DATA_STREAMS_TO_IGNORE", "description": [], + "signature": [ + "string[]" + ], "path": "src/plugins/data_views/common/constants.ts", "deprecated": false } diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 22864869dbcfa..6bbfb613480c5 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViews plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 945 | 0 | 201 | 0 | +| 943 | 0 | 199 | 0 | ## Client diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 28bca4a317ebc..c8f0dc4159104 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataVisualizer plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 6c1d68815d01c..0b004fb065085 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 1f0985e83797e..74c5405b0b74e 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 7c2c6c0de8296..b487c52323374 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team summary: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index bab76e873aa2b..d1a66d83eb453 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github summary: API docs for the devTools plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 72f73fe043307..008ce55af5ec5 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github summary: API docs for the discover plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index b962e99385921..e601532664c5e 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the discoverEnhanced plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/elastic_apm_synthtrace.mdx b/api_docs/elastic_apm_synthtrace.mdx index 539dc5e90f8cf..22f5d14c68d7f 100644 --- a/api_docs/elastic_apm_synthtrace.mdx +++ b/api_docs/elastic_apm_synthtrace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/elastic-apm-synthtrace title: "@elastic/apm-synthtrace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @elastic/apm-synthtrace plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/apm-synthtrace'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 66b9b60c14ec5..9dcf520eef23c 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github summary: API docs for the embeddable plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index dfc85cced95fc..42e9d0825eceb 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the embeddableEnhanced plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 40eb6f94668d7..fa12313742d26 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the encryptedSavedObjects plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index ce8281a32574c..4910b22726c45 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the enterpriseSearch plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 2dbf4d36b6a68..1dd87e2f43a70 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github summary: API docs for the esUiShared plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 3e6d99e665677..94196d18c036e 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventAnnotation plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 818f154dc5845..dbf3e9989b95d 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventLog plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 3e19d4babd52f..ae27d29c34c0d 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionError plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 1bc118f68515b..b59345f33e59f 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionGauge plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 14aae9ced31e6..b9681028971ec 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionHeatmap plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index b5615295dc816..041174d17f695 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionImage plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 849aa65517c37..d557cd3d408ff 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionLegacyMetricVis plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index cb44fefeee55d..c8f6a25260ae5 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetric plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 1f8116634883a..594054a9f03cb 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetricVis plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index b9e5bb881cb4d..c299735df94bc 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionPartitionVis plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index f6ee15aaa23ee..713676a784f11 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRepeatImage plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 29fed05b73c15..a44b3476af664 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRevealImage plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 9a400bf1e500e..f9d588e2fc7bb 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionShape plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 5bcd7964420a2..392b7b49d5a70 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionTagcloud plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 964cb152407b6..a82a65d7aa66d 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionXY plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 2fd3a2479f2a2..36a95d690a340 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressions plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 25bf5617bb050..1bb9913bc88e4 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github summary: API docs for the features plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index d15893e7a6b2c..84ad7bbf17789 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github summary: API docs for the fieldFormats plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index c1f80d63ae97e..445cfceda7551 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github summary: API docs for the fileUpload plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index cb62c2c134bdb..727d7e709351a 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github summary: API docs for the fleet plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index e0e300e6e2dba..9ab3d227a3b96 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the globalSearch plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/home.mdx b/api_docs/home.mdx index ff59a2d99b132..e40bfc8372fb4 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github summary: API docs for the home plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index a74e8ec9ad801..92077942efa90 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexLifecycleManagement plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 41afd93c25662..f86225d3c0c72 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexManagement plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index e15ba17089092..f34f5a43ac79d 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github summary: API docs for the infra plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index ded532d9f8e93..fa233bdf41acd 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github summary: API docs for the inspector plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index ee39aad81dd4c..73b7ef904892d 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github summary: API docs for the interactiveSetup plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 29c4a67c52d78..dc19436275a02 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ace plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_aiops_components.devdocs.json b/api_docs/kbn_aiops_components.devdocs.json index 3f97c3f94849c..f72706ba13e58 100644 --- a/api_docs/kbn_aiops_components.devdocs.json +++ b/api_docs/kbn_aiops_components.devdocs.json @@ -27,7 +27,7 @@ "label": "DualBrush", "description": [], "signature": [ - "({\n windowParameters,\n min,\n max,\n onChange,\n marginLeft,\n width,\n}: DualBrushProps) => JSX.Element" + "({\n windowParameters,\n min,\n max,\n onChange,\n marginLeft,\n snapTimestamps,\n width,\n}: DualBrushProps) => JSX.Element" ], "path": "x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx", "deprecated": false, @@ -37,7 +37,7 @@ "id": "def-common.DualBrush.$1", "type": "Object", "tags": [], - "label": "{\n windowParameters,\n min,\n max,\n onChange,\n marginLeft,\n width,\n}", + "label": "{\n windowParameters,\n min,\n max,\n onChange,\n marginLeft,\n snapTimestamps,\n width,\n}", "description": [], "signature": [ "DualBrushProps" @@ -89,7 +89,7 @@ "label": "ProgressControls", "description": [], "signature": [ - "({\n progress,\n progressMessage,\n onRefresh,\n onCancel,\n isRunning,\n}: ProgressControlProps) => JSX.Element" + "({\n progress,\n progressMessage,\n onRefresh,\n onCancel,\n isRunning,\n shouldRerunAnalysis,\n}: ProgressControlProps) => JSX.Element" ], "path": "x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx", "deprecated": false, @@ -99,7 +99,7 @@ "id": "def-common.ProgressControls.$1", "type": "Object", "tags": [], - "label": "{\n progress,\n progressMessage,\n onRefresh,\n onCancel,\n isRunning,\n}", + "label": "{\n progress,\n progressMessage,\n onRefresh,\n onCancel,\n isRunning,\n shouldRerunAnalysis,\n}", "description": [], "signature": [ "ProgressControlProps" diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 582a8b0bfe0fd..9886a1539a025 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/aiops-components plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_aiops_utils.devdocs.json b/api_docs/kbn_aiops_utils.devdocs.json index 3efcc1adf9861..5b96cc8b8b7c7 100644 --- a/api_docs/kbn_aiops_utils.devdocs.json +++ b/api_docs/kbn_aiops_utils.devdocs.json @@ -19,6 +19,80 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getSnappedWindowParameters", + "type": "Function", + "tags": [], + "label": "getSnappedWindowParameters", + "description": [ + "\n\nConverts window paramaters from the brushes to “snap” the brushes to the chart histogram bar width and ensure timestamps\ncorrespond to bucket timestamps\n" + ], + "signature": [ + "(windowParameters: ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + }, + ", snapTimestamps: number[]) => ", + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + } + ], + "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getSnappedWindowParameters.$1", + "type": "Object", + "tags": [], + "label": "windowParameters", + "description": [ + "time range definition for baseline and deviation to be used by spike log analysis" + ], + "signature": [ + { + "pluginId": "@kbn/aiops-utils", + "scope": "common", + "docId": "kibKbnAiopsUtilsPluginApi", + "section": "def-common.WindowParameters", + "text": "WindowParameters" + } + ], + "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/aiops-utils", + "id": "def-common.getSnappedWindowParameters.$2", + "type": "Array", + "tags": [], + "label": "snapTimestamps", + "description": [ + "time range definition that always corresponds to histogram bucket timestamps" + ], + "signature": [ + "number[]" + ], + "path": "x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "WindowParameters" + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/aiops-utils", "id": "def-common.getWindowParameters", diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 8c504730efded..8c0f155c497c6 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/aiops-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact Machine Learning UI for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 46 | 0 | 24 | 0 | +| 49 | 0 | 24 | 0 | ## Common diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index d839b9ba80c9d..4c394ae0db206 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/alerts plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 7ff02c0531f4b..ba11bc0700ed3 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 42b3860ecbb9d..a2543aacc179c 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -219,6 +219,16 @@ "description": [ "\nDefinition of the full event structure" ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "common", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-common.Event", + "text": "Event" + }, + "" + ], "path": "packages/analytics/client/src/events/types.ts", "deprecated": false, "children": [ @@ -249,14 +259,14 @@ { "parentPluginId": "@kbn/analytics-client", "id": "def-common.Event.properties", - "type": "Object", + "type": "Uncategorized", "tags": [], "label": "properties", "description": [ "\nThe specific properties of the event type." ], "signature": [ - "{ [x: string]: unknown; }" + "Properties" ], "path": "packages/analytics/client/src/events/types.ts", "deprecated": false @@ -547,7 +557,7 @@ "\nReports a telemetry event." ], "signature": [ - ">(eventType: string, eventData: EventTypeData) => void" + "(eventType: string, eventData: EventTypeData) => void" ], "path": "packages/analytics/client/src/analytics_client/types.ts", "deprecated": false, @@ -945,7 +955,7 @@ "section": "def-common.Event", "text": "Event" }, - "[]) => void" + ">[]) => void" ], "path": "packages/analytics/client/src/shippers/types.ts", "deprecated": false, @@ -967,7 +977,7 @@ "section": "def-common.Event", "text": "Event" }, - "[]" + ">[]" ], "path": "packages/analytics/client/src/shippers/types.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 7c082c659af50..a0c1b0cd70e5d 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-client plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.devdocs.json b/api_docs/kbn_analytics_shippers_elastic_v3_browser.devdocs.json index 904adc713d0e3..6e2887f902564 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.devdocs.json +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.devdocs.json @@ -203,7 +203,7 @@ "signature": [ "(events: ", "Event", - "[]) => void" + ">[]) => void" ], "path": "packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.ts", "deprecated": false, @@ -219,7 +219,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index c4c9e56f0c2b7..423c865416e0a 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.devdocs.json b/api_docs/kbn_analytics_shippers_elastic_v3_common.devdocs.json index 3ced4f235b0b9..cd7ecef5e5286 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.devdocs.json +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.devdocs.json @@ -227,7 +227,7 @@ "TelemetryCounter", ">, source: string) => (events: ", "Event", - "[], { type, code, error, }?: { type?: ", + ">[], { type, code, error, }?: { type?: ", "TelemetryCounterType", " | undefined; code?: string | undefined; error?: Error | undefined; }) => void" ], @@ -285,7 +285,7 @@ "signature": [ "(events: ", "Event", - "[]) => string" + ">[]) => string" ], "path": "packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.ts", "deprecated": false, @@ -301,7 +301,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 28c2d11c0e5b5..e1e0b1e68c8c4 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.devdocs.json b/api_docs/kbn_analytics_shippers_elastic_v3_server.devdocs.json index 981257e0fcd16..1d4721767610f 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.devdocs.json +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.devdocs.json @@ -195,7 +195,7 @@ "signature": [ "(events: ", "Event", - "[]) => void" + ">[]) => void" ], "path": "packages/analytics/shippers/elastic_v3/server/src/server_shipper.ts", "deprecated": false, @@ -211,7 +211,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "packages/analytics/shippers/elastic_v3/server/src/server_shipper.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index e7720f6263ec8..52449517c968d 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_fullstory.devdocs.json b/api_docs/kbn_analytics_shippers_fullstory.devdocs.json index 19cd61dd2651f..005302e9a1a7b 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.devdocs.json +++ b/api_docs/kbn_analytics_shippers_fullstory.devdocs.json @@ -191,7 +191,7 @@ "signature": [ "(events: ", "Event", - "[]) => void" + ">[]) => void" ], "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", "deprecated": false, @@ -207,7 +207,7 @@ ], "signature": [ "Event", - "[]" + ">[]" ], "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 7f431a9a02162..094c52d069655 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 551be5ca6faf9..a61fa87d21c75 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-config-loader plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 068ebda07dac9..fe5c77a30f79e 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 371a0f441008a..a7e24a009ca64 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/axe-config plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_bazel_packages.mdx b/api_docs/kbn_bazel_packages.mdx index b3c81463e3319..b72376cd65633 100644 --- a/api_docs/kbn_bazel_packages.mdx +++ b/api_docs/kbn_bazel_packages.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-packages title: "@kbn/bazel-packages" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/bazel-packages plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-packages'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index f1766dcb01c68..89ecddefbc25a 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-core plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 5d4876c7c410c..c4ca5ff472e25 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index f1a91956c8747..e6424911f5aa6 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-reporter plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 98c4878ddc4cc..59ac09cbe70fe 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/cli-dev-mode plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index d33693bd36c2e..ee8663c646e69 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/coloring plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 276608435772b..8e9977261de53 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 64656e4bbc026..6fbbe19f75a4c 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 76cef641fdea0..95f6adda0962a 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config-schema plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser.devdocs.json b/api_docs/kbn_core_analytics_browser.devdocs.json index c64c779b2034d..7cb2e58a1c1cf 100644 --- a/api_docs/kbn_core_analytics_browser.devdocs.json +++ b/api_docs/kbn_core_analytics_browser.devdocs.json @@ -34,7 +34,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -66,7 +66,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 8d7e432f32e4a..5153314e25aea 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index de0c4ee8e1289..77856ae65c8a1 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 3484294c68f4e..6426283795560 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server.devdocs.json b/api_docs/kbn_core_analytics_server.devdocs.json index d2f49935b878d..622c76c39b920 100644 --- a/api_docs/kbn_core_analytics_server.devdocs.json +++ b/api_docs/kbn_core_analytics_server.devdocs.json @@ -26,7 +26,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -58,7 +58,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", @@ -90,7 +90,7 @@ "signature": [ "{ optIn: (optInConfig: ", "OptInConfig", - ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", + ") => void; reportEvent: (eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ", "Observable", "<", "TelemetryCounter", diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index feb1dd8dd5cf3..410e15e3ffc68 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index e4ed17dfedd79..a1e055051bbfb 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 76a851dd16a39..e8d1814bb5150 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 58b7d5ea0032c..c6a76c065af7b 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index a56eff28e560f..00d95d4e2dd83 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 1cdd2ed2dbc18..b1bce63c32c7b 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 0c3b649d57972..20b8931a13050 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index b5c70a4c003cc..3143850c5f326 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-capabilities-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 11d966a3191e8..84bae56c2a66f 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-capabilities-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 84ccd4b086bee..692a6dfd7da84 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_config_server_internal.devdocs.json b/api_docs/kbn_core_config_server_internal.devdocs.json index da4e89e53775a..3b2226a89c2a0 100644 --- a/api_docs/kbn_core_config_server_internal.devdocs.json +++ b/api_docs/kbn_core_config_server_internal.devdocs.json @@ -78,170 +78,6 @@ ], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor", - "type": "Function", - "tags": [], - "label": "getDeprecationsFor", - "description": [], - "signature": [ - "({ provider, settings, path, }: { provider: ", - "ConfigDeprecationProvider", - "; settings?: Record | undefined; path: string; }) => { messages: string[]; levels: string[]; migrated: Record; }" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor.$1", - "type": "Object", - "tags": [], - "label": "{\n provider,\n settings = {},\n path,\n}", - "description": [], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor.$1.provider", - "type": "Function", - "tags": [], - "label": "provider", - "description": [], - "signature": [ - "(factory: ", - "ConfigDeprecationFactory", - ") => ", - "ConfigDeprecation", - "[]" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor.$1.provider.$1", - "type": "Object", - "tags": [], - "label": "factory", - "description": [], - "signature": [ - "ConfigDeprecationFactory" - ], - "path": "node_modules/@types/kbn__config/index.d.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor.$1.settings", - "type": "Object", - "tags": [], - "label": "settings", - "description": [], - "signature": [ - "Record | undefined" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsFor.$1.path", - "type": "string", - "tags": [], - "label": "path", - "description": [], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsForGlobalSettings", - "type": "Function", - "tags": [], - "label": "getDeprecationsForGlobalSettings", - "description": [], - "signature": [ - "({ provider, settings, }: { provider: ", - "ConfigDeprecationProvider", - "; settings?: Record | undefined; }) => { messages: string[]; levels: string[]; migrated: Record; }" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsForGlobalSettings.$1", - "type": "Object", - "tags": [], - "label": "{\n provider,\n settings = {},\n}", - "description": [], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsForGlobalSettings.$1.provider", - "type": "Function", - "tags": [], - "label": "provider", - "description": [], - "signature": [ - "(factory: ", - "ConfigDeprecationFactory", - ") => ", - "ConfigDeprecation", - "[]" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsForGlobalSettings.$1.provider.$1", - "type": "Object", - "tags": [], - "label": "factory", - "description": [], - "signature": [ - "ConfigDeprecationFactory" - ], - "path": "node_modules/@types/kbn__config/index.d.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/core-config-server-internal", - "id": "def-server.getDeprecationsForGlobalSettings.$1.settings", - "type": "Object", - "tags": [], - "label": "settings", - "description": [], - "signature": [ - "Record | undefined" - ], - "path": "packages/core/config/core-config-server-internal/src/test_utils.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [], diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index b5f099d6443b3..63d5c475d180b 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-config-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 15 | 0 | 13 | 0 | +| 4 | 0 | 4 | 0 | ## Server diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 5434f82d104ae..f1d3f068d7196 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-deprecations-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 07119eb704591..29caef8bfb516 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 6100abd823315..e5793b9460ea8 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 03ba55455bcb1..bca997153a79c 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-deprecations-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index a8abf884d3119..fd92aa59723b3 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 0fcd16e9245d7..070148d7b3005 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index b19c516159d75..e0feb6d381f16 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 8626ccdf9b06a..eba83a869baec 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 73e2d5f899af0..1417ac64698bd 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 67d26b2894766..dd895b144ddf8 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index bf0800804ec56..9111681b43b6b 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-elasticsearch-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 43afa319f0862..6413a41c90c85 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 66b1fec1185a5..8f5d444372ef8 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index cbf777d556606..c44939d3f61c9 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-environment-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 75ca0729e4c3c..66e0e5e8698a1 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-environment-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 9de1df05d3c51..f05874250d0e6 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index fa70cf1f5f615..d8f8ce79a821f 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index d4109b6be5a79..ba45072b73d59 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 0f8b0671f507a..c69ce77cffdd2 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index ddbb73758c9af..8e6ef3cd0d6f6 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index a587bb3eeb047..d081dc431f8f6 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 2d8bcc04f4bc8..f978823799417 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 58179f2be6fa8..680796eb5168e 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index edef3cb92ff5e..7e1ae8d667be4 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 882650258ff22..3ccb58070105e 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 070fc58c3bab1..a9f6228f4d092 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 7e05161533bf8..1b5a7ca28fb33 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 15e97a6bfc80e..fa5d89c035fa6 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index d0712c12e01b6..af25cbf2bd37a 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 7fdd28a7bfa3a..b91065065dd6b 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-http-router-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 9a57440173036..cfbef0f5d47c3 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index a83140390f324..d7ed0ddd9c7eb 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index d555464e1a2ab..b65c07014917b 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index e9cabc3ab741a..e3f527f7b9049 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index dfc83c93d9a3c..190936c4307c8 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-i18n-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 9753dda1e1d65..402ffd9889893 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_injected_metadata_browser.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 5a32ce33c9e0b..4685b9678c144 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 6026c7b7c9073..315d98a288204 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 66b23751de87d..3b75ce2d33d6f 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index d0270f6956c4f..bd8af601ca168 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 6a0579a2f28bb..1d7cc068cb487 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index ee379a1cca97b..1e3e4be341ec3 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index bb3fa29b48ca3..f1bae40daa273 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 278a2e2626eea..469d235c500ce 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 1d045ad232624..4ec5b703b9056 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 47717924ab2a0..693a19f9b793f 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-metrics-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 4c2c48ecf041c..c3c51523b750a 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-metrics-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 69ade150c409b..2d1365199a1d5 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 883da788e40f8..a85e7d941ab79 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-mount-utils-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_mount_utils_browser_internal.mdx b/api_docs/kbn_core_mount_utils_browser_internal.mdx index c6579ee4f42b4..8b3c609916a5c 100644 --- a/api_docs/kbn_core_mount_utils_browser_internal.mdx +++ b/api_docs/kbn_core_mount_utils_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser-internal title: "@kbn/core-mount-utils-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-mount-utils-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 241a16e29d734..417b502f7b618 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index c89854619b182..28140e9ad3493 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 567ae095ca5a7..79002bbb270c9 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 24ff2999ae9ae..f72bc47666d42 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-notifications-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 52351d371efef..0f649712833c4 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 9d1a8f5d4351f..901d3dfa0ccba 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 7c20aaa76f2ae..f199c7d261511 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-overlays-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 2abf70dd54c0a..b66128bbdebe5 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index f6d9fb1f73db8..71422a505359c 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 06e04eafd9b96..b1276f140df4b 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-preboot-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index bbff3b155da5e..6a3351c76d92a 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 5576782174bf1..be23b9a8df7d3 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 1f83b88ba10ae..d63fa9e44eafa 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_saved_objects_browser.devdocs.json b/api_docs/kbn_core_saved_objects_browser.devdocs.json new file mode 100644 index 0000000000000..7aa59ac3da7f7 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser.devdocs.json @@ -0,0 +1,56 @@ +{ + "id": "@kbn/core-saved-objects-browser", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser", + "id": "def-common.SavedObjectsStart", + "type": "Interface", + "tags": [], + "label": "SavedObjectsStart", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-browser/src/contracts.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser", + "id": "def-common.SavedObjectsStart.client", + "type": "Object", + "tags": [], + "label": "client", + "description": [ + "{@link SavedObjectsClientContract}" + ], + "signature": [ + "SavedObjectsClientContract" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser/src/contracts.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx new file mode 100644 index 0000000000000..a542705065f9a --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnCoreSavedObjectsBrowserPluginApi +slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser +title: "@kbn/core-saved-objects-browser" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/core-saved-objects-browser plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.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 | 1 | 0 | + +## Common + +### Interfaces + + diff --git a/api_docs/kbn_core_saved_objects_browser_internal.devdocs.json b/api_docs/kbn_core_saved_objects_browser_internal.devdocs.json new file mode 100644 index 0000000000000..a973d2292623d --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser_internal.devdocs.json @@ -0,0 +1,130 @@ +{ + "id": "@kbn/core-saved-objects-browser-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService", + "type": "Class", + "tags": [], + "label": "SavedObjectsService", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-browser-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBrowserInternalPluginApi", + "section": "def-common.SavedObjectsService", + "text": "SavedObjectsService" + }, + " implements ", + "CoreService", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "({ http }: { http: ", + "HttpSetup", + "; }) => Promise<", + "SavedObjectsStart", + ">" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService.start.$1", + "type": "Object", + "tags": [], + "label": "{ http }", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService.start.$1.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + "HttpSetup" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-browser-internal", + "id": "def-common.SavedObjectsService.stop", + "type": "Function", + "tags": [], + "label": "stop", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx new file mode 100644 index 0000000000000..5667aeecc328c --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnCoreSavedObjectsBrowserInternalPluginApi +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 +summary: API docs for the @kbn/core-saved-objects-browser-internal plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 6 | 0 | 6 | 0 | + +## Common + +### Classes + + diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.devdocs.json b/api_docs/kbn_core_saved_objects_browser_mocks.devdocs.json new file mode 100644 index 0000000000000..8c9a3906a2504 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser_mocks.devdocs.json @@ -0,0 +1,135 @@ +{ + "id": "@kbn/core-saved-objects-browser-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.savedObjectsServiceMock", + "type": "Object", + "tags": [], + "label": "savedObjectsServiceMock", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.savedObjectsServiceMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.Mocked" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.savedObjectsServiceMock.createStartContract", + "type": "Function", + "tags": [], + "label": "createStartContract", + "description": [], + "signature": [ + "() => jest.Mocked<", + "SavedObjectsStart", + ">" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.simpleSavedObjectMock", + "type": "Object", + "tags": [], + "label": "simpleSavedObjectMock", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.simpleSavedObjectMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "(client: ", + "SavedObjectsClientContract", + ", savedObject: ", + "SavedObject", + ") => ", + "SimpleSavedObjectImpl", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.simpleSavedObjectMock.create.$1", + "type": "Object", + "tags": [], + "label": "client", + "description": [], + "signature": [ + "SavedObjectsClientContract" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-browser-mocks", + "id": "def-common.simpleSavedObjectMock.create.$2", + "type": "Object", + "tags": [], + "label": "savedObject", + "description": [], + "signature": [ + "SavedObject", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts", + "deprecated": false + } + ] + } + ], + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx new file mode 100644 index 0000000000000..68a1868e0e1a0 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnCoreSavedObjectsBrowserMocksPluginApi +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 +summary: API docs for the @kbn/core-saved-objects-browser-mocks plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_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 | + +## Common + +### Objects + + diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 08d48240e8962..3b6f553c5e218 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-saved-objects-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 15521b3da2e4e..fff6ab110d5b2 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-saved-objects-server plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.devdocs.json b/api_docs/kbn_core_test_helpers_deprecations_getters.devdocs.json new file mode 100644 index 0000000000000..05089cba7a7ba --- /dev/null +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.devdocs.json @@ -0,0 +1,192 @@ +{ + "id": "@kbn/core-test-helpers-deprecations-getters", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor", + "type": "Function", + "tags": [], + "label": "getDeprecationsFor", + "description": [], + "signature": [ + "({ provider, settings, path, }: { provider: ", + "ConfigDeprecationProvider", + "; settings?: Record | undefined; path: string; }) => { messages: string[]; levels: string[]; migrated: Record; }" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor.$1", + "type": "Object", + "tags": [], + "label": "{\n provider,\n settings = {},\n path,\n}", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor.$1.provider", + "type": "Function", + "tags": [], + "label": "provider", + "description": [], + "signature": [ + "(factory: ", + "ConfigDeprecationFactory", + ") => ", + "ConfigDeprecation", + "[]" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor.$1.provider.$1", + "type": "Object", + "tags": [], + "label": "factory", + "description": [], + "signature": [ + "ConfigDeprecationFactory" + ], + "path": "node_modules/@types/kbn__config/index.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor.$1.settings", + "type": "Object", + "tags": [], + "label": "settings", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsFor.$1.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsForGlobalSettings", + "type": "Function", + "tags": [], + "label": "getDeprecationsForGlobalSettings", + "description": [], + "signature": [ + "({ provider, settings, }: { provider: ", + "ConfigDeprecationProvider", + "; settings?: Record | undefined; }) => { messages: string[]; levels: string[]; migrated: Record; }" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsForGlobalSettings.$1", + "type": "Object", + "tags": [], + "label": "{\n provider,\n settings = {},\n}", + "description": [], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsForGlobalSettings.$1.provider", + "type": "Function", + "tags": [], + "label": "provider", + "description": [], + "signature": [ + "(factory: ", + "ConfigDeprecationFactory", + ") => ", + "ConfigDeprecation", + "[]" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsForGlobalSettings.$1.provider.$1", + "type": "Object", + "tags": [], + "label": "factory", + "description": [], + "signature": [ + "ConfigDeprecationFactory" + ], + "path": "node_modules/@types/kbn__config/index.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/core-test-helpers-deprecations-getters", + "id": "def-server.getDeprecationsForGlobalSettings.$1.settings", + "type": "Object", + "tags": [], + "label": "settings", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "packages/core/test-helpers/core-test-helpers-deprecations-getters/src/deprecations_getters.ts", + "deprecated": false + } + ] + } + ], + "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_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx new file mode 100644 index 0000000000000..dff29e4cb66b6 --- /dev/null +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnCoreTestHelpersDeprecationsGettersPluginApi +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 +summary: API docs for the @kbn/core-test-helpers-deprecations-getters plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 11 | 0 | 9 | 0 | + +## Server + +### Functions + + 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 10fcdbdbfd91c..a7558d8857c87 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index baae698cc5207..28b0872847eb0 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index e0b54bcbd638c..1b79050bee746 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index cf06f6191aa58..60451d02c0846 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index aff6e83db56eb..c4a88f3ba555a 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-ui-settings-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index c9ecd46d30816..11d673ba71f47 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 7849e9b25e95f..925a473515bf9 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 93db56ddbcb48..c194896101be6 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-ui-settings-common plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 7084e71852107..f9004db68fcbc 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/crypto plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 39db8c06b6e44..e368ba2e49ea6 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/crypto-browser plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 2c1461d4ab516..b8cb4f92eb0cb 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/datemath plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 4a343e96afc60..7e86024c2df8d 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-cli-errors plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 9560e60ff411b..d3b7cdbc6e6f2 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-cli-runner plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index d2a6296e82af8..68b57693e1db6 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-proc-runner plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_utils.devdocs.json b/api_docs/kbn_dev_utils.devdocs.json index 6ff8061e664cf..d3b4ebd72fb2a 100644 --- a/api_docs/kbn_dev_utils.devdocs.json +++ b/api_docs/kbn_dev_utils.devdocs.json @@ -184,22 +184,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.runUpdateVscodeConfigCli", - "type": "Function", - "tags": [], - "label": "runUpdateVscodeConfigCli", - "description": [], - "signature": [ - "() => void" - ], - "path": "packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/dev-utils", "id": "def-server.shipCiStatsCli", diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 7e8b54a48fd49..67c90a4051b23 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 31 | 2 | 27 | 0 | +| 30 | 2 | 26 | 0 | ## Server diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 4545f6d369743..0d1affc4ab124 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/doc-links plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index e8b0b46689585..a181d1c42ffcd 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/docs-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ebt_tools.devdocs.json b/api_docs/kbn_ebt_tools.devdocs.json new file mode 100644 index 0000000000000..7f6e60305d982 --- /dev/null +++ b/api_docs/kbn_ebt_tools.devdocs.json @@ -0,0 +1,343 @@ +{ + "id": "@kbn/ebt-tools", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.registerPerformanceMetricEventType", + "type": "Function", + "tags": [ + "private" + ], + "label": "registerPerformanceMetricEventType", + "description": [ + "\nRegister the `performance_metric` event type" + ], + "signature": [ + "(analytics: Pick<", + "AnalyticsClient", + ", \"registerEventType\">) => void" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.registerPerformanceMetricEventType.$1", + "type": "Object", + "tags": [], + "label": "analytics", + "description": [ + "The {@link AnalyticsClient } during the setup phase (it has the method `registerEventType`)" + ], + "signature": [ + "Pick<", + "AnalyticsClient", + ", \"registerEventType\">" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.reportPerformanceMetricEvent", + "type": "Function", + "tags": [], + "label": "reportPerformanceMetricEvent", + "description": [ + "\nReport a `performance_metric` event type." + ], + "signature": [ + "(analytics: Pick<", + "AnalyticsClient", + ", \"reportEvent\">, eventData: ", + { + "pluginId": "@kbn/ebt-tools", + "scope": "common", + "docId": "kibKbnEbtToolsPluginApi", + "section": "def-common.PerformanceMetricEvent", + "text": "PerformanceMetricEvent" + }, + ") => void" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.reportPerformanceMetricEvent.$1", + "type": "Object", + "tags": [], + "label": "analytics", + "description": [ + "The {@link AnalyticsClient } to report the events." + ], + "signature": [ + "Pick<", + "AnalyticsClient", + ", \"reportEvent\">" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.reportPerformanceMetricEvent.$2", + "type": "Object", + "tags": [], + "label": "eventData", + "description": [ + "The data to send, conforming the structure of a {@link MetricEvent }." + ], + "signature": [ + { + "pluginId": "@kbn/ebt-tools", + "scope": "common", + "docId": "kibKbnEbtToolsPluginApi", + "section": "def-common.PerformanceMetricEvent", + "text": "PerformanceMetricEvent" + } + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent", + "type": "Interface", + "tags": [], + "label": "PerformanceMetricEvent", + "description": [ + "\nStructure of the `metric` event" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.eventName", + "type": "string", + "tags": [], + "label": "eventName", + "description": [ + "\nThe name of the event that is tracked in the metrics i.e. kibana_loaded, kibana_started" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [ + "\nSearchable but not aggregateable metadata relevant to the tracked action." + ], + "signature": [ + "Record | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.duration", + "type": "number", + "tags": [ + "group" + ], + "label": "duration", + "description": [], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.key1", + "type": "string", + "tags": [ + "group" + ], + "label": "key1", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.value1", + "type": "number", + "tags": [ + "group" + ], + "label": "value1", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.key2", + "type": "string", + "tags": [ + "group" + ], + "label": "key2", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.value2", + "type": "number", + "tags": [ + "group" + ], + "label": "value2", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.key3", + "type": "string", + "tags": [ + "group" + ], + "label": "key3", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.value3", + "type": "number", + "tags": [ + "group" + ], + "label": "value3", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.key4", + "type": "string", + "tags": [ + "group" + ], + "label": "key4", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.value4", + "type": "number", + "tags": [ + "group" + ], + "label": "value4", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.key5", + "type": "string", + "tags": [ + "group" + ], + "label": "key5", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ebt-tools", + "id": "def-common.PerformanceMetricEvent.value5", + "type": "number", + "tags": [ + "group" + ], + "label": "value5", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ebt-tools/src/performance_metric_events/schema.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx new file mode 100644 index 0000000000000..c60e85cc4a099 --- /dev/null +++ b/api_docs/kbn_ebt_tools.mdx @@ -0,0 +1,30 @@ +--- +id: kibKbnEbtToolsPluginApi +slug: /kibana-dev-docs/api/kbn-ebt-tools +title: "@kbn/ebt-tools" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/ebt-tools plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 19 | 0 | 11 | 0 | + +## Common + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 6b2b4da17a161..a3107704b5139 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-archiver plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 6a276ceb384b3..ac749cd05abe2 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-errors plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 557dc33c2af02..bf766e1c79cbf 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-query plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 359aed632fe6c..0865db882fafe 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/eslint-plugin-imports plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 2fec873fee110..cab4066790b07 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/field-types plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index b92fdac82eff3..f2e06aeb0b43c 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/find-used-node-modules plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 7b4108bb40541..600762dfd3321 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/generate plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index e8b977413a8a7..6f3dc4b2ff237 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/get-repo-files plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index b466bffda3ab6..f0238094c110b 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/handlebars plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 773b64b425d5b..b88b6ed869d69 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/hapi-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index d2c48ec563309..9f4cf0a9cceec 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/home-sample-data-card plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 769d3e4a41ea4..df7920375764d 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/home-sample-data-tab plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 9c691fa1cc589..a4bb763b8bb27 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/i18n plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index b2c3555b5d192..2cb4c592cf5d6 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/import-resolver plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 1df3d25ab7444..c807f19d3ce73 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/interpreter plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index d0a900500fa56..72a0349f98591 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/io-ts-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 4a5036d7f13d2..ef80e776abb49 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/jest-serializers plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_kibana_json_schema.devdocs.json b/api_docs/kbn_kibana_json_schema.devdocs.json deleted file mode 100644 index 0cc8a1e25feb5..0000000000000 --- a/api_docs/kbn_kibana_json_schema.devdocs.json +++ /dev/null @@ -1,844 +0,0 @@ -{ - "id": "@kbn/kibana-json-schema", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema", - "type": "Object", - "tags": [], - "label": "KibanaJsonSchema", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"object\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.required", - "type": "Array", - "tags": [], - "label": "required", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties", - "type": "Object", - "tags": [], - "label": "properties", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.id", - "type": "Object", - "tags": [], - "label": "id", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.id.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.id.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.id.pattern", - "type": "string", - "tags": [], - "label": "pattern", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.version", - "type": "Object", - "tags": [], - "label": "version", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.version.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.version.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.version.pattern", - "type": "string", - "tags": [], - "label": "pattern", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.kibanaVersion", - "type": "Object", - "tags": [], - "label": "kibanaVersion", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.pattern", - "type": "string", - "tags": [], - "label": "pattern", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.type", - "type": "Object", - "tags": [], - "label": "type", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.type.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.type.enum", - "type": "Array", - "tags": [], - "label": "enum", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.configPath", - "type": "Object", - "tags": [], - "label": "configPath", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.configPath.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.configPath.oneOf", - "type": "Array", - "tags": [], - "label": "oneOf", - "description": [], - "signature": [ - "({ type: \"string\"; } | { type: \"array\"; items: { type: \"string\"; }; })[]" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredPlugins", - "type": "Object", - "tags": [], - "label": "requiredPlugins", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.items", - "type": "Object", - "tags": [], - "label": "items", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.items.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.optionalPlugins", - "type": "Object", - "tags": [], - "label": "optionalPlugins", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.items", - "type": "Object", - "tags": [], - "label": "items", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.items.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredBundles", - "type": "Object", - "tags": [], - "label": "requiredBundles", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredBundles.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredBundles.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredBundles.items", - "type": "Object", - "tags": [], - "label": "items", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.requiredBundles.items.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.ui", - "type": "Object", - "tags": [], - "label": "ui", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.ui.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.ui.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"boolean\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.server", - "type": "Object", - "tags": [], - "label": "server", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.server.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.server.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"boolean\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs", - "type": "Object", - "tags": [], - "label": "extraPublicDirs", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.items", - "type": "Object", - "tags": [], - "label": "items", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.items.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.serviceFolders", - "type": "Object", - "tags": [], - "label": "serviceFolders", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.serviceFolders.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.serviceFolders.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.serviceFolders.items", - "type": "Object", - "tags": [], - "label": "items", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.serviceFolders.items.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner", - "type": "Object", - "tags": [], - "label": "owner", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"object\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.required", - "type": "Array", - "tags": [], - "label": "required", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties", - "type": "Object", - "tags": [], - "label": "properties", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.name", - "type": "Object", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.name.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.name.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam", - "type": "Object", - "tags": [], - "label": "githubTeam", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.description", - "type": "Object", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.description.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.description.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"string\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages", - "type": "Object", - "tags": [], - "label": "enabledOnAnonymousPages", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/kibana-json-schema", - "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"boolean\"" - ], - "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", - "deprecated": false - } - ] - } - ] - } - ], - "initialIsOpen": false - } - ] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/kbn_kibana_manifest_parser.devdocs.json b/api_docs/kbn_kibana_manifest_parser.devdocs.json new file mode 100644 index 0000000000000..69af5a03b1498 --- /dev/null +++ b/api_docs/kbn_kibana_manifest_parser.devdocs.json @@ -0,0 +1,167 @@ +{ + "id": "@kbn/kibana-manifest-parser", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.parseKibanaManifest", + "type": "Function", + "tags": [], + "label": "parseKibanaManifest", + "description": [ + "\nParse a kibana.jsonc file from a string" + ], + "signature": [ + "(content: string) => ", + { + "pluginId": "@kbn/kibana-manifest-parser", + "scope": "server", + "docId": "kibKbnKibanaManifestParserPluginApi", + "section": "def-server.KibanaPackageManifest", + "text": "KibanaPackageManifest" + } + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.parseKibanaManifest.$1", + "type": "string", + "tags": [], + "label": "content", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.readKibanaManifest", + "type": "Function", + "tags": [], + "label": "readKibanaManifest", + "description": [ + "\nParse a kibana.jsonc file from the filesystem" + ], + "signature": [ + "(path: string) => ", + { + "pluginId": "@kbn/kibana-manifest-parser", + "scope": "server", + "docId": "kibKbnKibanaManifestParserPluginApi", + "section": "def-server.KibanaPackageManifest", + "text": "KibanaPackageManifest" + } + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.readKibanaManifest.$1", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.validateKibanaManifest", + "type": "Function", + "tags": [], + "label": "validateKibanaManifest", + "description": [ + "\nValidate the contents of a parsed kibana.jsonc file." + ], + "signature": [ + "(parsed: unknown) => ", + { + "pluginId": "@kbn/kibana-manifest-parser", + "scope": "server", + "docId": "kibKbnKibanaManifestParserPluginApi", + "section": "def-server.KibanaPackageManifest", + "text": "KibanaPackageManifest" + } + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.validateKibanaManifest.$1", + "type": "Unknown", + "tags": [], + "label": "parsed", + "description": [], + "signature": [ + "unknown" + ], + "path": "packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/kibana-manifest-parser", + "id": "def-server.KibanaPackageManifest", + "type": "Type", + "tags": [], + "label": "KibanaPackageManifest", + "description": [], + "signature": [ + "PluginPackageManifest", + " | ", + "SharedBrowserPackageManifest", + " | ", + "BasePackageManifest" + ], + "path": "packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_kibana_manifest_parser.mdx b/api_docs/kbn_kibana_manifest_parser.mdx new file mode 100644 index 0000000000000..aca8f6337063e --- /dev/null +++ b/api_docs/kbn_kibana_manifest_parser.mdx @@ -0,0 +1,30 @@ +--- +id: kibKbnKibanaManifestParserPluginApi +slug: /kibana-dev-docs/api/kbn-kibana-manifest-parser +title: "@kbn/kibana-manifest-parser" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/kibana-manifest-parser plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-parser'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnKibanaManifestParserObj from './kbn_kibana_manifest_parser.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 7 | 0 | 4 | 3 | + +## Server + +### Functions + + +### Consts, variables and types + + diff --git a/api_docs/kbn_kibana_manifest_schema.devdocs.json b/api_docs/kbn_kibana_manifest_schema.devdocs.json new file mode 100644 index 0000000000000..6f93a8e7f8851 --- /dev/null +++ b/api_docs/kbn_kibana_manifest_schema.devdocs.json @@ -0,0 +1,1024 @@ +{ + "id": "@kbn/kibana-manifest-schema", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1", + "type": "Object", + "tags": [], + "label": "MANIFEST_V1", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.required", + "type": "Array", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.id", + "type": "Object", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.id.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.id.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.id.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.version", + "type": "Object", + "tags": [], + "label": "version", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.version.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.version.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.version.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.kibanaVersion", + "type": "Object", + "tags": [], + "label": "kibanaVersion", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.kibanaVersion.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.kibanaVersion.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.kibanaVersion.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.type", + "type": "Object", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.type.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.type.enum", + "type": "Array", + "tags": [], + "label": "enum", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.configPath", + "type": "Object", + "tags": [], + "label": "configPath", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.configPath.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.configPath.oneOf", + "type": "Array", + "tags": [], + "label": "oneOf", + "description": [], + "signature": [ + "({ type: string; } | { type: string; items: { type: string; }; })[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredPlugins", + "type": "Object", + "tags": [], + "label": "requiredPlugins", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredPlugins.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredPlugins.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredPlugins.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredPlugins.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.optionalPlugins", + "type": "Object", + "tags": [], + "label": "optionalPlugins", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.optionalPlugins.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.optionalPlugins.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.optionalPlugins.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.optionalPlugins.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredBundles", + "type": "Object", + "tags": [], + "label": "requiredBundles", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredBundles.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredBundles.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredBundles.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.requiredBundles.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.ui", + "type": "Object", + "tags": [], + "label": "ui", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.ui.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.ui.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.server", + "type": "Object", + "tags": [], + "label": "server", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.server.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.server.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.extraPublicDirs", + "type": "Object", + "tags": [], + "label": "extraPublicDirs", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.extraPublicDirs.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.extraPublicDirs.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.extraPublicDirs.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.extraPublicDirs.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.serviceFolders", + "type": "Object", + "tags": [], + "label": "serviceFolders", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.serviceFolders.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.serviceFolders.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.serviceFolders.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.serviceFolders.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner", + "type": "Object", + "tags": [], + "label": "owner", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.required", + "type": "Array", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.name", + "type": "Object", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.name.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.name.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.githubTeam", + "type": "Object", + "tags": [], + "label": "githubTeam", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.githubTeam.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.owner.properties.githubTeam.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.description", + "type": "Object", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.description.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.description.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.enabledOnAnonymousPages", + "type": "Object", + "tags": [], + "label": "enabledOnAnonymousPages", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.enabledOnAnonymousPages.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V1.properties.enabledOnAnonymousPages.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts", + "deprecated": false + } + ] + } + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2", + "type": "Object", + "tags": [], + "label": "MANIFEST_V2", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.required", + "type": "Array", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.id", + "type": "Object", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.id.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.id.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.owner", + "type": "Object", + "tags": [], + "label": "owner", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.owner.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.owner.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.owner.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.typeDependencies", + "type": "Object", + "tags": [], + "label": "typeDependencies", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.typeDependencies.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.typeDependencies.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.typeDependencies.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.typeDependencies.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.runtimeDependencies", + "type": "Object", + "tags": [], + "label": "runtimeDependencies", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.properties.runtimeDependencies.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-server.MANIFEST_V2.oneOf", + "type": "Array", + "tags": [], + "label": "oneOf", + "description": [], + "signature": [ + "({ type: string; properties: { type: { enum: string[]; }; plugin: { type: string; required: string[]; properties: { id: { type: string; pattern: string; }; configPath: { description: string; type: string; items: { type: string; pattern: string; }; }; requiredPlugins: { type: string; items: { type: string; pattern: string; }; }; optionalPlugins: { type: string; items: { type: string; pattern: string; }; }; description: { description: string; type: string; }; enabledOnAnonymousPages: { description: string; type: string; }; serviceFolders: { description: string; type: string; items: { type: string; }; }; }; }; }; } | { type: string; properties: { type: { const: string; }; sharedBrowserBundle: { type: string; description: string; }; }; } | { type: string; properties: { type: { enum: string[]; }; }; })[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_kibana_json_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx similarity index 50% rename from api_docs/kbn_kibana_json_schema.mdx rename to api_docs/kbn_kibana_manifest_schema.mdx index 83953aab2ecfe..9fd9b442a44ec 100644 --- a/api_docs/kbn_kibana_json_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -1,14 +1,14 @@ --- -id: kibKbnKibanaJsonSchemaPluginApi -slug: /kibana-dev-docs/api/kbn-kibana-json-schema -title: "@kbn/kibana-json-schema" +id: kibKbnKibanaManifestSchemaPluginApi +slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema +title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github -summary: API docs for the @kbn/kibana-json-schema plugin -date: 2022-08-03 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-json-schema'] +summary: API docs for the @kbn/kibana-manifest-schema plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- -import kbnKibanaJsonSchemaObj from './kbn_kibana_json_schema.devdocs.json'; +import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; @@ -18,10 +18,10 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 69 | 0 | 69 | 0 | +| 91 | 0 | 91 | 0 | ## Server ### Objects - + diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index e0ba4e37c3900..dd24a7c53a95a 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index e689e5b7afb7f..93fd455f8626d 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_managed_vscode_config.devdocs.json b/api_docs/kbn_managed_vscode_config.devdocs.json new file mode 100644 index 0000000000000..8a40ed80ca04a --- /dev/null +++ b/api_docs/kbn_managed_vscode_config.devdocs.json @@ -0,0 +1,130 @@ +{ + "id": "@kbn/managed-vscode-config", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.updateVscodeConfig", + "type": "Function", + "tags": [], + "label": "updateVscodeConfig", + "description": [ + "\nUpdate the settings.json file used by VSCode in the Kibana repository. If the file starts\nwith the comment \"// self managed\" then it is not touched. If a top-level keys is prefixed with\n`// self managed` then all the properties of that setting are left untouched. And finally, if\na specific child property of a setting like `search.exclude` is prefixed with `// self managed`\nthen it is left untouched.\n\nWe don't just use `JSON.parse()` and `JSON.stringify()` in order to support this customization and\nalso to support users using comments in this file, which is very useful for temporarily disabling settings.\n\nAfter the config file is updated it is formatted with prettier.\n" + ], + "signature": [ + "(keys: ", + "ManagedConfigKey", + "[], infoText: string, json: string | undefined) => string" + ], + "path": "packages/kbn-managed-vscode-config/src/update_vscode_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.updateVscodeConfig.$1", + "type": "Array", + "tags": [], + "label": "keys", + "description": [ + "The config keys which are managed" + ], + "signature": [ + "ManagedConfigKey", + "[]" + ], + "path": "packages/kbn-managed-vscode-config/src/update_vscode_config.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.updateVscodeConfig.$2", + "type": "string", + "tags": [], + "label": "infoText", + "description": [ + "The text which should be written to the top of the file to educate users how to customize the settings" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-managed-vscode-config/src/update_vscode_config.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.updateVscodeConfig.$3", + "type": "string", + "tags": [], + "label": "json", + "description": [ + "The settings file as a string" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-managed-vscode-config/src/update_vscode_config.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.MANAGED_CONFIG_FILES", + "type": "Array", + "tags": [], + "label": "MANAGED_CONFIG_FILES", + "description": [], + "signature": [ + "{ name: string; content: string; }[]" + ], + "path": "packages/kbn-managed-vscode-config/src/managed_config_keys.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/managed-vscode-config", + "id": "def-server.MANAGED_CONFIG_KEYS", + "type": "Array", + "tags": [], + "label": "MANAGED_CONFIG_KEYS", + "description": [ + "\nDefines the keys which we overrite in user's vscode config for the workspace. We currently\nonly support object values because that's all we needed to support, but support for non object\nvalues should be easy to add." + ], + "signature": [ + "ManagedConfigKey", + "[]" + ], + "path": "packages/kbn-managed-vscode-config/src/managed_config_keys.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx new file mode 100644 index 0000000000000..35339e96006f8 --- /dev/null +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -0,0 +1,30 @@ +--- +id: kibKbnManagedVscodeConfigPluginApi +slug: /kibana-dev-docs/api/kbn-managed-vscode-config +title: "@kbn/managed-vscode-config" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/managed-vscode-config plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 6 | 0 | 1 | 1 | + +## Server + +### Functions + + +### Consts, variables and types + + diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 32ec13ef17914..26c6934ea668e 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/mapbox-gl plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 6972b3c792329..1e4dfc6e9ae81 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-agg-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index bf322145f5d97..07f41d04f6d6b 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-is-populated-object plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 2fe1937dab8a5..a706b10098cbc 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-string-hash plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 0b88545ae5c1f..030ee367617f5 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/monaco plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 6fc438aef3842..da0b717882761 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/optimizer plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index da9a21c3d2a6c..c425df126fff8 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index af8c9adcf68f9..3665225852659 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index dac3bfca7e055..7c126a8137e20 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-generator plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index c031128076226..cdbaccd79fbe0 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-helpers plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index abbc63c896fe0..0dfea2497ac10 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/react-field plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 3c0c18df90330..87b273893a57b 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/repo-source-classifier plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 041c3b1a43118..35058a11e94e1 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/rule-data-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_scalability_simulation_generator.devdocs.json b/api_docs/kbn_scalability_simulation_generator.devdocs.json deleted file mode 100644 index e2fef53729609..0000000000000 --- a/api_docs/kbn_scalability_simulation_generator.devdocs.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "id": "@kbn/scalability-simulation-generator", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [ - { - "parentPluginId": "@kbn/scalability-simulation-generator", - "id": "def-server.generateScalabilitySimulations", - "type": "Function", - "tags": [], - "label": "generateScalabilitySimulations", - "description": [], - "signature": [ - "() => Promise" - ], - "path": "packages/kbn-scalability-simulation-generator/src/cli.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/scalability-simulation-generator", - "id": "def-server.generator", - "type": "Function", - "tags": [], - "label": "generator", - "description": [], - "signature": [ - "({ dir, baseUrl, packageName, log }: ", - "CLIParams", - ") => Promise" - ], - "path": "packages/kbn-scalability-simulation-generator/src/generate_files.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/scalability-simulation-generator", - "id": "def-server.generator.$1", - "type": "Object", - "tags": [], - "label": "{ dir, baseUrl, packageName, log }", - "description": [], - "signature": [ - "CLIParams" - ], - "path": "packages/kbn-scalability-simulation-generator/src/generate_files.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/kbn_scalability_simulation_generator.mdx b/api_docs/kbn_scalability_simulation_generator.mdx deleted file mode 100644 index f099bf29646c1..0000000000000 --- a/api_docs/kbn_scalability_simulation_generator.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -id: kibKbnScalabilitySimulationGeneratorPluginApi -slug: /kibana-dev-docs/api/kbn-scalability-simulation-generator -title: "@kbn/scalability-simulation-generator" -image: https://source.unsplash.com/400x175/?github -summary: API docs for the @kbn/scalability-simulation-generator plugin -date: 2022-08-03 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/scalability-simulation-generator'] -warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. ---- -import kbnScalabilitySimulationGeneratorObj from './kbn_scalability_simulation_generator.devdocs.json'; - -A library to generate scalability benchmarking simulation files from APM traces. - -Contact [Owner missing] for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 3 | 0 | 3 | 1 | - -## Server - -### Functions - - diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 1612397d0a286..1524979e7f658 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 1e4c1029e5c78..c5fffeb1a7a7d 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-es-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index e49b5d189b39e..4d38077a5e0f8 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index be7e5479ff367..cfa2f3fa35c3a 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 43be069053d8f..753b38f87251e 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index aa3c9cbfde9db..8b52bb4632100 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index bafc1b7611874..0772a1d985707 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index e5da490c3aeb3..04cd40dd5a197 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-api plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 0e9e291928646..067c5da768ecf 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-constants plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 367b5e791da07..b777044322169 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 706f2d6e8e08b..43b8c84c07c51 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index f0718fcd501df..2dcccba55cdd2 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-rules plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 4d62e59bf5cd7..d13adf5392eda 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-t-grid plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 72fff1c45738b..885de701a8211 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 5276f235567b7..88603ac5d64b1 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-http-tools plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 1151f310a847a..16a8229e33830 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-route-repository plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_svg.devdocs.json b/api_docs/kbn_shared_svg.devdocs.json new file mode 100644 index 0000000000000..264d5fb254209 --- /dev/null +++ b/api_docs/kbn_shared_svg.devdocs.json @@ -0,0 +1,50 @@ +{ + "id": "@kbn/shared-svg", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/shared-svg", + "id": "def-common.content", + "type": "string", + "tags": [], + "label": "content", + "description": [], + "path": "node_modules/@kbn/ambient-ui-types/index.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-svg", + "id": "def-common.content", + "type": "string", + "tags": [], + "label": "content", + "description": [], + "path": "node_modules/@kbn/ambient-ui-types/index.d.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx new file mode 100644 index 0000000000000..4349736b252e1 --- /dev/null +++ b/api_docs/kbn_shared_svg.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnSharedSvgPluginApi +slug: /kibana-dev-docs/api/kbn-shared-svg +title: "@kbn/shared-svg" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/shared-svg plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 0 | 0 | + +## Common + +### Consts, variables and types + + 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 604ef41cb2743..b32e59b7c2878 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 @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index cd185fa16b250..080ba411e5867 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 3d928f793ff22..3cb4091cb5142 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- 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 ba871afe1166f..86589552d9e82 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_components.mdx b/api_docs/kbn_shared_ux_components.mdx index 593e709762a78..e6cdab4d9abac 100644 --- a/api_docs/kbn_shared_ux_components.mdx +++ b/api_docs/kbn_shared_ux_components.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-components title: "@kbn/shared-ux-components" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-components plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-components'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- 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 01245802791e9..dba90d4f832eb 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 5cacff36e009c..faa962efeaad1 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- 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 dab5e6114ccc5..9c68223a23b13 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 @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 7ce7b9fce2ffb..ea7223613dbcd 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- 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 b06114dfded4d..f7d4e15b40701 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 @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index cf20d260b12a0..888a1132c18ce 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 357a8d91f914a..86e72ceef7f69 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index b632505a8d25e..86029119d6a48 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 @@ -4,7 +4,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 summary: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_services.mdx b/api_docs/kbn_shared_ux_services.mdx index 394dd12bbecb9..de7fc465bfc3a 100644 --- a/api_docs/kbn_shared_ux_services.mdx +++ b/api_docs/kbn_shared_ux_services.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-services title: "@kbn/shared-ux-services" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-services plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-services'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_storybook.mdx b/api_docs/kbn_shared_ux_storybook.mdx index 7cd8bf637738d..d2920bae12110 100644 --- a/api_docs/kbn_shared_ux_storybook.mdx +++ b/api_docs/kbn_shared_ux_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook title: "@kbn/shared-ux-storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-storybook plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 58f4c124fee17..22f76b2c4a1ee 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 8fe615f189494..0a7df2085d99b 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-utility plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index ca728e5a9d31f..8cf128f2dd76d 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/some-dev-log plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 8e4e5a3e975f1..a16656e0b3525 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/sort-package-json plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 5a9aa4302d18a..7db268d57d2b0 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/std plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index af5ddf99f6290..e7018eae50eb5 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/stdio-dev-helpers plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index e88a366eed917..76bee02c28b90 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/storybook plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 48281d80cb61c..c3cb55c301c3a 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/telemetry-tools plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 1030e6905f9ef..ca0f3e4401e79 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 5d702dd4cb952..879217657c2c9 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test-jest-helpers plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index ed2d73b1766de..ad96e87f675fe 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/tooling-log plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index a5c6be055b4ed..c2d0c179ebb3b 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/type-summarizer plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index ab4bc6232e840..92676ede7f1ce 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/type-summarizer-core plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 6caf18c0f4170..7ab8071ab1924 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/typed-react-router-config plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 6a206384dd6f0..d6381bf0fd24d 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ui-theme plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_user_profile_components.devdocs.json b/api_docs/kbn_user_profile_components.devdocs.json new file mode 100644 index 0000000000000..cf2e0482a54cc --- /dev/null +++ b/api_docs/kbn_user_profile_components.devdocs.json @@ -0,0 +1,805 @@ +{ + "id": "@kbn/user-profile-components", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatar", + "type": "Function", + "tags": [], + "label": "UserAvatar", + "description": [ + "\nRenders an avatar given a user profile" + ], + "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.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatar.$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.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesPopover", + "type": "Function", + "tags": [], + "label": "UserProfilesPopover", + "description": [ + "\nRenders a selectable component inside a popover given a list of user profiles" + ], + "signature": [ + "({ title, selectableProps, ...popoverProps }: React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesPopoverProps", + "text": "UserProfilesPopoverProps" + }, + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_popover.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesPopover.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n title,\n selectableProps,\n ...popoverProps\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesPopoverProps", + "text": "UserProfilesPopoverProps" + }, + ">" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_popover.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectable", + "type": "Function", + "tags": [], + "label": "UserProfilesSelectable", + "description": [ + "\nRenders a selectable component given a list of user profiles" + ], + "signature": [ + "({ selectedOptions, defaultOptions, options, onChange, onSearchChange, isLoading, singleSelection, height, loadingMessage, noMatchesMessage, emptyMessage, errorMessage, searchPlaceholder, selectedStatusMessage, clearButtonLabel, }: React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesSelectableProps", + "text": "UserProfilesSelectableProps" + }, + ">) => JSX.Element" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectable.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n selectedOptions,\n defaultOptions,\n options,\n onChange,\n onSearchChange,\n isLoading = false,\n singleSelection = false,\n height,\n loadingMessage,\n noMatchesMessage,\n emptyMessage,\n errorMessage,\n searchPlaceholder,\n selectedStatusMessage,\n clearButtonLabel,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesSelectableProps", + "text": "UserProfilesSelectableProps" + }, + ">" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatarProps", + "type": "Interface", + "tags": [], + "label": "UserAvatarProps", + "description": [ + "\nProps of {@link UserAvatar} component" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserAvatarProps", + "text": "UserAvatarProps" + }, + " extends Omit<", + "EuiAvatarProps", + ", \"type\" | \"color\" | \"name\" | \"iconColor\" | \"iconType\" | \"iconSize\" | \"initials\" | \"initialsLength\" | \"imageUrl\">" + ], + "path": "packages/kbn-user-profile-components/src/user_avatar.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatarProps.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" + }, + " | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_avatar.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserAvatarProps.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_avatar.tsx", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfile", + "type": "Interface", + "tags": [], + "label": "UserProfile", + "description": [ + "\nIMPORTANT:\n\nThe types in this file have been imported from\n`x-pack/plugins/security/common/model/user_profile.ts`\n\nWhen making changes please ensure to keep both files in sync.\n\nDescribes basic properties stored in user profile." + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfile.uid", + "type": "string", + "tags": [], + "label": "uid", + "description": [ + "\nUnique ID for of the user profile." + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfile.user", + "type": "Object", + "tags": [], + "label": "user", + "description": [ + "\nInformation about the user that owns profile." + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileUserInfo", + "text": "UserProfileUserInfo" + } + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfile.data", + "type": "Object", + "tags": [], + "label": "data", + "description": [ + "\nUser specific data associated with the profile." + ], + "signature": [ + "{ [P in keyof D]?: D[P] | undefined; }" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileAvatarData", + "type": "Interface", + "tags": [], + "label": "UserProfileAvatarData", + "description": [ + "\nAvatar stored in user profile." + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileAvatarData.initials", + "type": "string", + "tags": [], + "label": "initials", + "description": [ + "\nOptional initials (two letters) of the user to use as avatar if avatar picture isn't specified." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileAvatarData.color", + "type": "string", + "tags": [], + "label": "color", + "description": [ + "\nBackground color of the avatar when initials are used." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileAvatarData.imageUrl", + "type": "string", + "tags": [], + "label": "imageUrl", + "description": [ + "\nBase64 data URL for the user avatar image." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesPopoverProps", + "type": "Interface", + "tags": [], + "label": "UserProfilesPopoverProps", + "description": [ + "\nProps of {@link UserProfilesPopover} component" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesPopoverProps", + "text": "UserProfilesPopoverProps" + }, + " extends ", + "EuiPopoverProps" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_popover.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesPopoverProps.title", + "type": "CompoundType", + "tags": [ + "see" + ], + "label": "title", + "description": [ + "\nTitle of the popover" + ], + "signature": [ + "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_popover.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesPopoverProps.selectableProps", + "type": "Object", + "tags": [ + "see" + ], + "label": "selectableProps", + "description": [ + "\nProps forwarded to selectable component" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesSelectableProps", + "text": "UserProfilesSelectableProps" + } + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_popover.tsx", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps", + "type": "Interface", + "tags": [], + "label": "UserProfilesSelectableProps", + "description": [ + "\nProps of {@link UserProfilesSelectable} component" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfilesSelectableProps", + "text": "UserProfilesSelectableProps" + }, + " extends Pick<", + "EuiSelectableProps", + "<{}>, \"height\" | \"errorMessage\" | \"singleSelection\" | \"loadingMessage\" | \"noMatchesMessage\" | \"emptyMessage\">" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.defaultOptions", + "type": "Array", + "tags": [], + "label": "defaultOptions", + "description": [ + "\nList of users to be rendered as suggestions." + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileWithAvatar", + "text": "UserProfileWithAvatar" + }, + "[] | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.selectedOptions", + "type": "Array", + "tags": [], + "label": "selectedOptions", + "description": [ + "\nList of selected users." + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileWithAvatar", + "text": "UserProfileWithAvatar" + }, + "[] | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [ + "\nList of users from search results. Should be updated based on the search term provided by `onSearchChange` callback." + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileWithAvatar", + "text": "UserProfileWithAvatar" + }, + "[] | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.onChange", + "type": "Function", + "tags": [], + "label": "onChange", + "description": [ + "\nPasses back the list of selected users." + ], + "signature": [ + "((options: ", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileWithAvatar", + "text": "UserProfileWithAvatar" + }, + "[]) => void) | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.onChange.$1", + "type": "Array", + "tags": [], + "label": "options", + "description": [ + "List of selected users" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileWithAvatar", + "text": "UserProfileWithAvatar" + }, + "[]" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.onSearchChange", + "type": "Function", + "tags": [], + "label": "onSearchChange", + "description": [ + "\nPasses back the search term." + ], + "signature": [ + "((searchTerm: string) => void) | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.onSearchChange.$1", + "type": "string", + "tags": [], + "label": "searchTerm", + "description": [ + "Search term" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.isLoading", + "type": "CompoundType", + "tags": [], + "label": "isLoading", + "description": [ + "\nLoading indicator for asynchronous search operations." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.searchPlaceholder", + "type": "string", + "tags": [], + "label": "searchPlaceholder", + "description": [ + "\nPlaceholder text for search box." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.selectedStatusMessage", + "type": "Function", + "tags": [], + "label": "selectedStatusMessage", + "description": [ + "\nReturns text for selected status." + ], + "signature": [ + "((selectedCount: number) => React.ReactNode) | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.selectedStatusMessage.$1", + "type": "number", + "tags": [], + "label": "selectedCount", + "description": [ + "Number of selected users" + ], + "signature": [ + "number" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfilesSelectableProps.clearButtonLabel", + "type": "CompoundType", + "tags": [], + "label": "clearButtonLabel", + "description": [ + "\nText for label of clear button." + ], + "signature": [ + "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profiles_selectable.tsx", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileUserInfo", + "type": "Interface", + "tags": [], + "label": "UserProfileUserInfo", + "description": [ + "\nBasic user information returned in user profile." + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileUserInfo.username", + "type": "string", + "tags": [], + "label": "username", + "description": [ + "\nUsername of the user." + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileUserInfo.email", + "type": "string", + "tags": [], + "label": "email", + "description": [ + "\nOptional email of the user." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileUserInfo.full_name", + "type": "string", + "tags": [], + "label": "full_name", + "description": [ + "\nOptional full name of the user." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileUserInfo.display_name", + "type": "string", + "tags": [], + "label": "display_name", + "description": [ + "\nOptional display name of the user." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-user-profile-components/src/user_profile.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/user-profile-components", + "id": "def-common.UserProfileWithAvatar", + "type": "Type", + "tags": [], + "label": "UserProfileWithAvatar", + "description": [ + "\nConvenience type for a {@link UserProfile} with avatar data" + ], + "signature": [ + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "<{ avatar?: ", + { + "pluginId": "@kbn/user-profile-components", + "scope": "common", + "docId": "kibKbnUserProfileComponentsPluginApi", + "section": "def-common.UserProfileAvatarData", + "text": "UserProfileAvatarData" + }, + " | undefined; }>" + ], + "path": "packages/kbn-user-profile-components/src/user_avatar.tsx", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx new file mode 100644 index 0000000000000..6b241c74faec3 --- /dev/null +++ b/api_docs/kbn_user_profile_components.mdx @@ -0,0 +1,33 @@ +--- +id: kibKbnUserProfileComponentsPluginApi +slug: /kibana-dev-docs/api/kbn-user-profile-components +title: "@kbn/user-profile-components" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/user-profile-components plugin +date: 2022-08-08 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 39 | 0 | 3 | 0 | + +## Common + +### Functions + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 05cce1bcc32ff..5a4b4a905b479 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utility-types plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 1ceaa666f67a8..76fc0a8449fdb 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utility-types-jest plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index bbd59ac0847a0..04d581660d6c5 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index e81df082b4ba3..0c35776cf6638 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/yarn-lock-validator plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 1009b34616b9d..aaa17e38ad8ea 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaOverview plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index d0fe7574aed78..5bd2da953db8b 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -4115,13 +4115,7 @@ " | undefined; http?: ", "HttpSetup", " | undefined; savedObjects?: ", - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - }, + "SavedObjectsStart", " | undefined; i18n?: ", "I18nStart", " | undefined; notifications?: ", diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index d9f3423b3d556..40c0c4c0878bc 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaReact plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 304f60affdc07..1309c3a0dac66 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaUtils plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 1d3408efebcbd..a5292a5795ac3 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github summary: API docs for the kubernetesSecurity plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index a969b7b0a2c2a..43917b42f5d61 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -1462,7 +1462,7 @@ "tags": [], "label": "DatatableVisualizationState", "description": [], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false, "children": [ { @@ -1476,7 +1476,7 @@ "ColumnState", "[]" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1486,7 +1486,7 @@ "tags": [], "label": "layerId", "description": [], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1499,7 +1499,7 @@ "signature": [ "\"data\" | \"referenceLine\" | \"annotations\"" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1513,7 +1513,7 @@ "SortingState", " | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1526,7 +1526,7 @@ "signature": [ "\"auto\" | \"custom\" | \"single\" | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1539,7 +1539,7 @@ "signature": [ "\"auto\" | \"custom\" | \"single\" | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1552,7 +1552,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1565,7 +1565,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false }, { @@ -1579,7 +1579,7 @@ "PagingState", " | undefined" ], - "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", + "path": "x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx", "deprecated": false } ], @@ -4835,6 +4835,55 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.renderDimensionEditorAdditionalSection", + "type": "Function", + "tags": [], + "label": "renderDimensionEditorAdditionalSection", + "description": [ + "\nAdditional editor that gets rendered inside the dimension popover.\nThis can be used to configure dimension-specific options" + ], + "signature": [ + "((domElement: Element, props: ", + "VisualizationDimensionEditorProps", + ") => void | ((cleanupElement: Element) => void)) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.renderDimensionEditorAdditionalSection.$1", + "type": "Object", + "tags": [], + "label": "domElement", + "description": [], + "signature": [ + "Element" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.renderDimensionEditorAdditionalSection.$2", + "type": "CompoundType", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "VisualizationDimensionEditorProps", + "" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.renderDimensionTrigger", @@ -5476,7 +5525,7 @@ "tags": [], "label": "XYAnnotationLayerConfig", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "children": [ { @@ -5486,7 +5535,7 @@ "tags": [], "label": "layerId", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5499,7 +5548,7 @@ "signature": [ "\"annotations\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5519,7 +5568,7 @@ }, "[]" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5532,7 +5581,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false } ], @@ -5944,7 +5993,7 @@ "tags": [], "label": "XYDataLayerConfig", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "children": [ { @@ -5954,7 +6003,7 @@ "tags": [], "label": "layerId", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5967,7 +6016,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5980,7 +6029,7 @@ "signature": [ "\"data\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -5993,7 +6042,7 @@ "signature": [ "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6006,7 +6055,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6019,7 +6068,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6039,7 +6088,7 @@ }, "[] | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6052,7 +6101,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6066,7 +6115,7 @@ "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6079,7 +6128,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6099,7 +6148,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6112,7 +6161,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6125,7 +6174,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false } ], @@ -6138,7 +6187,7 @@ "tags": [], "label": "XYReferenceLineLayerConfig", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "children": [ { @@ -6148,7 +6197,7 @@ "tags": [], "label": "layerId", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6161,7 +6210,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6181,7 +6230,7 @@ }, "[] | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6194,7 +6243,7 @@ "signature": [ "\"referenceLine\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false } ], @@ -6265,7 +6314,7 @@ "tags": [], "label": "XYState", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "children": [ { @@ -6278,7 +6327,7 @@ "signature": [ "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6297,7 +6346,7 @@ "text": "LegendConfig" } ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6317,7 +6366,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6337,7 +6386,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6350,7 +6399,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6370,7 +6419,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6390,7 +6439,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6410,7 +6459,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6430,7 +6479,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6450,7 +6499,7 @@ }, "[]" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6463,7 +6512,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6476,7 +6525,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6489,7 +6538,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6509,7 +6558,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6529,7 +6578,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6549,7 +6598,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6569,7 +6618,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6589,7 +6638,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6603,7 +6652,7 @@ "LabelsOrientationConfig", " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6623,7 +6672,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6636,7 +6685,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6649,7 +6698,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6662,7 +6711,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false } ], @@ -6675,7 +6724,7 @@ "tags": [], "label": "YConfig", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "children": [ { @@ -6685,7 +6734,7 @@ "tags": [], "label": "forAccessor", "description": [], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6698,7 +6747,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6711,7 +6760,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6724,7 +6773,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6744,7 +6793,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6764,7 +6813,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6784,7 +6833,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6797,7 +6846,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false }, { @@ -6817,7 +6866,7 @@ }, " | undefined" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false } ], @@ -7147,7 +7196,7 @@ "Palette", " | undefined; }" ], - "path": "x-pack/plugins/lens/public/heatmap_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/heatmap/types.ts", "deprecated": false, "initialIsOpen": false }, @@ -7468,7 +7517,7 @@ "signature": [ "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "initialIsOpen": false }, @@ -7619,7 +7668,7 @@ " | ", "ValidXYDataLayerConfig" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "initialIsOpen": false }, @@ -7711,7 +7760,7 @@ "text": "XYAnnotationLayerConfig" } ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "initialIsOpen": false }, @@ -7725,7 +7774,7 @@ "signature": [ "\"auto\" | \"bottom\" | \"left\" | \"right\"" ], - "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, "initialIsOpen": false }, diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 971afc3611c60..52eab12f1013c 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github summary: API docs for the lens plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 608 | 0 | 527 | 41 | +| 611 | 0 | 529 | 41 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 3223c14e9a215..fbb92e3f8e713 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseApiGuard plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 2028991f01f26..3398861d93ff8 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseManagement plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index e0ecf8de4211d..5d9b64f51f639 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github summary: API docs for the licensing plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 0b7c4e2db03c3..8dc7ca9f50ce3 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github summary: API docs for the lists plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/management.mdx b/api_docs/management.mdx index f0a9d4451dc35..7c8ca9a1a08b0 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github summary: API docs for the management plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index ca8e73ea737ce..114b485e012cb 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github summary: API docs for the maps plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 81cd2dac3f1c2..294d2f1792512 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github summary: API docs for the mapsEms plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 30ea2ec88d359..d5f315b7db65b 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github summary: API docs for the ml plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index f76fa7c3e730f..02bec40051aa4 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoring plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 796ecabb52f32..1d220a82f6cd2 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoringCollection plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 4a5b113af6cf5..b1d62a1f229ed 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github summary: API docs for the navigation plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index ea4feaeb25ce2..c6ce9d60ae123 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github summary: API docs for the newsfeed plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 45b7a6ff1d098..ec992224fa207 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -3473,6 +3473,25 @@ ], "path": "x-pack/plugins/observability/public/plugin.ts", "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsStart.security", + "type": "Object", + "tags": [], + "label": "security", + "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "public", + "docId": "kibSecurityPluginApi", + "section": "def-public.SecurityPluginStart", + "text": "SecurityPluginStart" + } + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false } ], "initialIsOpen": false @@ -3924,7 +3943,13 @@ "label": "filters", "description": [], "signature": [ - "UrlFilter", + { + "pluginId": "observability", + "scope": "public", + "docId": "kibObservabilityPluginApi", + "section": "def-public.UrlFilter", + "text": "UrlFilter" + }, "[] | undefined" ], "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", @@ -4231,6 +4256,81 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter", + "type": "Interface", + "tags": [], + "label": "UrlFilter", + "description": [], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter.values", + "type": "Array", + "tags": [], + "label": "values", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter.notValues", + "type": "Array", + "tags": [], + "label": "notValues", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter.wildcards", + "type": "Array", + "tags": [], + "label": "wildcards", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.UrlFilter.notWildcards", + "type": "Array", + "tags": [], + "label": "notWildcards", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.UxFetchDataResponse", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 890575ba77dff..53bc0f5767412 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github summary: API docs for the observability plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 392 | 2 | 389 | 31 | +| 399 | 2 | 396 | 30 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 56cd53123fd9a..0f839d5fcd0af 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github summary: API docs for the osquery plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 53cc62cd42830..bfbcc91f51110 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -3,7 +3,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory summary: Directory of public APIs available through plugins or packages. -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -12,13 +12,13 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 391 | 327 | 36 | +| 401 | 335 | 36 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 28741 | 175 | 19539 | 907 | +| 28864 | 175 | 19600 | 911 | ## Plugin Directory @@ -32,22 +32,22 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 78 | 1 | 69 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | -| | [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 81 | 0 | 65 | 28 | +| | [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 82 | 0 | 66 | 29 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 272 | 2 | 253 | 9 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 24 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 18 | 0 | 2 | 3 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 207 | 0 | 199 | 7 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2524 | 2 | 297 | 6 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2524 | 2 | 296 | 6 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 102 | 0 | 83 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 143 | 0 | 141 | 12 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 52 | 0 | 51 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3097 | 34 | 2420 | 21 | +| | [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. | 3098 | 34 | 2421 | 22 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 49 | 0 | 29 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 0 | -| | [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. | 945 | 0 | 201 | 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. | 943 | 0 | 199 | 0 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 28 | 3 | 24 | 1 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 10 | 0 | 8 | 2 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 84 | 0 | 68 | 7 | @@ -95,7 +95,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 420 | 9 | | | [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. | 608 | 0 | 527 | 41 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 611 | 0 | 529 | 41 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -109,7 +109,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 34 | 0 | 34 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 392 | 2 | 389 | 31 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 399 | 2 | 396 | 30 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 13 | 0 | 13 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 243 | 2 | 187 | 12 | @@ -125,7 +125,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 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. | 236 | 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. | 239 | 0 | 90 | 0 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 54 | 0 | 53 | 22 | | | [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 | @@ -143,10 +143,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 447 | 1 | 342 | 32 | | | [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) | - | 426 | 0 | 405 | 46 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 428 | 0 | 407 | 46 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 130 | 0 | 91 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 205 | 0 | 142 | 9 | -| | [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. | 98 | 2 | 84 | 16 | +| | [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. | 97 | 2 | 84 | 16 | | upgradeAssistant | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | urlDrilldown | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds drilldown implementations to Kibana | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 12 | 0 | 12 | 0 | @@ -175,7 +175,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | Elastic APM trace data generator | 74 | 0 | 74 | 11 | | | [Owner missing] | - | 11 | 5 | 11 | 0 | | | Machine Learning UI | React components for AIOps related efforts. | 6 | 0 | 6 | 0 | -| | Machine Learning UI | Static utilities for AIOps related efforts. | 46 | 0 | 24 | 0 | +| | Machine Learning UI | Static utilities for AIOps related efforts. | 49 | 0 | 24 | 0 | | | [Owner missing] | Alerts components and hooks | 9 | 1 | 9 | 0 | | | Kibana Core | Kibana Analytics tool | 69 | 0 | 69 | 2 | | | Kibana Core | - | 96 | 0 | 0 | 0 | @@ -208,7 +208,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | Kibana Core | - | 5 | 0 | 0 | 0 | | | Kibana Core | - | 16 | 0 | 7 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | -| | Kibana Core | - | 15 | 0 | 13 | 0 | +| | Kibana Core | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 9 | 0 | 3 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 4 | 0 | 4 | 0 | @@ -272,8 +272,12 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 94 | 1 | 66 | 0 | | | Kibana Core | - | 288 | 1 | 125 | 0 | +| | Kibana Core | - | 2 | 0 | 1 | 0 | +| | Kibana Core | - | 6 | 0 | 6 | 0 | +| | Kibana Core | - | 7 | 0 | 7 | 0 | | | Kibana Core | - | 82 | 0 | 41 | 0 | | | Kibana Core | - | 225 | 0 | 82 | 0 | +| | Kibana Core | - | 11 | 0 | 9 | 0 | | | Kibana Core | - | 5 | 0 | 5 | 0 | | | Kibana Core | - | 6 | 0 | 4 | 0 | | | Kibana Core | - | 2 | 0 | 1 | 0 | @@ -288,9 +292,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 9 | 1 | 9 | 0 | | | [Owner missing] | - | 65 | 0 | 64 | 0 | | | [Owner missing] | - | 15 | 0 | 9 | 0 | -| | [Owner missing] | - | 31 | 2 | 27 | 0 | +| | [Owner missing] | - | 30 | 2 | 26 | 0 | | | [Owner missing] | - | 67 | 0 | 67 | 2 | | | [Owner missing] | - | 1 | 0 | 1 | 0 | +| | [Owner missing] | - | 19 | 0 | 11 | 0 | | | [Owner missing] | - | 27 | 0 | 14 | 1 | | | Kibana Core | - | 7 | 0 | 3 | 0 | | | [Owner missing] | - | 222 | 1 | 168 | 12 | @@ -308,9 +313,11 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | App Services | - | 35 | 4 | 35 | 0 | | | [Owner missing] | - | 20 | 0 | 20 | 2 | | | [Owner missing] | - | 13 | 0 | 13 | 0 | -| | [Owner missing] | - | 69 | 0 | 69 | 0 | +| | [Owner missing] | - | 7 | 0 | 4 | 3 | +| | [Owner missing] | - | 91 | 0 | 91 | 0 | | | Kibana Core | - | 30 | 0 | 5 | 37 | | | Kibana Core | - | 8 | 0 | 8 | 0 | +| | [Owner missing] | - | 6 | 0 | 1 | 1 | | | [Owner missing] | - | 534 | 1 | 1 | 0 | | | Machine Learning UI | This package includes utility functions related to creating elasticsearch aggregation queries, data manipulation and verification. | 53 | 2 | 35 | 4 | | | Machine Learning UI | A type guard to check record like object structures. | 3 | 0 | 2 | 0 | @@ -324,7 +331,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 21 | 0 | 10 | 0 | | | [Owner missing] | - | 6 | 0 | 6 | 1 | | | [Owner missing] | - | 74 | 0 | 71 | 0 | -| | [Owner missing] | A library to generate scalability benchmarking simulation files from APM traces. | 3 | 0 | 3 | 1 | | | [Owner missing] | Security Solution auto complete | 50 | 1 | 35 | 0 | | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | | | [Owner missing] | Security Solution utilities for React hooks | 15 | 0 | 7 | 0 | @@ -341,6 +347,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | security solution utilities to use across plugins such lists, security_solution, cases, etc... | 31 | 0 | 29 | 0 | | | Kibana Core | - | 53 | 0 | 50 | 1 | | | [Owner missing] | - | 25 | 0 | 24 | 1 | +| | [Owner missing] | - | 2 | 0 | 0 | 0 | | | [Owner missing] | - | 13 | 0 | 9 | 0 | | | [Owner missing] | - | 20 | 0 | 6 | 0 | | | [Owner missing] | - | 10 | 0 | 4 | 0 | @@ -371,6 +378,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 113 | 1 | 65 | 0 | | | [Owner missing] | - | 83 | 0 | 83 | 1 | | | [Owner missing] | - | 7 | 0 | 6 | 0 | +| | [Owner missing] | - | 39 | 0 | 3 | 0 | | | [Owner missing] | - | 32 | 0 | 12 | 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 c34b8f22bd3af..b0d7c1dc08388 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github summary: API docs for the presentationUtil plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 314b88deda879..710686e9fa1ac 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github summary: API docs for the remoteClusters plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 0b43c7aecfa28..e9dd7653613c9 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github summary: API docs for the reporting plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 3ab0c6fc31b14..6d6e7fe976481 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github summary: API docs for the rollup plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 5650592555279..5dfb3f11cded3 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github summary: API docs for the ruleRegistry plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 648761dd160a1..e8845e53df8a4 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github summary: API docs for the runtimeFields plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects.devdocs.json b/api_docs/saved_objects.devdocs.json index 2eb978fe290ae..fba13f71ed1cc 100644 --- a/api_docs/saved_objects.devdocs.json +++ b/api_docs/saved_objects.devdocs.json @@ -821,13 +821,7 @@ "description": [], "signature": [ "(savedObject: ", - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - }, + "SavedObjectsStart", ", uiSettings: ", "IUiSettingsClient", ") => (props: ", @@ -845,13 +839,7 @@ "label": "savedObject", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - } + "SavedObjectsStart" ], "path": "src/plugins/saved_objects/public/finder/saved_object_finder.tsx", "deprecated": false, @@ -2900,13 +2888,7 @@ "description": [], "signature": [ "{ savedObjects: ", - { - "pluginId": "core", - "scope": "public", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-public.SavedObjectsStart", - "text": "SavedObjectsStart" - }, + "SavedObjectsStart", "; uiSettings: ", "IUiSettingsClient", "; } & ", diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 04b30cec2d9b1..a62f0a0d7d73d 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjects plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 151aff38f6b27..5ea34029c565d 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsManagement plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 0c09406cef78d..8d7f7e14cbc9d 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTagging plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index fdf0de50a9e17..90edb754d8a6a 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTaggingOss plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 22e94adfe2954..b9c4bfd867854 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotMode plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 85d702f767330..6412428b7dc4f 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotting plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index bb06aa96cef9b..11dfcb65020ff 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -788,6 +788,63 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-public.UserProfileSuggestParams", + "type": "Interface", + "tags": [], + "label": "UserProfileSuggestParams", + "description": [ + "\nParameters for the suggest API." + ], + "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-public.UserProfileSuggestParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nQuery string used to match name-related fields in user profiles. The following fields are treated as\nname-related: username, full_name and email." + ], + "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "deprecated": false + }, + { + "parentPluginId": "security", + "id": "def-public.UserProfileSuggestParams.size", + "type": "number", + "tags": [], + "label": "size", + "description": [ + "\nDesired number of suggestions to return. The default value is 10." + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "deprecated": false + }, + { + "parentPluginId": "security", + "id": "def-public.UserProfileSuggestParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, suggest API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [], @@ -934,7 +991,7 @@ "\nA set of methods to work with Kibana user profiles." ], "signature": [ - "{ getCurrent(params?: ", + ">(path: string, params: ", { "pluginId": "security", "scope": "public", "docId": "kibSecurityPluginApi", - "section": "def-public.UserProfileGetCurrentParams", - "text": "UserProfileGetCurrentParams" + "section": "def-public.UserProfileSuggestParams", + "text": "UserProfileSuggestParams" }, - " | undefined): Promise<", + ") => Promise<", { "pluginId": "security", "scope": "common", "docId": "kibSecurityPluginApi", - "section": "def-common.UserProfileWithSecurity", - "text": "UserProfileWithSecurity" + "section": "def-common.UserProfile", + "text": "UserProfile" }, - "[]>; getCurrent: | null>; bulkGet(params?: ", + { + "pluginId": "security", + "scope": "public", + "docId": "kibSecurityPluginApi", + "section": "def-public.UserProfileGetCurrentParams", + "text": "UserProfileGetCurrentParams" + }, + " | undefined) => Promise<", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.GetUserProfileResponse", + "text": "GetUserProfileResponse" + }, + ">; bulkGet: Promise<", { "pluginId": "security", "scope": "common", @@ -2048,7 +2121,7 @@ "tags": [], "label": "suggest", "description": [ - "\nRetrieves a single user profile by identifier." + "\nSuggests multiple user profiles by search criteria." ], "signature": [ " JSX.Element" ], @@ -23,7 +23,7 @@ "id": "def-public.DataViewPicker.$1", "type": "Object", "tags": [], - "label": "{\n isMissingCurrent,\n currentDataViewId,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n showNewMenuTour,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n}", + "label": "{\n isMissingCurrent,\n currentDataViewId,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n}", "description": [], "signature": [ "DataViewPickerPropsExtended" @@ -502,21 +502,6 @@ "children": [], "returnComment": [] }, - { - "parentPluginId": "unifiedSearch", - "id": "def-public.DataViewPickerProps.showNewMenuTour", - "type": "CompoundType", - "tags": [], - "label": "showNewMenuTour", - "description": [ - "\nFlag to show the tour component for the first time." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", - "deprecated": false - }, { "parentPluginId": "unifiedSearch", "id": "def-public.DataViewPickerProps.textBasedLanguages", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index c64ee5e7ce2a5..0db1a30762630 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the unifiedSearch plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 98 | 2 | 84 | 16 | +| 97 | 2 | 84 | 16 | ## Client diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 68f2f2374de4e..4f28e4e939197 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github summary: API docs for the unifiedSearch.autocomplete plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 98 | 2 | 84 | 16 | +| 97 | 2 | 84 | 16 | ## Client diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 2fdfb613a2bc7..afe7d3468da1d 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github summary: API docs for the urlForwarding plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 988758bf33879..24c15fe918f4c 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the usageCollection plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 91a2a7b530778..71457797f93c8 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github summary: API docs for the ux plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index a1e26af8f3387..8761ab09e052f 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the visDefaultEditor plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 6ec204a5baafe..17f3ca755415f 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeGauge plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index ff3ca2729bc9d..ab47ff3151238 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeHeatmap plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 492f7cca4c428..d5fd739a83113 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypePie plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 2189f7b8a9603..0b25a9d343315 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTable plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index c097183bcb395..7de4951b099c9 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimelion plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 88ca2a004f2bc..c454e66b16a9d 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimeseries plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 5a000cc13478d..24434a894dc2b 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVega plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index a372b64545688..0b7cad3e6ce63 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVislib plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 72daa67a4bebd..a102c03d77cc4 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeXy plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 671f0c333880f..a5a65cd36a79c 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github summary: API docs for the visualizations plugin -date: 2022-08-03 +date: 2022-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/dev_docs/operations/operations_landing.mdx b/dev_docs/operations/operations_landing.mdx index 2533df2f194b9..2b1a6e773d39f 100644 --- a/dev_docs/operations/operations_landing.mdx +++ b/dev_docs/operations/operations_landing.mdx @@ -12,6 +12,13 @@ layout: landing description="👋 Here you will find documentation about all the things the Kibana Operations team manages. Feel free to look around and ask questions!" /> + + \ No newline at end of file diff --git a/dev_docs/operations/packages_idm.mdx b/dev_docs/operations/packages_idm.mdx new file mode 100644 index 0000000000000..62fe1a76c69f2 --- /dev/null +++ b/dev_docs/operations/packages_idm.mdx @@ -0,0 +1,208 @@ +--- +id: kibDevDocsOpsPackages +slug: /kibana-dev-docs/ops/packages +title: "Packages / Internal Dependency Management" +description: Information about packages, where we are going, and how we are going to get there +tags: ['kibana', 'dev', 'contributor', 'operations', 'idm', 'packages'] +--- + +## Summary + +The size of the Kibana repository has surpassed almost all other Typescript projects on Github, and the Javascript tooling ecosystem is inadequate. AppEx Operations team has done a lot of work over the years to close these gaps and keep up with our codebase's growth. Still, significant steps are necessary to provide a more efficient development experience. The AppEx Operations team is leading an effort to migrate to Bazel, which among other things, will provide remote caching and incremental builds. This initiative should drastically improve the productivity of Kibana contributors by minimizing what needs to be built, both locally and in CI, and providing faster and more thorough feedback. + + + This document represents the target of the IDM project and not the currently implemented features. See [What works now?][status] for information about current implementation status. + + +## Goals + +- Use packages as a core unit of code used throughout the repository +- Packages have well defined boundaries, a single responsibility, and are easily reusable and shareable across the repository when desired +- Support organizing the repository by domain +- Allow developers and CI to benefit from a remote cache and incremental builds for all development/validation/build tasks + +## Definitions + +These are some of the terms we are using to describe parts of this initiative: + +Build Tasks/Tasks +: We refer to any task/command that is executed as part of developement or CI as a "build task" or just "task" + +Package +: Packages can be installed from NPM, or implemented in the repository. Local packages are structured in a specific way detailed in the [Package Structure](#package-structure) section. + +Incremental tasks +: The ability to execute the minimal set of build tasks necessary by inspecting the files which have changed and utilizing local+remote caches + +## Package Structure + +Every package has: + - a globally unique module ID in the `@kbn/` namespace + - a type, see [Package Types](#package-types) + - dependencies on other packages in the repo or from NPM declared in their `kibana.jsonc` file and updated automatically + - configuration files like `BUILD.bazel`, `jest.config.js`, `package.json`, etc. which are automatically generated when necessary from the `kibana.jsonc` file. + - a single interface. When you import a package you get it's one and only interface. Packages don't have sub-imports, or sub-modules. + - the ability to include jest tests alongside the code + +## Package Types + +Every package is of one of the following types. Using package types allows us to have many different packages and pre-defined build tasks, restrictions, and unique config for each package type. There is currently a proposal to add a new `test-helpers` package type, and we expect more package types to be defined in the future. + +`shared-common` +: These packages can be imported from all other packages. + +`shared-browser` +: These packages can be imported from `shared-browser` and `plugin-browser` packages. Storybooks may be included in package of type `shared-browser`. + +`shared-server` +: These packages can be imported from `shared-server` and `plugin-server` packages. + +`shared-scss` +: These packages can be imported by `shared-browser` and `plugin-browser` packages. Instead of an `index.ts` file these pacakges have an `index.scss` file which will be exposed to consumers of this package. + +`plugin-browser` +: These packages expose types to other packages via a root `types.ts` file, `import type` statements must be used when importing. Module IDs must end with `-plugin-browser`. + +`plugin-server` +: These packages expose types to other packages via a root `types.ts` file, `import type` statements must be used when importing. Module IDs must end with `-plugin-server`. + +`functional-test` +: These packages can not be imported by other packages and expose one or more functional testing configurations, including API integration tests. Having this separate means that iterating on functional tests will not need to rebuild the application and updating the application will usually not rebuild the tests. + + +## Phases + +We're planning to implement the full package system described in phases. Currently, those phases look like this: + +### Phase 1: Ground preparation + +**status:** ✅ complete + +This phase is about identifying issues that would prevent a migration for teams in order to provide adequate time for them before they have the need to migrate. + + - Migrate all plugins to use module IDs + - Find instances of ESLint being disabled and illegal cross-boundary imports are used; create issues for teams to address + - Prevent naked eslint-disable + - Prevent disabling specific ESLint rules + - Windows development is not supported + - Rewrite @kbn/pm using native Node.js to remove its build step. Anything outside of bootstrap like clean and reset commands will move to different packages. + - Document how to break up packages into smaller pieces + +### Phase 2: Legacy packages migration + +**status:** in progress + +This phase is about migrating the existing legacy packages to one of the [package types](#package-types). + + - Add kibana.json files to existing packages + - Auto generating configuration files based on the `kibana.jsonc` manifest + - Discover dependencies used in packages automatically + +### Phase 3: Double down on DX + +This phase is all about making it easier for teams to start breaking their plugin up into packages. + + - Rebuilding packages when running the Kibana Development CLI is automatic + - Pause requests when packages are being re-built + - Ensure the server restarts when files in a shared-server package are rebuilt + - Package linting to validate rules + - Start with a package rule to validate dependencies are all used and included + - Document how to create, build and watch packages. + +### Phase 4: Plugins everywhere + +This phase is all about supporting the creation of plugin-browser and plugin-server packages anywhere in the repository. + + - Extracting Webpack config from @kbn/optimizer to work on a single bundle inside Bazel + - Update plugin discovery to find and differentiate between legacy plugins and plugin-browser/server packages + - ESLint rules to validate imports into and out of plugin-browser and plugin-server packages + - Finish the migration of core to packages and reflect with the core team (and possibly the Shared UX team) around what we should recommend for plugin authors before we start to migrate legacy plugins to the package system + - Documentation, documentation, documentation + - How do 3rd party plugins migrate to the new system? + +### Phase 5: Legacy plugins migration + +This phase is all about having the solution teams migrating their legacy plugins into packages. + + - Identify the order of plugins that can be migrated + - Identify and communicate what needs to be done by teams + - Provide migration consultations + - Deprecate legacy 3rd party plugin styles and communicate 18 month migration period + +### Phase 6: Cleanup + +This phase is about finalizing the rough edges and making sure every piece of code is on Bazel. + + - No code lives outside a Bazel package + - Extend the package development tooling to support 3rd party package development and allow packages to participate in the benefits of Bazel within the `plugins` directory. + - Ability for 3rd party packages to require specific versions of specific packages from NPM + - Automatically build the components that need changes + - Build package artifacts that can be installed in Kibana distributables + + +## FAQ + +### Is it time for me to start creating packages? + +Probably not. The Shared UX and Core teams are currently our Guinea Pig teams and they're experiencing the pain of living on the bleeding edge. If you want to create a single package you are welcome to, but for now it's probably best that you wait until Operations reached out to your team. + +### How do circular dependencies work? + +By breaking the repository into packages we not be able to support cross-package circular dependency. + +Imagine trying to build the types for `@kbn/a`, which depend on the types for `@kbn/b`. If `@kbn/b` also depends on the types for `@kbn/a` there is no way to build the types for eather package because they form a circular dependency. + +If you cause a circular dependency in the task graph Bazel will produce a pretty great error message explaining where the cycle is. + +So far, the solution to resolving circular dependencies has always been to break out some component which is causing the circular dependency into a separate package. Packages are light weight, and are very easy to create ([work in progress][status]) so please feel comfortable creating more packages. + +### How do we name packages? + +There are a few package naming rules: + - all packages must use the `@kbn/` namespace + - `plugin-browser` packages must end with `-plugin-browser` + - `plugin-server` packages must end with `-plugin-server` + - be considerate of the fact that we are operating in a global namespace and avoid overly generic names + +Other than these rules, it's up to you what the best name for your package is. + + + Keep the single responsibility principle in mind, if there isn't a clear name for your package it might mean that it includes too many things and should be split into multiple smaller packages with explicit purposes. + + +The shared-ux team makes a lot of packages containing a single component which is widely shared and provides a lot of helpers and types for other packages to consume, the shared-ux team has used the following naming scheme: + +``` +/{domain}/{componentName} + impl/ :: `@kbn/shared-ux-{domain}-{componentName}` + mocks/ :: `@kbn/shared-ux-{domain}-{componentName}-mocks` + types/ :: `@kbn/shared-ux-{domain}-{componentName}-types` +``` + +The `@kbn/{team}-{domain}-{component}(-{type})?` style naming scheme is also followed by the core team in their packages: + + - `@kbn/core-analytics-browser` + - `@kbn/core-analytics-browser-internal` + - `@kbn/core-analytics-browser-mocks` + - `@kbn/core-analytics-server` + - `@kbn/core-analytics-server-mocks` + - `@kbn/core-analytics-server-internal` + - etc. + +### Where do I put my package? + +The only rule the package system enforces is that packages can't live inside of other packages. Additionally, for licensing purposes it's probably best to keep SSPL licensed code in the `packages` or `src` directories and Elastic licensed code in the `x-pack` directory. + +Otherise, you can put your packages wherever you like. It's probably best that you don't put them in the current `packages` directory as it's huge and only getting bigger with time. + +To define a new directory where packages will live you will need to edit the [`BAZEL_PACKAGE_DIRS`][pkgDirs] const. This list points to all the directories where packages can live in the repository and includes the current list of locations where packages are being created. + +### What works now? + +Today we have a basic package generator that produces legacy package definitions. We're done laying the groundwork now and have both the Core and Shared UX teams playing guinea pig. You can use the legacy package generator with `node scripts/generate package` and create a package, but it's not the right time for more teams to start migrating large portions of their code to packages. + +We're now entering Phase 2 of the plan, more details about the phases of our plan can be found [above](#phases) + +[status]: #what-works-now +[idm-rfc]: https://docs.google.com/document/d/1Bhg601MoGQjqGMGdLWSLnkopRexwrcbf_0MNcUkhx3I "Internal Dependency Management RFC on Google Docs" +[pkgDirs]: https://github.com/elastic/kibana/blob/main/packages/kbn-bazel-packages/src/bazel_package_dirs.ts#L22 diff --git a/docs/developer/plugin/external-plugin-localization.asciidoc b/docs/developer/plugin/external-plugin-localization.asciidoc index 9fbf7cdab9b76..1eb56a6787a62 100644 --- a/docs/developer/plugin/external-plugin-localization.asciidoc +++ b/docs/developer/plugin/external-plugin-localization.asciidoc @@ -132,9 +132,9 @@ Full details are {kib-repo}tree/main/packages/kbn-i18n#react[here]. [discrete] === Resources -To learn more about i18n tooling, see {kib-repo}blob/{branch}src/dev/i18n/README.md[i18n dev tooling]. +To learn more about i18n tooling, see {kib-repo}blob/{branch}/src/dev/i18n/README.md[i18n dev tooling]. To learn more about implementing i18n in the UI, use the following links: -* {kib-repo}blob/{branch}packages/kbn-i18n/README.md[i18n plugin] -* {kib-repo}blob/{branch}packages/kbn-i18n/GUIDELINE.md[i18n guidelines] \ No newline at end of file +* {kib-repo}blob/{branch}/packages/kbn-i18n/README.md[i18n plugin] +* {kib-repo}blob/{branch}/packages/kbn-i18n/GUIDELINE.md[i18n guidelines] diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index 99cbf6f8eb533..d5e0ea6e397f7 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -46,7 +46,8 @@ a|Format dates. Supports relative dates expressions (for example, "now-15d"). R Example: `{{date event.from “YYYY MM DD”}}` + -`{{date “now-15”}}` +`{{date “now-15d”}}` + +`{{date “now/d” roundUp=true}}` |formatNumber a|Format numbers. Numbers can be formatted to look like currency, percentages, times or numbers with decimal places, thousands, and abbreviations. diff --git a/docs/user/plugins.asciidoc b/docs/user/plugins.asciidoc index 36f7ce8eb49ed..0bf407ac1ff90 100644 --- a/docs/user/plugins.asciidoc +++ b/docs/user/plugins.asciidoc @@ -70,6 +70,7 @@ We are unable to evaluate or maintain the known plugins, so care should be taken * https://github.com/PhaedrusTheGreek/transform_vis[Transform Visualization] (PhaedrusTheGreek) * https://github.com/nyurik/kibana-vega-vis[Vega-based visualizations] (nyurik) - Support for user-defined graphs, external data sources, maps, images, and user-defined interactivity. * https://github.com/Camichan/kbn_aframe[VR Graph Visualizations] (Camichan) +* https://github.com/uniberg/kbn_sankey_vis[Sankey-Visualization] (uniberg) [float] === Other diff --git a/examples/response_stream/README.md b/examples/response_stream/README.md index 3582f06fd216b..9f66fc19f2393 100644 --- a/examples/response_stream/README.md +++ b/examples/response_stream/README.md @@ -21,7 +21,7 @@ The request's headers get passed on to automatically identify if compression is On the client, the custom hook is used like this: ```ts -const { error, start, cancel, data, isRunning } = useFetchStream< +const { errors, start, cancel, data, isRunning } = useFetchStream< ApiSimpleStringStream, typeof basePath >(`${basePath}/internal/response_stream/simple_string_stream`); ``` diff --git a/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx b/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx index 8f5687db1749d..0755765374330 100644 --- a/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx +++ b/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx @@ -45,7 +45,7 @@ export const PageReducerStream: FC = () => { const [simulateErrors, setSimulateErrors] = useState(false); - const { dispatch, start, cancel, data, error, isCancelled, isRunning } = useFetchStream< + const { dispatch, start, cancel, data, errors, isCancelled, isRunning } = useFetchStream< ApiReducerStream, typeof basePath >( @@ -65,13 +65,15 @@ export const PageReducerStream: FC = () => { } }; + // TODO This approach needs to be adapted as it might miss when error messages arrive bulk. // This is for low level errors on the stream/HTTP level. useEffect(() => { - if (error) { - notifications.toasts.addDanger(error); + if (errors.length > 0) { + notifications.toasts.addDanger(errors[errors.length - 1]); } - }, [error, notifications.toasts]); + }, [errors, notifications.toasts]); + // TODO This approach needs to be adapted as it might miss when error messages arrive bulk. // This is for errors on the application level useEffect(() => { if (data.errors.length > 0) { diff --git a/examples/response_stream/public/containers/app/pages/page_simple_string_stream/index.tsx b/examples/response_stream/public/containers/app/pages/page_simple_string_stream/index.tsx index ba159887edfaf..cfa76688d9701 100644 --- a/examples/response_stream/public/containers/app/pages/page_simple_string_stream/index.tsx +++ b/examples/response_stream/public/containers/app/pages/page_simple_string_stream/index.tsx @@ -21,7 +21,7 @@ export const PageSimpleStringStream: FC = () => { const { core } = useDeps(); const basePath = core.http?.basePath.get() ?? ''; - const { dispatch, error, start, cancel, data, isRunning } = useFetchStream< + const { dispatch, errors, start, cancel, data, isRunning } = useFetchStream< ApiSimpleStringStream, typeof basePath >(`${basePath}/internal/response_stream/simple_string_stream`, { timeout: 500 }); @@ -61,9 +61,17 @@ export const PageSimpleStringStream: FC = () => {

{data}

- {error && ( + {errors.length > 0 && ( -

{error}

+ {errors.length === 1 ? ( +

{errors[0]}

+ ) : ( +
    + {errors.map((e, i) => ( +
  • {e}
  • + ))} +
+ )}{' '}
)} diff --git a/examples/user_profile_examples/README.md b/examples/user_profile_examples/README.md new file mode 100644 index 0000000000000..7d000bb04e34f --- /dev/null +++ b/examples/user_profile_examples/README.md @@ -0,0 +1,3 @@ +# User profile examples + +Demo of how to implement a suggest user functionality. diff --git a/examples/user_profile_examples/kibana.json b/examples/user_profile_examples/kibana.json new file mode 100644 index 0000000000000..c808302fe65a7 --- /dev/null +++ b/examples/user_profile_examples/kibana.json @@ -0,0 +1,14 @@ +{ + "id": "userProfileExamples", + "kibanaVersion": "kibana", + "version": "0.0.1", + "server": true, + "ui": true, + "owner": { + "name": "Kibana Platform Security", + "githubTeam": "kibana-security" + }, + "description": "Demo of how to implement a suggest user functionality", + "requiredPlugins": ["developerExamples", "security", "spaces"], + "optionalPlugins": [] +} diff --git a/examples/user_profile_examples/public/avatar_demo.tsx b/examples/user_profile_examples/public/avatar_demo.tsx new file mode 100644 index 0000000000000..b80bfa8b575a6 --- /dev/null +++ b/examples/user_profile_examples/public/avatar_demo.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { FunctionComponent } from 'react'; +import { EuiTitle, EuiSpacer } from '@elastic/eui'; +import { UserAvatar } from '@kbn/user-profile-components'; +import { PanelWithCodeBlock } from './panel_with_code_block'; + +export const AvatarDemo: FunctionComponent = () => { + const userProfile = { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + data: { + avatar: { + color: '#09e8ca', + initials: 'DN', + imageUrl: 'https://source.unsplash.com/64x64/?cat', + }, + }, + }; + + return ( + + +   + + + +

Unknown

+
+ + +
+ ); +}; + +const code = `import { UserAvatar } from '@kbn/user-profile-components'; + +const userProfile = { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + data: { + avatar: { + color: '#09e8ca', + initials: 'DN', + imageUrl: 'https://source.unsplash.com/64x64/?cat' + } + }, +}; + +`; diff --git a/scripts/generate_scalability_simulations.js b/examples/user_profile_examples/public/index.ts old mode 100644 new mode 100755 similarity index 75% rename from scripts/generate_scalability_simulations.js rename to examples/user_profile_examples/public/index.ts index 9773e1d1439a7..7b5e8eb32a645 --- a/scripts/generate_scalability_simulations.js +++ b/examples/user_profile_examples/public/index.ts @@ -5,6 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { UserProfilesPlugin } from './plugin'; -require('../src/setup_node_env'); -require('@kbn/scalability-simulation-generator').generateScalabilitySimulations(); +export const plugin = () => new UserProfilesPlugin(); diff --git a/examples/user_profile_examples/public/panel_with_code_block.tsx b/examples/user_profile_examples/public/panel_with_code_block.tsx new file mode 100644 index 0000000000000..3b86b343eda7d --- /dev/null +++ b/examples/user_profile_examples/public/panel_with_code_block.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { EuiTitle, EuiSpacer, EuiSplitPanel, EuiCodeBlock } from '@elastic/eui'; + +export interface PanelWithCodeBlockProps { + title: string; + code: string; +} + +export const PanelWithCodeBlock: React.FunctionComponent = ({ + title, + code, + children, +}) => ( + <> + +

{title}

+
+ + + {children} + + + {code} + + + + + +); diff --git a/examples/user_profile_examples/public/plugin.tsx b/examples/user_profile_examples/public/plugin.tsx new file mode 100755 index 0000000000000..4a273dc097291 --- /dev/null +++ b/examples/user_profile_examples/public/plugin.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 ReactDOM from 'react-dom'; +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import { EuiPageTemplate } from '@elastic/eui'; +import { AvatarDemo } from './avatar_demo'; +import { PopoverDemo } from './popover_demo'; +import { SelectableDemo } from './selectable_demo'; + +interface SetupDeps { + developerExamples: DeveloperExamplesSetup; + security: SecurityPluginSetup; +} + +interface StartDeps { + security: SecurityPluginStart; +} + +export class UserProfilesPlugin implements Plugin { + public setup(core: CoreSetup, deps: SetupDeps) { + // Register an application into the side navigation menu + core.application.register({ + id: 'userProfileExamples', + title: 'User profile components', + async mount({ element }: AppMountParameters) { + // Fetch user suggestions + // const [, depsStart] = await core.getStartServices(); + // depsStart.security.userProfiles.suggest('/internal/user_profiles_examples/_suggest', { + // name: 'a', + // }); + + ReactDOM.render( + + + + + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); + }, + }); + + deps.developerExamples.register({ + appId: 'userProfileExamples', + title: 'User Profile', + description: 'Demo of how to implement a suggest user functionality', + }); + } + + public start(core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/examples/user_profile_examples/public/popover_demo.tsx b/examples/user_profile_examples/public/popover_demo.tsx new file mode 100644 index 0000000000000..5b9ef060fd277 --- /dev/null +++ b/examples/user_profile_examples/public/popover_demo.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { FunctionComponent, useState } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { UserProfilesPopover, UserProfileWithAvatar } from '@kbn/user-profile-components'; +import { PanelWithCodeBlock } from './panel_with_code_block'; + +export const PopoverDemo: FunctionComponent = () => { + const [isOpen, setIsOpen] = useState(false); + const [selectedOptions, setSelectedOptions] = useState([ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + }, + ]); + const defaultOptions: UserProfileWithAvatar[] = [ + { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + data: {}, + user: { + username: 'damaged_raccoon', + email: 'damaged_raccoon@elastic.co', + full_name: 'Damaged Raccoon', + }, + }, + { + uid: 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + data: {}, + user: { + username: 'physical_dinosaur', + email: 'physical_dinosaur@elastic.co', + full_name: 'Physical Dinosaur', + }, + }, + { + uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + data: {}, + user: { + username: 'wet_dingo', + email: 'wet_dingo@elastic.co', + full_name: 'Wet Dingo', + }, + }, + ]; + + return ( + + setIsOpen((value) => !value)}> + Edit assignees + + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + selectableProps={{ + selectedOptions, + defaultOptions, + onChange: setSelectedOptions, + height: 32 * 8, + }} + panelStyle={{ + width: 32 * 16, + }} + /> + + ); +}; + +const code = `import { UserProfilesPopover } from '@kbn/user-profile-components'; + +const [selectedOptions, setSelectedOptions] = useState([ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + }, +]); + + + Edit assignees + + } + selectableProps={{ + selectedOptions, + onChange: setSelectedOptions + }} +/>`; diff --git a/examples/user_profile_examples/public/selectable_demo.tsx b/examples/user_profile_examples/public/selectable_demo.tsx new file mode 100644 index 0000000000000..09b0f6ce54758 --- /dev/null +++ b/examples/user_profile_examples/public/selectable_demo.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { FunctionComponent, useState } from 'react'; +import { UserProfilesSelectable, UserProfileWithAvatar } from '@kbn/user-profile-components'; +import { PanelWithCodeBlock } from './panel_with_code_block'; + +export const SelectableDemo: FunctionComponent = () => { + const [selectedOptions, setSelectedOptions] = useState([ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + }, + ]); + + const defaultOptions: UserProfileWithAvatar[] = [ + { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + data: {}, + user: { + username: 'damaged_raccoon', + email: 'damaged_raccoon@elastic.co', + full_name: 'Damaged Raccoon', + }, + }, + { + uid: 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + data: {}, + user: { + username: 'physical_dinosaur', + email: 'physical_dinosaur@elastic.co', + full_name: 'Physical Dinosaur', + }, + }, + { + uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + data: {}, + user: { + username: 'wet_dingo', + email: 'wet_dingo@elastic.co', + full_name: 'Wet Dingo', + }, + }, + ]; + + return ( + + + + ); +}; + +const code = `import { UserProfilesSelectable } from '@kbn/user-profile-components'; + +const [selectedOptions, setSelectedOptions] = useState([ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@elastic.co', + full_name: 'Delighted Nightingale', + }, + }, +]); + +`; diff --git a/examples/user_profile_examples/server/index.ts b/examples/user_profile_examples/server/index.ts new file mode 100755 index 0000000000000..7b5e8eb32a645 --- /dev/null +++ b/examples/user_profile_examples/server/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. + */ +import { UserProfilesPlugin } from './plugin'; + +export const plugin = () => new UserProfilesPlugin(); diff --git a/examples/user_profile_examples/server/plugin.ts b/examples/user_profile_examples/server/plugin.ts new file mode 100644 index 0000000000000..8f92b98b935a9 --- /dev/null +++ b/examples/user_profile_examples/server/plugin.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 type { Plugin, CoreSetup } from '@kbn/core/server'; +import { + PluginSetupContract as FeaturesPluginSetup, + PluginStartContract as FeaturesPluginStart, +} from '@kbn/features-plugin/server'; +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import { schema } from '@kbn/config-schema'; + +export interface SetupDeps { + features: FeaturesPluginSetup; + security: SecurityPluginSetup; + spaces: SpacesPluginSetup; +} + +export interface StartDeps { + features: FeaturesPluginStart; + security: SecurityPluginStart; + spaces: SpacesPluginStart; +} + +export class UserProfilesPlugin implements Plugin { + setup(core: CoreSetup) { + const router = core.http.createRouter(); + router.post( + { + path: '/internal/user_profiles_examples/_suggest', + validate: { + body: schema.object({ + name: schema.string(), + dataPath: schema.maybe(schema.string()), + }), + }, + /** + * Important: You must restrict access to this endpoint using access `tags`. + */ + options: { tags: ['access:suggestUserProfiles'] }, + }, + async (context, request, response) => { + const [, pluginDeps] = await core.getStartServices(); + + /** + * Important: `requiredPrivileges` must be hard-coded server-side and cannot be exposed as a + * param client-side. + * + * If your app requires suggestions based on different privileges you must expose separate + * endpoints for each use-case. + * + * In this example we ensure that suggested users have access to the current space and are + * able to login but in your app you will want to change that to something more relevant. + */ + const profiles = await pluginDeps.security.userProfiles.suggest({ + name: request.body.name, + dataPath: request.body.dataPath, + requiredPrivileges: { + spaceId: pluginDeps.spaces.spacesService.getSpaceId(request), + privileges: { + kibana: [pluginDeps.security.authz.actions.login], + }, + }, + }); + + return response.ok({ body: profiles }); + } + ); + } + + start() { + return {}; + } + + stop() { + return {}; + } +} diff --git a/examples/user_profile_examples/tsconfig.json b/examples/user_profile_examples/tsconfig.json new file mode 100644 index 0000000000000..da98fc26aa8f2 --- /dev/null +++ b/examples/user_profile_examples/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types" + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../x-pack/plugins/security/tsconfig.json" }, + { "path": "../developer_examples/tsconfig.json" } + ] +} diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index 434e7075c21aa..9aa3530578857 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -439,6 +439,14 @@ "id": "kibDevDocsOpsOverview", "label": "Overview" }, + { + "label": "Initiatives", + "items": [ + { + "id": "kibDevDocsOpsPackages" + } + ] + }, { "label": "CI", "items": [ @@ -540,6 +548,21 @@ }, { "id": "kibDevDocsOpsRepoSourceClassifier" + }, + { + "id": "kibDevDocsOpsJsonc" + }, + { + "id": "kibDevDocsOpsKibanaManifestParser" + }, + { + "id": "kibDevDocsOpsKibanaManifestSchema" + }, + { + "id": "kibDevDocsOpsManagedVscodeConfig" + }, + { + "id": "kibDevDocsOpsManagedVscodeConfigCli" } ] } diff --git a/package.json b/package.json index ad7b41a63d069..6ba3c8023e4c1 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "@elastic/apm-rum": "^5.12.0", "@elastic/apm-rum-react": "^1.4.2", "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", - "@elastic/charts": "47.0.0", + "@elastic/charts": "47.1.1", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.3.0-canary.1", "@elastic/ems-client": "8.3.3", @@ -234,6 +234,9 @@ "@kbn/core-preboot-server-mocks": "link:bazel-bin/packages/core/preboot/core-preboot-server-mocks", "@kbn/core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser", "@kbn/core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server", + "@kbn/core-saved-objects-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser", + "@kbn/core-saved-objects-browser-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-internal", + "@kbn/core-saved-objects-browser-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-mocks", "@kbn/core-saved-objects-common": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-common", "@kbn/core-saved-objects-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server", "@kbn/core-test-helpers-deprecations-getters": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters", @@ -249,6 +252,7 @@ "@kbn/crypto-browser": "link:bazel-bin/packages/kbn-crypto-browser", "@kbn/datemath": "link:bazel-bin/packages/kbn-datemath", "@kbn/doc-links": "link:bazel-bin/packages/kbn-doc-links", + "@kbn/ebt-tools": "link:bazel-bin/packages/kbn-ebt-tools", "@kbn/es-errors": "link:bazel-bin/packages/kbn-es-errors", "@kbn/es-query": "link:bazel-bin/packages/kbn-es-query", "@kbn/eslint-plugin-disable": "link:bazel-bin/packages/kbn-eslint-plugin-disable", @@ -264,7 +268,9 @@ "@kbn/i18n-react": "link:bazel-bin/packages/kbn-i18n-react", "@kbn/interpreter": "link:bazel-bin/packages/kbn-interpreter", "@kbn/io-ts-utils": "link:bazel-bin/packages/kbn-io-ts-utils", - "@kbn/kibana-json-schema": "link:bazel-bin/packages/kbn-kibana-json-schema", + "@kbn/jsonc": "link:bazel-bin/packages/kbn-jsonc", + "@kbn/kibana-manifest-parser": "link:bazel-bin/packages/kbn-kibana-manifest-parser", + "@kbn/kibana-manifest-schema": "link:bazel-bin/packages/kbn-kibana-manifest-schema", "@kbn/logging": "link:bazel-bin/packages/kbn-logging", "@kbn/logging-mocks": "link:bazel-bin/packages/kbn-logging-mocks", "@kbn/mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl", @@ -326,6 +332,7 @@ "@kbn/ui-shared-deps-npm": "link:bazel-bin/packages/kbn-ui-shared-deps-npm", "@kbn/ui-shared-deps-src": "link:bazel-bin/packages/kbn-ui-shared-deps-src", "@kbn/ui-theme": "link:bazel-bin/packages/kbn-ui-theme", + "@kbn/user-profile-components": "link:bazel-bin/packages/kbn-user-profile-components", "@kbn/utility-types": "link:bazel-bin/packages/kbn-utility-types", "@kbn/utility-types-jest": "link:bazel-bin/packages/kbn-utility-types-jest", "@kbn/utils": "link:bazel-bin/packages/kbn-utils", @@ -636,6 +643,8 @@ "@kbn/get-repo-files": "link:bazel-bin/packages/kbn-get-repo-files", "@kbn/import-resolver": "link:bazel-bin/packages/kbn-import-resolver", "@kbn/jest-serializers": "link:bazel-bin/packages/kbn-jest-serializers", + "@kbn/managed-vscode-config": "link:bazel-bin/packages/kbn-managed-vscode-config", + "@kbn/managed-vscode-config-cli": "link:bazel-bin/packages/kbn-managed-vscode-config-cli", "@kbn/optimizer": "link:bazel-bin/packages/kbn-optimizer", "@kbn/optimizer-webpack-helpers": "link:bazel-bin/packages/kbn-optimizer-webpack-helpers", "@kbn/performance-testing-dataset-extractor": "link:bazel-bin/packages/kbn-performance-testing-dataset-extractor", @@ -643,7 +652,6 @@ "@kbn/plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers", "@kbn/repo-source-classifier": "link:bazel-bin/packages/kbn-repo-source-classifier", "@kbn/repo-source-classifier-cli": "link:bazel-bin/packages/kbn-repo-source-classifier-cli", - "@kbn/scalability-simulation-generator": "link:bazel-bin/packages/kbn-scalability-simulation-generator", "@kbn/some-dev-log": "link:bazel-bin/packages/kbn-some-dev-log", "@kbn/sort-package-json": "link:bazel-bin/packages/kbn-sort-package-json", "@kbn/spec-to-console": "link:bazel-bin/packages/kbn-spec-to-console", @@ -689,6 +697,7 @@ "@types/apidoc": "^0.22.3", "@types/archiver": "^5.3.1", "@types/babel__core": "^7.1.19", + "@types/babel__generator": "^7.6.4", "@types/babel__helper-plugin-utils": "^7.10.0", "@types/base64-js": "^1.2.5", "@types/chance": "^1.0.0", @@ -747,6 +756,7 @@ "@types/js-search": "^1.4.0", "@types/js-yaml": "^3.11.1", "@types/jsdom": "^16.2.14", + "@types/json-schema": "^7", "@types/json-stable-stringify": "^1.0.32", "@types/json5": "^0.0.30", "@types/jsonwebtoken": "^8.5.6", @@ -867,6 +877,9 @@ "@types/kbn__core-public-internal-base": "link:bazel-bin/packages/core/public/internal-base/npm_module_types", "@types/kbn__core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser/npm_module_types", "@types/kbn__core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server/npm_module_types", + "@types/kbn__core-saved-objects-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser/npm_module_types", + "@types/kbn__core-saved-objects-browser-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-internal/npm_module_types", + "@types/kbn__core-saved-objects-browser-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-mocks/npm_module_types", "@types/kbn__core-saved-objects-common": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-common/npm_module_types", "@types/kbn__core-saved-objects-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server/npm_module_types", "@types/kbn__core-server-internal-base": "link:bazel-bin/packages/core/server/internal-base/npm_module_types", @@ -888,6 +901,7 @@ "@types/kbn__dev-utils": "link:bazel-bin/packages/kbn-dev-utils/npm_module_types", "@types/kbn__doc-links": "link:bazel-bin/packages/kbn-doc-links/npm_module_types", "@types/kbn__docs-utils": "link:bazel-bin/packages/kbn-docs-utils/npm_module_types", + "@types/kbn__ebt-tools": "link:bazel-bin/packages/kbn-ebt-tools/npm_module_types", "@types/kbn__es-archiver": "link:bazel-bin/packages/kbn-es-archiver/npm_module_types", "@types/kbn__es-errors": "link:bazel-bin/packages/kbn-es-errors/npm_module_types", "@types/kbn__es-query": "link:bazel-bin/packages/kbn-es-query/npm_module_types", @@ -908,10 +922,14 @@ "@types/kbn__interpreter": "link:bazel-bin/packages/kbn-interpreter/npm_module_types", "@types/kbn__io-ts-utils": "link:bazel-bin/packages/kbn-io-ts-utils/npm_module_types", "@types/kbn__jest-serializers": "link:bazel-bin/packages/kbn-jest-serializers/npm_module_types", + "@types/kbn__jsonc": "link:bazel-bin/packages/kbn-jsonc/npm_module_types", "@types/kbn__kbn-ci-stats-performance-metrics": "link:bazel-bin/packages/kbn-kbn-ci-stats-performance-metrics/npm_module_types", - "@types/kbn__kibana-json-schema": "link:bazel-bin/packages/kbn-kibana-json-schema/npm_module_types", + "@types/kbn__kibana-manifest-parser": "link:bazel-bin/packages/kbn-kibana-manifest-parser/npm_module_types", + "@types/kbn__kibana-manifest-schema": "link:bazel-bin/packages/kbn-kibana-manifest-schema/npm_module_types", "@types/kbn__logging": "link:bazel-bin/packages/kbn-logging/npm_module_types", "@types/kbn__logging-mocks": "link:bazel-bin/packages/kbn-logging-mocks/npm_module_types", + "@types/kbn__managed-vscode-config": "link:bazel-bin/packages/kbn-managed-vscode-config/npm_module_types", + "@types/kbn__managed-vscode-config-cli": "link:bazel-bin/packages/kbn-managed-vscode-config-cli/npm_module_types", "@types/kbn__mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl/npm_module_types", "@types/kbn__ml-agg-utils": "link:bazel-bin/x-pack/packages/ml/agg_utils/npm_module_types", "@types/kbn__ml-is-populated-object": "link:bazel-bin/x-pack/packages/ml/is_populated_object/npm_module_types", @@ -927,7 +945,6 @@ "@types/kbn__repo-source-classifier": "link:bazel-bin/packages/kbn-repo-source-classifier/npm_module_types", "@types/kbn__repo-source-classifier-cli": "link:bazel-bin/packages/kbn-repo-source-classifier-cli/npm_module_types", "@types/kbn__rule-data-utils": "link:bazel-bin/packages/kbn-rule-data-utils/npm_module_types", - "@types/kbn__scalability-simulation-generator": "link:bazel-bin/packages/kbn-scalability-simulation-generator/npm_module_types", "@types/kbn__securitysolution-autocomplete": "link:bazel-bin/packages/kbn-securitysolution-autocomplete/npm_module_types", "@types/kbn__securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils/npm_module_types", "@types/kbn__securitysolution-hook-utils": "link:bazel-bin/packages/kbn-securitysolution-hook-utils/npm_module_types", @@ -987,6 +1004,7 @@ "@types/kbn__ui-shared-deps-npm": "link:bazel-bin/packages/kbn-ui-shared-deps-npm/npm_module_types", "@types/kbn__ui-shared-deps-src": "link:bazel-bin/packages/kbn-ui-shared-deps-src/npm_module_types", "@types/kbn__ui-theme": "link:bazel-bin/packages/kbn-ui-theme/npm_module_types", + "@types/kbn__user-profile-components": "link:bazel-bin/packages/kbn-user-profile-components/npm_module_types", "@types/kbn__utility-types": "link:bazel-bin/packages/kbn-utility-types/npm_module_types", "@types/kbn__utility-types-jest": "link:bazel-bin/packages/kbn-utility-types-jest/npm_module_types", "@types/kbn__utils": "link:bazel-bin/packages/kbn-utils/npm_module_types", @@ -1085,6 +1103,7 @@ "@yarnpkg/lockfile": "^1.1.0", "abab": "^2.0.4", "aggregate-error": "^3.1.0", + "ajv": "^8.11.0", "antlr4ts-cli": "^0.5.0-alpha.3", "apidoc": "^0.29.0", "apidoc-markdown": "^6.0.0", @@ -1187,7 +1206,7 @@ "jest-styled-components": "^7.0.3", "jimp": "^0.14.0", "jsdom": "13.1.0", - "json-schema-typed": "^7.0.3", + "json-schema-typed": "^8.0.1", "json5": "^1.0.1", "jsondiffpatch": "0.4.1", "license-checker": "^25.0.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index b64608d5e718b..c0957f7ee2bb8 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -102,6 +102,9 @@ filegroup( "//packages/core/preboot/core-preboot-server:build", "//packages/core/saved-objects/core-saved-objects-api-browser:build", "//packages/core/saved-objects/core-saved-objects-api-server:build", + "//packages/core/saved-objects/core-saved-objects-browser-internal:build", + "//packages/core/saved-objects/core-saved-objects-browser-mocks:build", + "//packages/core/saved-objects/core-saved-objects-browser:build", "//packages/core/saved-objects/core-saved-objects-common:build", "//packages/core/saved-objects/core-saved-objects-server:build", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build", @@ -147,6 +150,7 @@ filegroup( "//packages/kbn-dev-utils:build", "//packages/kbn-doc-links:build", "//packages/kbn-docs-utils:build", + "//packages/kbn-ebt-tools:build", "//packages/kbn-es-archiver:build", "//packages/kbn-es-errors:build", "//packages/kbn-es-query:build", @@ -169,9 +173,13 @@ filegroup( "//packages/kbn-interpreter:build", "//packages/kbn-io-ts-utils:build", "//packages/kbn-jest-serializers:build", - "//packages/kbn-kibana-json-schema:build", + "//packages/kbn-jsonc:build", + "//packages/kbn-kibana-manifest-parser:build", + "//packages/kbn-kibana-manifest-schema:build", "//packages/kbn-logging-mocks:build", "//packages/kbn-logging:build", + "//packages/kbn-managed-vscode-config-cli:build", + "//packages/kbn-managed-vscode-config:build", "//packages/kbn-mapbox-gl:build", "//packages/kbn-monaco:build", "//packages/kbn-optimizer-webpack-helpers:build", @@ -184,7 +192,6 @@ filegroup( "//packages/kbn-repo-source-classifier-cli:build", "//packages/kbn-repo-source-classifier:build", "//packages/kbn-rule-data-utils:build", - "//packages/kbn-scalability-simulation-generator:build", "//packages/kbn-securitysolution-autocomplete:build", "//packages/kbn-securitysolution-es-utils:build", "//packages/kbn-securitysolution-hook-utils:build", @@ -228,6 +235,7 @@ filegroup( "//packages/kbn-ui-shared-deps-npm:build", "//packages/kbn-ui-shared-deps-src:build", "//packages/kbn-ui-theme:build", + "//packages/kbn-user-profile-components:build", "//packages/kbn-utility-types-jest:build", "//packages/kbn-utility-types:build", "//packages/kbn-utils:build", @@ -359,6 +367,9 @@ filegroup( "//packages/core/preboot/core-preboot-server:build_types", "//packages/core/saved-objects/core-saved-objects-api-browser:build_types", "//packages/core/saved-objects/core-saved-objects-api-server:build_types", + "//packages/core/saved-objects/core-saved-objects-browser-internal:build_types", + "//packages/core/saved-objects/core-saved-objects-browser-mocks:build_types", + "//packages/core/saved-objects/core-saved-objects-browser:build_types", "//packages/core/saved-objects/core-saved-objects-common:build_types", "//packages/core/saved-objects/core-saved-objects-server:build_types", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build_types", @@ -399,6 +410,7 @@ filegroup( "//packages/kbn-dev-utils:build_types", "//packages/kbn-doc-links:build_types", "//packages/kbn-docs-utils:build_types", + "//packages/kbn-ebt-tools:build_types", "//packages/kbn-es-archiver:build_types", "//packages/kbn-es-errors:build_types", "//packages/kbn-es-query:build_types", @@ -416,9 +428,13 @@ filegroup( "//packages/kbn-interpreter:build_types", "//packages/kbn-io-ts-utils:build_types", "//packages/kbn-jest-serializers:build_types", - "//packages/kbn-kibana-json-schema:build_types", + "//packages/kbn-jsonc:build_types", + "//packages/kbn-kibana-manifest-parser:build_types", + "//packages/kbn-kibana-manifest-schema:build_types", "//packages/kbn-logging-mocks:build_types", "//packages/kbn-logging:build_types", + "//packages/kbn-managed-vscode-config-cli:build_types", + "//packages/kbn-managed-vscode-config:build_types", "//packages/kbn-mapbox-gl:build_types", "//packages/kbn-monaco:build_types", "//packages/kbn-optimizer-webpack-helpers:build_types", @@ -431,7 +447,6 @@ filegroup( "//packages/kbn-repo-source-classifier-cli:build_types", "//packages/kbn-repo-source-classifier:build_types", "//packages/kbn-rule-data-utils:build_types", - "//packages/kbn-scalability-simulation-generator:build_types", "//packages/kbn-securitysolution-autocomplete:build_types", "//packages/kbn-securitysolution-es-utils:build_types", "//packages/kbn-securitysolution-hook-utils:build_types", @@ -469,6 +484,7 @@ filegroup( "//packages/kbn-ui-shared-deps-npm:build_types", "//packages/kbn-ui-shared-deps-src:build_types", "//packages/kbn-ui-theme:build_types", + "//packages/kbn-user-profile-components:build_types", "//packages/kbn-utility-types-jest:build_types", "//packages/kbn-utility-types:build_types", "//packages/kbn-utils:build_types", diff --git a/packages/analytics/client/src/analytics_client/analytics_client.ts b/packages/analytics/client/src/analytics_client/analytics_client.ts index e6815b4415254..57741f098c6ac 100644 --- a/packages/analytics/client/src/analytics_client/analytics_client.ts +++ b/packages/analytics/client/src/analytics_client/analytics_client.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Type } from 'io-ts'; +import type { Mixed } from 'io-ts'; import type { Observable } from 'rxjs'; import { BehaviorSubject, Subject, combineLatest, from, merge } from 'rxjs'; import { @@ -43,7 +43,7 @@ import { ContextService } from './context_service'; import { schemaToIoTs, validateSchema } from '../schema/validation'; interface EventDebugLogMeta extends LogMeta { - ebt_event: Event; + ebt_event: Event; } export class AnalyticsClient implements IAnalyticsClient { @@ -65,7 +65,7 @@ export class AnalyticsClient implements IAnalyticsClient { private readonly shipperRegistered$ = new Subject(); private readonly eventTypeRegistry = new Map< EventType, - EventTypeOpts & { validator?: Type> } + EventTypeOpts & { validator?: Mixed } >(); private readonly contextService: ContextService; private readonly context$ = new BehaviorSubject>({}); @@ -88,7 +88,7 @@ export class AnalyticsClient implements IAnalyticsClient { this.reportEnqueuedEventsWhenClientIsReady(); } - public reportEvent = >( + public reportEvent = ( eventType: EventType, eventData: EventTypeData ) => { @@ -119,14 +119,18 @@ export class AnalyticsClient implements IAnalyticsClient { // If the validator is registered (dev-mode only), perform the validation. if (eventTypeOpts.validator) { - validateSchema(`Event Type '${eventType}'`, eventTypeOpts.validator, eventData); + validateSchema( + `Event Type '${eventType}'`, + eventTypeOpts.validator, + eventData + ); } const event: Event = { timestamp, event_type: eventType, context: this.context$.value, - properties: eventData, + properties: eventData as unknown as Record, }; // debug-logging before checking the opt-in status to help during development diff --git a/packages/analytics/client/src/analytics_client/types.ts b/packages/analytics/client/src/analytics_client/types.ts index 88b60dc100e89..2af29d88b5ceb 100644 --- a/packages/analytics/client/src/analytics_client/types.ts +++ b/packages/analytics/client/src/analytics_client/types.ts @@ -170,7 +170,7 @@ export interface IAnalyticsClient { * @param eventType The event type registered via the `registerEventType` API. * @param eventData The properties matching the schema declared in the `registerEventType` API. */ - reportEvent: >( + reportEvent: ( eventType: EventType, eventData: EventTypeData ) => void; diff --git a/packages/analytics/client/src/events/types.ts b/packages/analytics/client/src/events/types.ts index 0c97bee3fdbb7..78b2f792e9e2b 100644 --- a/packages/analytics/client/src/events/types.ts +++ b/packages/analytics/client/src/events/types.ts @@ -108,7 +108,7 @@ export interface TelemetryCounter { /** * Definition of the full event structure */ -export interface Event { +export interface Event> { /** * The time the event was generated in ISO format. */ @@ -120,7 +120,7 @@ export interface Event { /** * The specific properties of the event type. */ - properties: Record; + properties: Properties; /** * The {@link EventContext} enriched during the processing pipeline. */ diff --git a/packages/analytics/shippers/fullstory/src/format_payload.ts b/packages/analytics/shippers/fullstory/src/format_payload.ts index c55ed2409da50..03873617af8fc 100644 --- a/packages/analytics/shippers/fullstory/src/format_payload.ts +++ b/packages/analytics/shippers/fullstory/src/format_payload.ts @@ -19,7 +19,7 @@ const FULLSTORY_RESERVED_PROPERTIES = [ 'pageName', ]; -export function formatPayload(context: Record): Record { +export function formatPayload(context: object): Record { // format context keys as required for env vars, see docs: https://help.fullstory.com/hc/en-us/articles/360020623234 return Object.fromEntries( Object.entries(context) diff --git a/packages/core/analytics/core-analytics-browser-internal/BUILD.bazel b/packages/core/analytics/core-analytics-browser-internal/BUILD.bazel index 2540ccabd517c..59f18b8f11d4a 100644 --- a/packages/core/analytics/core-analytics-browser-internal/BUILD.bazel +++ b/packages/core/analytics/core-analytics-browser-internal/BUILD.bazel @@ -30,6 +30,7 @@ RUNTIME_DEPS = [ "@npm//rxjs", "@npm//uuid", "//packages/analytics/client", + "//packages/kbn-ebt-tools", "//packages/core/base/core-base-browser-mocks", "//packages/core/injected-metadata/core-injected-metadata-browser-mocks", ] @@ -41,6 +42,7 @@ TYPES_DEPS = [ "@npm//rxjs", "//packages/kbn-logging:npm_module_types", "//packages/analytics/client:npm_module_types", + "//packages/kbn-ebt-tools:npm_module_types", "//packages/core/base/core-base-browser-internal:npm_module_types", "//packages/core/injected-metadata/core-injected-metadata-browser-internal:npm_module_types", "//packages/core/analytics/core-analytics-browser:npm_module_types", diff --git a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.ts b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.ts index 8ae572ae34528..d3514cb80edf3 100644 --- a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.ts +++ b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.test.ts @@ -20,30 +20,150 @@ describe('AnalyticsService', () => { }); test('should register some context providers on creation', async () => { expect(analyticsClientMock.registerContextProvider).toHaveBeenCalledTimes(3); - await expect( - firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[0][0].context$) - ).resolves.toMatchInlineSnapshot(` - Object { - "branch": "branch", - "buildNum": 100, - "buildSha": "buildSha", - "isDev": true, - "isDistributable": false, - "version": "version", - } - `); - await expect( - firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[1][0].context$) - ).resolves.toEqual({ session_id: expect.any(String) }); - await expect( - firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[2][0].context$) - ).resolves.toEqual({ + expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[0][0].context$) + ).toMatchInlineSnapshot(` + Object { + "branch": "branch", + "buildNum": 100, + "buildSha": "buildSha", + "isDev": true, + "isDistributable": false, + "version": "version", + } + `); + expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[1][0].context$) + ).toEqual({ session_id: expect.any(String) }); + expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[2][0].context$) + ).toEqual({ preferred_language: 'en-US', preferred_languages: ['en-US', 'en'], user_agent: expect.any(String), }); }); + test('should register the `performance_metric` and `click` event types on creation', () => { + expect(analyticsClientMock.registerEventType).toHaveBeenCalledTimes(2); + expect(analyticsClientMock.registerEventType.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "eventType": "performance_metric", + "schema": Object { + "duration": Object { + "_meta": Object { + "description": "The main event duration in ms", + }, + "type": "integer", + }, + "eventName": Object { + "_meta": Object { + "description": "The name of the event that is tracked in the metrics i.e. kibana_loaded, kibana_started", + }, + "type": "keyword", + }, + "key1": Object { + "_meta": Object { + "description": "Performance metric label 1", + "optional": true, + }, + "type": "keyword", + }, + "key2": Object { + "_meta": Object { + "description": "Performance metric label 2", + "optional": true, + }, + "type": "keyword", + }, + "key3": Object { + "_meta": Object { + "description": "Performance metric label 3", + "optional": true, + }, + "type": "keyword", + }, + "key4": Object { + "_meta": Object { + "description": "Performance metric label 4", + "optional": true, + }, + "type": "keyword", + }, + "key5": Object { + "_meta": Object { + "description": "Performance metric label 5", + "optional": true, + }, + "type": "keyword", + }, + "meta": Object { + "_meta": Object { + "description": "Meta data that is searchable but not aggregatable", + "optional": true, + }, + "type": "pass_through", + }, + "value1": Object { + "_meta": Object { + "description": "Performance metric value 1", + "optional": true, + }, + "type": "long", + }, + "value2": Object { + "_meta": Object { + "description": "Performance metric value 2", + "optional": true, + }, + "type": "long", + }, + "value3": Object { + "_meta": Object { + "description": "Performance metric value 3", + "optional": true, + }, + "type": "long", + }, + "value4": Object { + "_meta": Object { + "description": "Performance metric value 4", + "optional": true, + }, + "type": "long", + }, + "value5": Object { + "_meta": Object { + "description": "Performance metric value 5", + "optional": true, + }, + "type": "long", + }, + }, + }, + ] + `); + expect(analyticsClientMock.registerEventType.mock.calls[1]).toMatchInlineSnapshot(` + Array [ + Object { + "eventType": "click", + "schema": Object { + "target": Object { + "items": Object { + "_meta": Object { + "description": "The attributes of the clicked element and all its parents in the form \`{attr.name}={attr.value}\`. It allows finding the clicked elements by looking up its attributes like \\"data-test-subj=my-button\\".", + }, + "type": "keyword", + }, + "type": "array", + }, + }, + }, + ] + `); + }); + test('setup should expose all the register APIs, reportEvent and opt-in', () => { const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); expect(analyticsService.setup({ injectedMetadata })).toStrictEqual({ @@ -60,9 +180,9 @@ describe('AnalyticsService', () => { test('setup should register the elasticsearch info context provider (undefined)', async () => { const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); analyticsService.setup({ injectedMetadata }); - await expect( - firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) - ).resolves.toMatchInlineSnapshot(`undefined`); + expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) + ).toMatchInlineSnapshot(`undefined`); }); test('setup should register the elasticsearch info context provider (with info)', async () => { @@ -73,15 +193,15 @@ describe('AnalyticsService', () => { cluster_version: 'version', }); analyticsService.setup({ injectedMetadata }); - await expect( - firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) - ).resolves.toMatchInlineSnapshot(` - Object { - "cluster_name": "cluster_name", - "cluster_uuid": "cluster_uuid", - "cluster_version": "version", - } - `); + expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) + ).toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster_name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "version", + } + `); }); test('setup should expose only the APIs report and opt-in', () => { diff --git a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts index 580fbac92aa5d..938b0b043bc29 100644 --- a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts +++ b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts @@ -9,6 +9,7 @@ import { of } from 'rxjs'; import type { AnalyticsClient } from '@kbn/analytics-client'; import { createAnalytics } from '@kbn/analytics-client'; +import { registerPerformanceMetricEventType } from '@kbn/ebt-tools'; import type { CoreContext } from '@kbn/core-base-browser-internal'; import type { InternalInjectedMetadataSetup } from '@kbn/core-injected-metadata-browser-internal'; import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-browser'; @@ -34,6 +35,7 @@ export class AnalyticsService { }); this.registerBuildInfoAnalyticsContext(core); + registerPerformanceMetricEventType(this.analyticsClient); // We may eventually move the following to the client's package since they are not Kibana-specific // and can benefit other consumers of the client. diff --git a/packages/core/analytics/core-analytics-server-internal/BUILD.bazel b/packages/core/analytics/core-analytics-server-internal/BUILD.bazel index b7ba4ed35a5a3..0ad590f1ee505 100644 --- a/packages/core/analytics/core-analytics-server-internal/BUILD.bazel +++ b/packages/core/analytics/core-analytics-server-internal/BUILD.bazel @@ -28,6 +28,7 @@ NPM_MODULE_EXTRA_FILES = [ RUNTIME_DEPS = [ "@npm//rxjs", "//packages/analytics/client", + "//packages/kbn-ebt-tools", ] TYPES_DEPS = [ @@ -35,6 +36,7 @@ TYPES_DEPS = [ "@npm//@types/jest", "@npm//rxjs", "//packages/analytics/client:npm_module_types", + "//packages/kbn-ebt-tools:npm_module_types", "//packages/core/base/core-base-server-internal:npm_module_types", "//packages/core/analytics/core-analytics-server:npm_module_types", ] diff --git a/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.mocks.ts b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.mocks.ts new file mode 100644 index 0000000000000..3d98cf4392926 --- /dev/null +++ b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.mocks.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { AnalyticsClient } from '@kbn/analytics-client'; +import { Subject } from 'rxjs'; + +export const analyticsClientMock: jest.Mocked = { + optIn: jest.fn(), + reportEvent: jest.fn(), + registerEventType: jest.fn(), + registerContextProvider: jest.fn(), + removeContextProvider: jest.fn(), + registerShipper: jest.fn(), + telemetryCounter$: new Subject(), + shutdown: jest.fn(), +}; + +jest.doMock('@kbn/analytics-client', () => ({ + createAnalytics: () => analyticsClientMock, +})); diff --git a/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.ts b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.ts new file mode 100644 index 0000000000000..2609859ae9d8c --- /dev/null +++ b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.test.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { firstValueFrom, Observable } from 'rxjs'; +import { mockCoreContext } from '@kbn/core-base-server-mocks'; +import { analyticsClientMock } from './analytics_service.test.mocks'; +import { AnalyticsService } from './analytics_service'; + +describe('AnalyticsService', () => { + let analyticsService: AnalyticsService; + beforeEach(() => { + jest.clearAllMocks(); + analyticsService = new AnalyticsService(mockCoreContext.create()); + }); + + test('should register the context provider `build info` on creation', async () => { + expect(analyticsClientMock.registerContextProvider).toHaveBeenCalledTimes(1); + await expect( + await firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[0][0].context$) + ).toMatchInlineSnapshot(` + Object { + "branch": "main", + "buildNum": 9007199254740991, + "buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "isDev": true, + "isDistributable": false, + "version": "8.5.0", + } + `); + }); + + test('should register the `performance_metric` event type on creation', () => { + expect(analyticsClientMock.registerEventType).toHaveBeenCalledTimes(1); + expect(analyticsClientMock.registerEventType.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "eventType": "performance_metric", + "schema": Object { + "duration": Object { + "_meta": Object { + "description": "The main event duration in ms", + }, + "type": "integer", + }, + "eventName": Object { + "_meta": Object { + "description": "The name of the event that is tracked in the metrics i.e. kibana_loaded, kibana_started", + }, + "type": "keyword", + }, + "key1": Object { + "_meta": Object { + "description": "Performance metric label 1", + "optional": true, + }, + "type": "keyword", + }, + "key2": Object { + "_meta": Object { + "description": "Performance metric label 2", + "optional": true, + }, + "type": "keyword", + }, + "key3": Object { + "_meta": Object { + "description": "Performance metric label 3", + "optional": true, + }, + "type": "keyword", + }, + "key4": Object { + "_meta": Object { + "description": "Performance metric label 4", + "optional": true, + }, + "type": "keyword", + }, + "key5": Object { + "_meta": Object { + "description": "Performance metric label 5", + "optional": true, + }, + "type": "keyword", + }, + "meta": Object { + "_meta": Object { + "description": "Meta data that is searchable but not aggregatable", + "optional": true, + }, + "type": "pass_through", + }, + "value1": Object { + "_meta": Object { + "description": "Performance metric value 1", + "optional": true, + }, + "type": "long", + }, + "value2": Object { + "_meta": Object { + "description": "Performance metric value 2", + "optional": true, + }, + "type": "long", + }, + "value3": Object { + "_meta": Object { + "description": "Performance metric value 3", + "optional": true, + }, + "type": "long", + }, + "value4": Object { + "_meta": Object { + "description": "Performance metric value 4", + "optional": true, + }, + "type": "long", + }, + "value5": Object { + "_meta": Object { + "description": "Performance metric value 5", + "optional": true, + }, + "type": "long", + }, + }, + }, + ] + `); + }); + + test('setup should expose all the register APIs, reportEvent and opt-in', () => { + expect(analyticsService.setup()).toStrictEqual({ + registerShipper: expect.any(Function), + registerContextProvider: expect.any(Function), + removeContextProvider: expect.any(Function), + registerEventType: expect.any(Function), + reportEvent: expect.any(Function), + optIn: expect.any(Function), + telemetryCounter$: expect.any(Observable), + }); + }); + + test('setup should expose only the APIs report and opt-in', () => { + expect(analyticsService.start()).toStrictEqual({ + reportEvent: expect.any(Function), + optIn: expect.any(Function), + telemetryCounter$: expect.any(Observable), + }); + }); +}); diff --git a/packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts index 0fa96ebe0ae51..46b0726660e4c 100644 --- a/packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts +++ b/packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts @@ -9,6 +9,7 @@ import { of } from 'rxjs'; import type { AnalyticsClient } from '@kbn/analytics-client'; import { createAnalytics } from '@kbn/analytics-client'; +import { registerPerformanceMetricEventType } from '@kbn/ebt-tools'; import type { CoreContext } from '@kbn/core-base-server-internal'; import type { AnalyticsServiceSetup, @@ -29,6 +30,7 @@ export class AnalyticsService { }); this.registerBuildInfoAnalyticsContext(core); + registerPerformanceMetricEventType(this.analyticsClient); } public preboot(): AnalyticsServicePreboot { diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-browser-internal/BUILD.bazel new file mode 100644 index 0000000000000..6e489170375fc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/BUILD.bazel @@ -0,0 +1,117 @@ +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-saved-objects-browser-internal" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-browser-internal" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//lodash", + "//packages/elastic-safer-lodash-set", + ### test dependencies + "//packages/core/http/core-http-browser-mocks", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//lodash", + "//packages/elastic-safer-lodash-set:npm_module_types", + "//packages/core/base/core-base-browser-internal:npm_module_types", + "//packages/core/http/core-http-browser:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-common:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-browser:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-api-server:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-api-browser:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/README.md b/packages/core/saved-objects/core-saved-objects-browser-internal/README.md new file mode 100644 index 0000000000000..0ba18ba699b42 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/README.md @@ -0,0 +1,6 @@ +# @kbn/core-saved-objects-browser-internal + +This package contains the implementation, internal types and tests for Core's browser-side saved objects service. + +Note: the types related to the savedObjects client and repository APIs can be found in the `@kbn/core-saved-objects-api-browser` package. + diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/jest.config.js b/packages/core/saved-objects/core-saved-objects-browser-internal/jest.config.js new file mode 100644 index 0000000000000..0c122581f1a55 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-browser-internal'], +}; diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/package.json b/packages/core/saved-objects/core-saved-objects-browser-internal/package.json new file mode 100644 index 0000000000000..dcb18eba421c5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-saved-objects-browser-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/public/saved_objects/index.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/index.ts similarity index 89% rename from src/core/public/saved_objects/index.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/index.ts index cb3ab31cea5c8..0b7d08a7f0b18 100644 --- a/src/core/public/saved_objects/index.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/index.ts @@ -9,4 +9,3 @@ export { SavedObjectsService } from './saved_objects_service'; export type { SavedObjectsClient } from './saved_objects_client'; export { SimpleSavedObjectImpl } from './simple_saved_object'; -export type { SavedObjectsStart } from './saved_objects_service'; diff --git a/src/core/public/saved_objects/saved_objects_client.test.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts similarity index 100% rename from src/core/public/saved_objects/saved_objects_client.test.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.test.ts diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts similarity index 100% rename from src/core/public/saved_objects/saved_objects_client.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts diff --git a/src/core/public/saved_objects/saved_objects_service.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts similarity index 78% rename from src/core/public/saved_objects/saved_objects_service.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts index 776e35700a0af..111d98bfcc126 100644 --- a/src/core/public/saved_objects/saved_objects_service.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_service.ts @@ -8,17 +8,9 @@ import type { CoreService } from '@kbn/core-base-browser-internal'; import type { HttpStart } from '@kbn/core-http-browser'; -import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-browser'; +import type { SavedObjectsStart } from '@kbn/core-saved-objects-browser'; import { SavedObjectsClient } from './saved_objects_client'; -/** - * @public - */ -export interface SavedObjectsStart { - /** {@link SavedObjectsClientContract} */ - client: SavedObjectsClientContract; -} - export class SavedObjectsService implements CoreService { public async setup() {} diff --git a/src/core/public/saved_objects/simple_saved_object.test.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.test.ts similarity index 100% rename from src/core/public/saved_objects/simple_saved_object.test.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.test.ts diff --git a/src/core/public/saved_objects/simple_saved_object.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts similarity index 100% rename from src/core/public/saved_objects/simple_saved_object.ts rename to packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-browser-internal/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-browser-mocks/BUILD.bazel new file mode 100644 index 0000000000000..c1f827840f738 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/BUILD.bazel @@ -0,0 +1,109 @@ +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-saved-objects-browser-mocks" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-browser-mocks" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/core/saved-objects/core-saved-objects-browser-internal" +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-utility-types:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-browser:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-browser-internal:npm_module_types" +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/README.md b/packages/core/saved-objects/core-saved-objects-browser-mocks/README.md new file mode 100644 index 0000000000000..5c3e17e59876e --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/README.md @@ -0,0 +1,3 @@ +# @kbn/core-saved-objects-browser-mocks + +This package contains the mocks for core's browser-side saved objects service. diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/jest.config.js b/packages/core/saved-objects/core-saved-objects-browser-mocks/jest.config.js new file mode 100644 index 0000000000000..25e2ef97a1d09 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-browser-mocks'], +}; diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/package.json b/packages/core/saved-objects/core-saved-objects-browser-mocks/package.json new file mode 100644 index 0000000000000..d05e22ddfda3f --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-saved-objects-browser-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/src/index.ts b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/index.ts new file mode 100644 index 0000000000000..f10ac6b53f2c9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { savedObjectsServiceMock } from './saved_objects_service.mock'; +export { simpleSavedObjectMock } from './simple_saved_object.mock'; diff --git a/src/core/public/saved_objects/saved_objects_service.mock.ts b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts similarity index 74% rename from src/core/public/saved_objects/saved_objects_service.mock.ts rename to packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts index 8e483094eed4b..0caa572238807 100644 --- a/src/core/public/saved_objects/saved_objects_service.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/saved_objects_service.mock.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { SavedObjectsService, SavedObjectsStart } from './saved_objects_service'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { SavedObjectsService } from '@kbn/core-saved-objects-browser-internal'; +import type { SavedObjectsStart } from '@kbn/core-saved-objects-browser'; + +type SavedObjectsServiceContract = PublicMethodsOf; const createStartContractMock = () => { const mock: jest.Mocked = { @@ -27,7 +31,7 @@ const createStartContractMock = () => { }; const createMock = () => { - const mocked: jest.Mocked = { + const mocked: jest.Mocked = { setup: jest.fn(), start: jest.fn(), stop: jest.fn(), diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts new file mode 100644 index 0000000000000..6ed402ccc30c5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-browser'; +import { SimpleSavedObjectImpl } from '@kbn/core-saved-objects-browser-internal'; +import type { SavedObject } from '@kbn/core-saved-objects-common'; + +const createSimpleSavedObjectMock = ( + client: SavedObjectsClientContract, + savedObject: SavedObject +) => new SimpleSavedObjectImpl(client, savedObject); + +export const simpleSavedObjectMock = { + create: createSimpleSavedObjectMock, +}; diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/tsconfig.json b/packages/core/saved-objects/core-saved-objects-browser-mocks/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/core/saved-objects/core-saved-objects-browser/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-browser/BUILD.bazel new file mode 100644 index 0000000000000..17fb2cdf203b9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/BUILD.bazel @@ -0,0 +1,106 @@ +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-saved-objects-browser" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-browser" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +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/saved-objects/core-saved-objects-api-browser:npm_module_types" +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-browser/README.md b/packages/core/saved-objects/core-saved-objects-browser/README.md new file mode 100644 index 0000000000000..ee5a54934313d --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/README.md @@ -0,0 +1,5 @@ +# @kbn/core-saved-objects-browser + +This package contains the public types for core's browser-side saved objects service. + +Note: the types related to the savedObjects client and repository APIs can be found in the `@kbn/core-saved-objects-api-browser` package. diff --git a/packages/core/saved-objects/core-saved-objects-browser/jest.config.js b/packages/core/saved-objects/core-saved-objects-browser/jest.config.js new file mode 100644 index 0000000000000..2765d4ead2322 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-browser'], +}; diff --git a/packages/core/saved-objects/core-saved-objects-browser/package.json b/packages/core/saved-objects/core-saved-objects-browser/package.json new file mode 100644 index 0000000000000..6494337430596 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/core-saved-objects-browser", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/saved-objects/core-saved-objects-browser/src/contracts.ts b/packages/core/saved-objects/core-saved-objects-browser/src/contracts.ts new file mode 100644 index 0000000000000..c372f3169ed80 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/src/contracts.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-browser'; + +/** + * @public + */ +export interface SavedObjectsStart { + /** {@link SavedObjectsClientContract} */ + client: SavedObjectsClientContract; +} diff --git a/packages/kbn-kibana-json-schema/src/index.ts b/packages/core/saved-objects/core-saved-objects-browser/src/index.ts similarity index 86% rename from packages/kbn-kibana-json-schema/src/index.ts rename to packages/core/saved-objects/core-saved-objects-browser/src/index.ts index 57d8be3e215e6..d9333ccedd329 100644 --- a/packages/kbn-kibana-json-schema/src/index.ts +++ b/packages/core/saved-objects/core-saved-objects-browser/src/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { KibanaJsonSchema } from './kibana_json_schema'; +export type { SavedObjectsStart } from './contracts'; diff --git a/packages/core/saved-objects/core-saved-objects-browser/tsconfig.json b/packages/core/saved-objects/core-saved-objects-browser/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-browser/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_extra_actions.tsx b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_extra_actions.tsx index 9d8d1fe590c9e..e7cdba71008f9 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_extra_actions.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_extra_actions.tsx @@ -51,7 +51,7 @@ export function ColorRangesExtraActions({ }); return ( - + ) => { const prevStartValue = colorRanges[index - 1]?.start ?? Number.NEGATIVE_INFINITY; @@ -162,15 +166,28 @@ export function ColorRangeItem({ } ); + const styles = useMemo( + () => css` + display: block; + min-width: ${euiTheme.size.xl}; + text-align: center; + `, + [euiTheme.size.xl] + ); + return ( - + {!isLast ? ( + ) : ( ) : ( - + )} diff --git a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx b/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx index 3bca5955182a9..2822402981e0d 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx @@ -76,10 +76,8 @@ export const CustomizablePalette = ({ const styles = useMemo( () => css` padding: ${euiTheme.size.base}; - background-color: ${euiTheme.colors.lightestShade}; - border-bottom: ${euiTheme.border.thin}; `, - [euiTheme.size.base, euiTheme.colors.lightestShade, euiTheme.border.thin] + [euiTheme.size.base] ); return ( diff --git a/packages/kbn-dev-utils/BUILD.bazel b/packages/kbn-dev-utils/BUILD.bazel index 8222acb1c3a00..303ce379cfd16 100644 --- a/packages/kbn-dev-utils/BUILD.bazel +++ b/packages/kbn-dev-utils/BUILD.bazel @@ -50,7 +50,7 @@ RUNTIME_DEPS = [ "//packages/kbn-stdio-dev-helpers", "//packages/kbn-ci-stats-reporter", "//packages/kbn-jest-serializers", - "//packages/kbn-kibana-json-schema", + "//packages/kbn-kibana-manifest-schema", "@npm//@babel/core", "@npm//axios", "@npm//chalk", @@ -84,7 +84,7 @@ TYPES_DEPS = [ "//packages/kbn-stdio-dev-helpers:npm_module_types", "//packages/kbn-ci-stats-reporter:npm_module_types", "//packages/kbn-jest-serializers:npm_module_types", - "//packages/kbn-kibana-json-schema:npm_module_types", + "//packages/kbn-kibana-manifest-schema:npm_module_types", "@npm//@babel/parser", "@npm//@babel/types", "@npm//@types/babel__core", diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index ddaa0ff37b47a..edaf2b2f47927 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -25,5 +25,4 @@ export * from './plugin_list'; export * from './streams'; export * from './babel'; export * from './extract'; -export * from './vscode_config'; export * from './diff_strings'; diff --git a/packages/kbn-ebt-tools/BUILD.bazel b/packages/kbn-ebt-tools/BUILD.bazel new file mode 100644 index 0000000000000..e4c4209361906 --- /dev/null +++ b/packages/kbn-ebt-tools/BUILD.bazel @@ -0,0 +1,101 @@ +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_BASE_NAME = "kbn-ebt-tools" +PKG_REQUIRE_NAME = "@kbn/ebt-tools" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = ["**/*.test.*"], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +RUNTIME_DEPS = [] + +TYPES_DEPS = [ + "//packages/analytics/client:npm_module_types", + "@npm//@types/jest", + "@npm//@types/node", +] + +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", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-ebt-tools/README.md b/packages/kbn-ebt-tools/README.md new file mode 100644 index 0000000000000..72aae680b49b8 --- /dev/null +++ b/packages/kbn-ebt-tools/README.md @@ -0,0 +1,3 @@ +# @kbn/ebt-tools + +Shared tools for event based telemetry \ No newline at end of file diff --git a/packages/kbn-ebt-tools/jest.config.js b/packages/kbn-ebt-tools/jest.config.js new file mode 100644 index 0000000000000..56faeaaa0b324 --- /dev/null +++ b/packages/kbn-ebt-tools/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-ebt-tools'], +}; diff --git a/packages/kbn-ebt-tools/package.json b/packages/kbn-ebt-tools/package.json new file mode 100644 index 0000000000000..5e5136966b5f9 --- /dev/null +++ b/packages/kbn-ebt-tools/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/ebt-tools", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "browser": "./target_web/index.js", + "main": "./target_node/index.js", + "private": true +} \ No newline at end of file diff --git a/packages/kbn-scalability-simulation-generator/src/index.ts b/packages/kbn-ebt-tools/src/index.ts similarity index 83% rename from packages/kbn-scalability-simulation-generator/src/index.ts rename to packages/kbn-ebt-tools/src/index.ts index 108817e96f603..9357513d40f55 100644 --- a/packages/kbn-scalability-simulation-generator/src/index.ts +++ b/packages/kbn-ebt-tools/src/index.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export { generator } from './generate_files'; -export * from './cli'; +export * from './performance_metric_events'; diff --git a/packages/kbn-ebt-tools/src/performance_metric_events/helpers.test.ts b/packages/kbn-ebt-tools/src/performance_metric_events/helpers.test.ts new file mode 100644 index 0000000000000..a6ad3970a6a97 --- /dev/null +++ b/packages/kbn-ebt-tools/src/performance_metric_events/helpers.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createAnalytics, type AnalyticsClient } from '@kbn/analytics-client'; +import { loggerMock } from '@kbn/logging-mocks'; +import { registerPerformanceMetricEventType, reportPerformanceMetricEvent } from './helpers'; +import { METRIC_EVENT_SCHEMA } from './schema'; + +describe('performance metric event helpers', () => { + let analyticsClient: AnalyticsClient; + + describe('registerPerformanceMetricEventType', () => { + beforeEach(() => { + analyticsClient = createAnalytics({ + isDev: true, // Explicitly setting `true` to ensure we have event validation to make sure the events sent pass our validation. + sendTo: 'staging', + logger: loggerMock.create(), + }); + }); + + test('registers the `performance_metric` eventType to the analytics client', () => { + const registerEventTypeSpy = jest.spyOn(analyticsClient, 'registerEventType'); + + expect(() => registerPerformanceMetricEventType(analyticsClient)).not.toThrow(); + + expect(registerEventTypeSpy).toHaveBeenCalledWith({ + eventType: 'performance_metric', + schema: METRIC_EVENT_SCHEMA, + }); + }); + }); + + describe('reportPerformanceMetricEvent', () => { + beforeEach(() => { + analyticsClient = createAnalytics({ + isDev: true, // Explicitly setting `true` to ensure we have event validation to make sure the events sent pass our validation. + sendTo: 'staging', + logger: loggerMock.create(), + }); + registerPerformanceMetricEventType(analyticsClient); + }); + + test('reports the minimum allowed event', () => { + reportPerformanceMetricEvent(analyticsClient, { eventName: 'test-event', duration: 1000 }); + }); + + test('reports all the allowed fields in the event', () => { + reportPerformanceMetricEvent(analyticsClient, { + eventName: 'test-event', + meta: { my: { custom: { fields: 'here' } }, another_field: true, status: 'something' }, + duration: 10, + key1: 'something', + value1: 10, + key2: 'something', + value2: 10, + key3: 'something', + value3: 10, + key4: 'something', + value4: 10, + key5: 'something', + value5: 10, + }); + }); + + test('should fail if eventName and duration is missing', () => { + expect(() => + reportPerformanceMetricEvent( + analyticsClient, + // @ts-expect-error + {} + ) + ).toThrowErrorMatchingInlineSnapshot(` + "Failed to validate payload coming from \\"Event Type 'performance_metric'\\": + - [eventName]: {\\"expected\\":\\"string\\",\\"actual\\":\\"undefined\\",\\"value\\":\\"undefined\\"} + - [duration]: {\\"expected\\":\\"number\\",\\"actual\\":\\"undefined\\",\\"value\\":\\"undefined\\"}" + `); + }); + + test('should fail if any additional unknown keys are added', () => { + expect(() => + reportPerformanceMetricEvent(analyticsClient, { + eventName: 'test-event', + duration: 1000, + // @ts-expect-error + an_unknown_field: 'blah', + }) + ).toThrowErrorMatchingInlineSnapshot(` + "Failed to validate payload coming from \\"Event Type 'performance_metric'\\": + - []: excess key 'an_unknown_field' found" + `); + }); + }); +}); diff --git a/packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts b/packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts new file mode 100644 index 0000000000000..ed971118687c3 --- /dev/null +++ b/packages/kbn-ebt-tools/src/performance_metric_events/helpers.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { AnalyticsClient } from '@kbn/analytics-client'; +import { type PerformanceMetricEvent, METRIC_EVENT_SCHEMA } from './schema'; + +const PERFORMANCE_METRIC_EVENT_TYPE = 'performance_metric'; + +/** + * Register the `performance_metric` event type + * @param analytics The {@link AnalyticsClient} during the setup phase (it has the method `registerEventType`) + * @private To be called only by core's Analytics Service + */ +export function registerPerformanceMetricEventType( + analytics: Pick +) { + analytics.registerEventType({ + eventType: PERFORMANCE_METRIC_EVENT_TYPE, + schema: METRIC_EVENT_SCHEMA, + }); +} + +/** + * Report a `performance_metric` event type. + * @param analytics The {@link AnalyticsClient} to report the events. + * @param eventData The data to send, conforming the structure of a {@link MetricEvent}. + */ +export function reportPerformanceMetricEvent( + analytics: Pick, + eventData: PerformanceMetricEvent +) { + analytics.reportEvent(PERFORMANCE_METRIC_EVENT_TYPE, eventData); +} diff --git a/packages/kbn-ebt-tools/src/performance_metric_events/index.ts b/packages/kbn-ebt-tools/src/performance_metric_events/index.ts new file mode 100644 index 0000000000000..0002b082754dd --- /dev/null +++ b/packages/kbn-ebt-tools/src/performance_metric_events/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { PerformanceMetricEvent as MetricEvent } from './schema'; +export { + registerPerformanceMetricEventType as registerPerformanceMetricEventType, + reportPerformanceMetricEvent, +} from './helpers'; diff --git a/packages/kbn-ebt-tools/src/performance_metric_events/schema.ts b/packages/kbn-ebt-tools/src/performance_metric_events/schema.ts new file mode 100644 index 0000000000000..ed0b3a8eefde5 --- /dev/null +++ b/packages/kbn-ebt-tools/src/performance_metric_events/schema.ts @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { RootSchema } from '@kbn/analytics-client'; + +/** + * Structure of the `metric` event + */ +export interface PerformanceMetricEvent { + /** + * The name of the event that is tracked in the metrics i.e. kibana_loaded, kibana_started + */ + eventName: string; + /** + * Searchable but not aggregateable metadata relevant to the tracked action. + */ + meta?: Record; + + /** + * @group Standardized fields + * The time (in milliseconds) it took to run the entire action. + */ + duration: number; + + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Description label for the metric 1 + */ + key1?: string; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Value for the metric 1 + */ + value1?: number; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Description label for the metric 2 + */ + key2?: string; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Value for the metric 2 + */ + value2?: number; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Description label for the metric 3 + */ + key3?: string; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Value for the metric 3 + */ + value3?: number; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Description label for the metric 4 + */ + key4?: string; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Value for the metric 4 + */ + value4?: number; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Description label for the metric 5 + */ + key5?: string; + /** + * @group Free fields for custom metrics (searchable and aggregateable) + * Value for the metric 5 + */ + value5?: number; +} + +export const METRIC_EVENT_SCHEMA: RootSchema = { + eventName: { + type: 'keyword', + _meta: { + description: + 'The name of the event that is tracked in the metrics i.e. kibana_loaded, kibana_started', + }, + }, + meta: { + type: 'pass_through', + _meta: { description: 'Meta data that is searchable but not aggregatable', optional: true }, + }, + duration: { + type: 'integer', + _meta: { description: 'The main event duration in ms' }, + }, + key1: { + type: 'keyword', + _meta: { description: 'Performance metric label 1', optional: true }, + }, + value1: { + type: 'long', + _meta: { description: 'Performance metric value 1', optional: true }, + }, + key2: { + type: 'keyword', + _meta: { description: 'Performance metric label 2', optional: true }, + }, + value2: { + type: 'long', + _meta: { description: 'Performance metric value 2', optional: true }, + }, + key3: { + type: 'keyword', + _meta: { description: 'Performance metric label 3', optional: true }, + }, + value3: { + type: 'long', + _meta: { description: 'Performance metric value 3', optional: true }, + }, + key4: { + type: 'keyword', + _meta: { description: 'Performance metric label 4', optional: true }, + }, + value4: { + type: 'long', + _meta: { description: 'Performance metric value 4', optional: true }, + }, + key5: { + type: 'keyword', + _meta: { description: 'Performance metric label 5', optional: true }, + }, + value5: { + type: 'long', + _meta: { description: 'Performance metric value 5', optional: true }, + }, +}; diff --git a/packages/kbn-ebt-tools/tsconfig.json b/packages/kbn-ebt-tools/tsconfig.json new file mode 100644 index 0000000000000..bb93370ed412f --- /dev/null +++ b/packages/kbn-ebt-tools/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-jsonc/BUILD.bazel b/packages/kbn-jsonc/BUILD.bazel new file mode 100644 index 0000000000000..41620e12bead7 --- /dev/null +++ b/packages/kbn-jsonc/BUILD.bazel @@ -0,0 +1,117 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-jsonc" +PKG_REQUIRE_NAME = "@kbn/jsonc" + +SOURCE_FILES = glob( + [ + "src/**/*.js", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", +] + +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, + allow_js = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-jsonc/README.mdx b/packages/kbn-jsonc/README.mdx new file mode 100644 index 0000000000000..79ab8fc52bd92 --- /dev/null +++ b/packages/kbn-jsonc/README.mdx @@ -0,0 +1,20 @@ +--- +id: kibDevDocsOpsJsonc +slug: /kibana-dev-docs/ops/jsonc +title: "@kbn/jsonc" +description: A package for parsing jsonc +date: 2022-05-24 +tags: ['kibana', 'dev', 'contributor', 'operations', 'json', 'jsonc'] +--- + +This package exposes a simple `parse(jsonc: string)` function for parsing JSON-C content. JSON-C is a variant of JSON which supports both block and line comments, which are incredibly useful for configuration files that are committed to the repository and would benefit from the context of comments. + +Additionally supported in JSON-C... TRAILING COMMAS! + +VSCode and TypeScript use jsonc for their config files, so we're already using it in a lot of places, but we're going to start using it in more places too. This package is implemented in vanilla JS with a vendored copy of [`strip-json-comments`](https://github.com/sindresorhus/strip-json-comments) so that we can use this code from kbn_pm without node modules installed. + +## API + +### parse(jsonc: string): any + +Parses a JSON-C string into the value defined by the content. \ No newline at end of file diff --git a/packages/kbn-scalability-simulation-generator/jest.config.js b/packages/kbn-jsonc/jest.config.js similarity index 86% rename from packages/kbn-scalability-simulation-generator/jest.config.js rename to packages/kbn-jsonc/jest.config.js index c8a3f90fa670b..c958647e1c154 100644 --- a/packages/kbn-scalability-simulation-generator/jest.config.js +++ b/packages/kbn-jsonc/jest.config.js @@ -9,5 +9,5 @@ module.exports = { preset: '@kbn/test/jest_node', rootDir: '../..', - roots: ['/packages/kbn-scalability-simulation-generator'], + roots: ['/packages/kbn-jsonc'], }; diff --git a/packages/kbn-kibana-json-schema/package.json b/packages/kbn-jsonc/package.json similarity index 77% rename from packages/kbn-kibana-json-schema/package.json rename to packages/kbn-jsonc/package.json index 12df4c2d199a2..73fc5acfe447c 100644 --- a/packages/kbn-kibana-json-schema/package.json +++ b/packages/kbn-jsonc/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/kibana-json-schema", + "name": "@kbn/jsonc", "private": true, "version": "1.0.0", "main": "./target_node/index.js", diff --git a/packages/kbn-jsonc/src/index.js b/packages/kbn-jsonc/src/index.js new file mode 100644 index 0000000000000..89ad44c7fdcc9 --- /dev/null +++ b/packages/kbn-jsonc/src/index.js @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const { stripJsonComments } = require('./strip_json_comments'); + +/** + * @param {string} jsonWithComments + */ +function parse(jsonWithComments) { + return JSON.parse( + stripJsonComments(jsonWithComments, { + whitespace: false, + trailingCommas: true, + }) + ); +} + +module.exports = { parse }; diff --git a/packages/kbn-jsonc/src/strip_json_comments.js b/packages/kbn-jsonc/src/strip_json_comments.js new file mode 100644 index 0000000000000..01c3f2932da2a --- /dev/null +++ b/packages/kbn-jsonc/src/strip_json_comments.js @@ -0,0 +1,153 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * @notice + * + * Vendored copy of `strip-json-comments` so that we can use it when npm modules are not available. + * https://github.com/sindresorhus/strip-json-comments/tree/34b79cb0f1129aa85ef4b5c3292e8bc546984ef9 + * + * MIT License + * + * Copyright (c) Sindre Sorhus (https://sindresorhus.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +const singleComment = Symbol('singleComment'); +const multiComment = Symbol('multiComment'); + +const stripWithoutWhitespace = () => ''; + +/** + * @param {string} string + * @param {number | undefined} start + * @param {number | undefined} end + */ +const stripWithWhitespace = (string, start = undefined, end = undefined) => + string.slice(start, end).replace(/\S/g, ' '); + +/** + * @param {string} jsonString + * @param {number} quotePosition + */ +const isEscaped = (jsonString, quotePosition) => { + let index = quotePosition - 1; + let backslashCount = 0; + + while (jsonString[index] === '\\') { + index -= 1; + backslashCount += 1; + } + + return Boolean(backslashCount % 2); +}; + +/** + * @param {string} jsonString + * @param {{ whitespace?: boolean; trailingCommas?: boolean }} options + */ +function stripJsonComments(jsonString, { whitespace = true, trailingCommas = false } = {}) { + if (typeof jsonString !== 'string') { + throw new TypeError( + `Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\`` + ); + } + + const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace; + + let isInsideString = false; + /** @type {boolean | symbol} */ + let isInsideComment = false; + let offset = 0; + let buffer = ''; + let result = ''; + let commaIndex = -1; + + for (let index = 0; index < jsonString.length; index++) { + const currentCharacter = jsonString[index]; + const nextCharacter = jsonString[index + 1]; + + if (!isInsideComment && currentCharacter === '"') { + // Enter or exit string + const escaped = isEscaped(jsonString, index); + if (!escaped) { + isInsideString = !isInsideString; + } + } + + if (isInsideString) { + continue; + } + + if (!isInsideComment && currentCharacter + nextCharacter === '//') { + // Enter single-line comment + buffer += jsonString.slice(offset, index); + offset = index; + isInsideComment = singleComment; + index++; + } else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') { + // Exit single-line comment via \r\n + index++; + isInsideComment = false; + buffer += strip(jsonString, offset, index); + offset = index; + continue; + } else if (isInsideComment === singleComment && currentCharacter === '\n') { + // Exit single-line comment via \n + isInsideComment = false; + buffer += strip(jsonString, offset, index); + offset = index; + } else if (!isInsideComment && currentCharacter + nextCharacter === '/*') { + // Enter multiline comment + buffer += jsonString.slice(offset, index); + offset = index; + isInsideComment = multiComment; + index++; + continue; + } else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') { + // Exit multiline comment + index++; + isInsideComment = false; + buffer += strip(jsonString, offset, index + 1); + offset = index + 1; + continue; + } else if (trailingCommas && !isInsideComment) { + if (commaIndex !== -1) { + if (currentCharacter === '}' || currentCharacter === ']') { + // Strip trailing comma + buffer += jsonString.slice(offset, index); + result += strip(buffer, 0, 1) + buffer.slice(1); + buffer = ''; + offset = index; + commaIndex = -1; + } else if ( + currentCharacter !== ' ' && + currentCharacter !== '\t' && + currentCharacter !== '\r' && + currentCharacter !== '\n' + ) { + // Hit non-whitespace following a comma; comma is not trailing + buffer += jsonString.slice(offset, index); + offset = index; + commaIndex = -1; + } + } else if (currentCharacter === ',') { + // Flush buffer prior to this point, and save new comma index + result += buffer + jsonString.slice(offset, index); + buffer = ''; + offset = index; + commaIndex = index; + } + } + } + + return ( + result + buffer + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset)) + ); +} + +module.exports = { stripJsonComments }; diff --git a/packages/kbn-jsonc/tsconfig.json b/packages/kbn-jsonc/tsconfig.json new file mode 100644 index 0000000000000..9f78bc243ac66 --- /dev/null +++ b/packages/kbn-jsonc/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "allowJs": true, + "checkJs": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-kibana-json-schema/README.md b/packages/kbn-kibana-json-schema/README.md deleted file mode 100644 index f1edda001081b..0000000000000 --- a/packages/kbn-kibana-json-schema/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @kbn/kibana-json-schema - -The JSON Schema used for kibana.json files \ No newline at end of file diff --git a/packages/kbn-kibana-manifest-parser/BUILD.bazel b/packages/kbn-kibana-manifest-parser/BUILD.bazel new file mode 100644 index 0000000000000..17c8bc4a226f6 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/BUILD.bazel @@ -0,0 +1,119 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-kibana-manifest-parser" +PKG_REQUIRE_NAME = "@kbn/kibana-manifest-parser" + +SOURCE_FILES = glob( + [ + "src/**/*.js", + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-jsonc: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, + allow_js = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-kibana-manifest-parser/README.mdx b/packages/kbn-kibana-manifest-parser/README.mdx new file mode 100644 index 0000000000000..52467e94cdcec --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/README.mdx @@ -0,0 +1,24 @@ +--- +id: kibDevDocsOpsKibanaManifestParser +slug: /kibana-dev-docs/ops/kibana-manifest-parser +title: "@kbn/kibana-manifest-parser" +description: A package for parsing Kibana package manifest files +date: 2022-05-24 +tags: ['kibana', 'dev', 'contributor', 'operations', 'package', 'kibana.json', 'kibana.jsonc'] +--- + +This package exposes functions and types for parsing Kibana manifest files (in the new `kibana.jsonc` format) + +## API + +### `parseKibanaManifest(jsonc: string): KibanaPackageManifest` + +Parses a JSON-C string into a valid `KibanaPackageManifest` object. If the manifest is invalid an error is thrown. + +### `readKibanaManifest(path: string): KibanaPackageManifest` + +Read a Kibana manifest from disk and parse it, returning a KibanaPackageManifest. If the file doesn't exist or is invalid in some way an error is thrown. + +### `validateKibanaManifest(value: unknown): KibanaPackageManifest` + +Validate a parsed Kibana manifest. If the manifest is invalid an error is thrown. diff --git a/packages/kbn-kibana-manifest-parser/jest.config.js b/packages/kbn-kibana-manifest-parser/jest.config.js new file mode 100644 index 0000000000000..f50552e3e36e5 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-kibana-manifest-parser'], +}; diff --git a/packages/kbn-kibana-manifest-parser/package.json b/packages/kbn-kibana-manifest-parser/package.json new file mode 100644 index 0000000000000..e2ab7ac2417e9 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/kibana-manifest-parser", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-kibana-manifest-parser/src/index.ts b/packages/kbn-kibana-manifest-parser/src/index.ts new file mode 100644 index 0000000000000..18f51e4986a6e --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/src/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { + parseKibanaManifest, + readKibanaManifest, + validateKibanaManifest, +} from './parse_kibana_manifest'; +export type { KibanaPackageManifest } from './kibana_manifest'; diff --git a/packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts b/packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts new file mode 100644 index 0000000000000..2f99b0293d8a2 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/src/kibana_manifest.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type KibanaPackageType = + | 'plugin-browser' + | 'plugin-server' + | 'shared-browser' + | 'shared-server' + | 'shared-common' + | 'shared-scss' + | 'functional-tests' + | 'test-helper'; + +interface PackageManifestBaseFields { + type: KibanaPackageType; + id: string; + owner: string; + typeDeps: string[]; + runtimeDeps: string[]; +} + +export interface PluginPackageManifest extends PackageManifestBaseFields { + type: 'plugin-browser' | 'plugin-server'; + plugin: { + id: string; + configPath?: string[]; + requiredPlugins?: string[]; + optionalPlugins?: string[]; + description?: string; + enabledOnAnonymousPages?: boolean; + serviceFolders?: string[]; + }; +} + +export interface SharedBrowserPackageManifest extends PackageManifestBaseFields { + type: 'shared-browser' | 'shared-common'; + sharedBrowserBundle?: boolean; +} + +export interface BasePackageManifest extends PackageManifestBaseFields { + type: 'shared-server' | 'functional-tests' | 'test-helper' | 'shared-scss'; +} + +export type KibanaPackageManifest = + | PluginPackageManifest + | SharedBrowserPackageManifest + | BasePackageManifest; diff --git a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts new file mode 100644 index 0000000000000..4743adb1f5e48 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { validateKibanaManifest } from './parse_kibana_manifest'; + +const BASE_FIELDS = { + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [], +}; + +describe('validateKibanaManifest', () => { + it('requires valid type', () => { + expect(() => validateKibanaManifest({})).toThrowErrorMatchingInlineSnapshot( + `"invalid package \\"type\\", options are [functional-tests, plugin-browser, plugin-server, shared-browser, shared-common, shared-server, test-helper, shared-scss]"` + ); + }); + + it('requires valid id', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid package \\"id\\", must be a string that starts with @kbn/"` + ); + }); + + it('requires valid owner', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid package \\"owner\\", must be a valid Github team handle starting with @"` + ); + }); + + it('requires valid typeDeps', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + }) + ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); + + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: false, + }) + ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); + + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [1], + }) + ).toThrowErrorMatchingInlineSnapshot(`"invalid \\"typeDeps\\", must be an array of strings"`); + }); + + it('requires valid runtimeDeps', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid \\"runtimeDeps\\", must be an array of strings"` + ); + + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: false, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid \\"runtimeDeps\\", must be an array of strings"` + ); + + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [1], + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid \\"runtimeDeps\\", must be an array of strings"` + ); + }); + + it('validates base types', () => { + expect( + validateKibanaManifest({ + type: 'shared-server', + ...BASE_FIELDS, + }) + ).toMatchInlineSnapshot(` + Object { + "id": "@kbn/foo", + "owner": "@elastic/kibana-operations", + "runtimeDeps": Array [], + "type": "shared-server", + "typeDeps": Array [], + } + `); + expect( + validateKibanaManifest({ + type: 'functional-tests', + ...BASE_FIELDS, + }) + ).toMatchInlineSnapshot(` + Object { + "id": "@kbn/foo", + "owner": "@elastic/kibana-operations", + "runtimeDeps": Array [], + "type": "functional-tests", + "typeDeps": Array [], + } + `); + expect( + validateKibanaManifest({ + type: 'test-helper', + ...BASE_FIELDS, + }) + ).toMatchInlineSnapshot(` + Object { + "id": "@kbn/foo", + "owner": "@elastic/kibana-operations", + "runtimeDeps": Array [], + "type": "test-helper", + "typeDeps": Array [], + } + `); + }); + + describe('plugin-* types', () => { + it('requires valid plugin for plugin-* types', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [], + }) + ).toThrowErrorMatchingInlineSnapshot(`"invalid package \\"plugin\\", must be an object"`); + }); + + it('requires "id" in plugins', () => { + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [], + plugin: {}, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid \\"plugin.id\\", must be a string in camel or snake case"` + ); + + expect(() => + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [], + plugin: { + id: 'not-camel-case', + }, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"invalid \\"plugin.id\\", must be a string in camel or snake case"` + ); + + expect( + validateKibanaManifest({ + type: 'plugin-browser', + id: '@kbn/foo', + owner: '@elastic/kibana-operations', + typeDeps: [], + runtimeDeps: [], + plugin: { + id: 'camelCase', + }, + }) + ).toMatchInlineSnapshot(` + Object { + "id": "@kbn/foo", + "owner": "@elastic/kibana-operations", + "plugin": Object { + "configPath": undefined, + "description": undefined, + "enabledOnAnonymousPages": undefined, + "id": "camelCase", + "optionalPlugins": undefined, + "requiredPlugins": undefined, + "serviceFolders": undefined, + }, + "runtimeDeps": Array [], + "type": "plugin-browser", + "typeDeps": Array [], + } + `); + }); + }); +}); diff --git a/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts new file mode 100644 index 0000000000000..aeccd08e6d8c0 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/src/parse_kibana_manifest.ts @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Fs from 'fs'; + +import { parse } from '@kbn/jsonc'; +import { PluginPackageManifest, KibanaPackageManifest } from './kibana_manifest'; + +import { + isObj, + isValidId, + isValidPkgType, + isArrOfIds, + isArrOfStrings, + PACKAGE_TYPES, +} from './util'; + +const err = (msg: string) => new Error(msg); + +function validateKibanaManifestPlugin(plugin: unknown): PluginPackageManifest['plugin'] { + if (!isObj(plugin)) { + throw err(`invalid package "plugin", must be an object`); + } + + const { + id, + configPath, + requiredPlugins, + optionalPlugins, + description, + enabledOnAnonymousPages, + serviceFolders, + ...extra + } = plugin; + + const extraKeys = Object.keys(extra); + if (extraKeys.length) { + throw err(`unexpected keys in "plugin" of package [${extraKeys.join(', ')}]`); + } + + if (typeof id !== 'string' || !isValidId(id)) { + throw err(`invalid "plugin.id", must be a string in camel or snake case`); + } + + if (configPath !== undefined && !isArrOfIds(configPath)) { + throw err(`invalid "plugin.configPath", must be an array of strings in camel or snake case`); + } + + if (requiredPlugins !== undefined && !isArrOfIds(requiredPlugins)) { + throw err( + `invalid "plugin.requiredPlugins", must be an array of strings in camel or snake case` + ); + } + + if (optionalPlugins !== undefined && !isArrOfIds(optionalPlugins)) { + throw err( + `invalid "plugin.requiredPlugins", must be an array of strings in camel or snake case` + ); + } + + if (description !== undefined && typeof description !== 'string') { + throw err(`invalid "plugin.description", must be a string`); + } + + if (enabledOnAnonymousPages !== undefined && typeof enabledOnAnonymousPages !== 'boolean') { + throw err(`invalid "plugin.enabledOnAnonymousPages", must be a boolean`); + } + + if (serviceFolders !== undefined && !isArrOfStrings(serviceFolders)) { + throw err(`invalid "plugin.serviceFolders", must be an array of strings`); + } + + return { + id, + configPath, + requiredPlugins, + optionalPlugins, + description, + enabledOnAnonymousPages, + serviceFolders, + }; +} + +/** + * Validate the contents of a parsed kibana.jsonc file. + */ +export function validateKibanaManifest(parsed: unknown): KibanaPackageManifest { + if (!isObj(parsed)) { + throw err('expected root value to be an object'); + } + + const { type, id, owner, typeDeps, runtimeDeps, plugin, sharedBrowserBundle, ...extra } = parsed; + + const extraKeys = Object.keys(extra); + if (extraKeys.length) { + throw err(`unexpected keys in package manifest [${extraKeys.join(', ')}]`); + } + + if (!isValidPkgType(type)) { + throw err(`invalid package "type", options are [${PACKAGE_TYPES.join(', ')}]`); + } + + if (typeof id !== 'string' || !id.startsWith('@kbn/')) { + throw err(`invalid package "id", must be a string that starts with @kbn/`); + } + + if (typeof owner !== 'string' || !owner.startsWith('@')) { + throw err(`invalid package "owner", must be a valid Github team handle starting with @`); + } + + if (!isArrOfStrings(typeDeps)) { + throw err(`invalid "typeDeps", must be an array of strings`); + } + + if (!isArrOfStrings(runtimeDeps)) { + throw err(`invalid "runtimeDeps", must be an array of strings`); + } + + const base = { + id, + owner, + typeDeps, + runtimeDeps, + }; + + // return if this is one of the more basic types of package types + if (type === 'shared-server' || type === 'functional-tests' || type === 'test-helper') { + return { + type, + ...base, + }; + } + + // handle the plugin field for plugin-* types + if (type === 'plugin-browser' || type === 'plugin-server') { + return { + type, + ...base, + plugin: validateKibanaManifestPlugin(plugin), + }; + } + + // parse the sharedBrowserBundle for shared-browser and shared-common types + if (sharedBrowserBundle !== undefined && typeof sharedBrowserBundle !== 'boolean') { + throw err(`invalid "sharedBrowserBundle" field, expected undefined or a boolean`); + } + return { + type, + ...base, + sharedBrowserBundle, + }; +} + +/** + * Parse a kibana.jsonc file from the filesystem + */ +export function readKibanaManifest(path: string) { + let content; + try { + content = Fs.readFileSync(path, 'utf8'); + } catch (error) { + if (error.code === 'ENOENT') { + throw err(`Missing kibana.json file at ${path}`); + } + + throw error; + } + + return parseKibanaManifest(content); +} + +/** + * Parse a kibana.jsonc file from a string + */ +export function parseKibanaManifest(content: string) { + let parsed; + try { + parsed = parse(content); + } catch (error) { + throw err(`Invalid JSONc: ${error.message}`); + } + + return validateKibanaManifest(parsed); +} diff --git a/packages/kbn-kibana-manifest-parser/src/util.ts b/packages/kbn-kibana-manifest-parser/src/util.ts new file mode 100644 index 0000000000000..07b6a6c40a91e --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/src/util.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { KibanaPackageType } from './kibana_manifest'; + +export const ID_PATTERN = /^[a-z][a-zA-Z_]*$/; + +export function isObj(v: unknown): v is Record { + return typeof v === 'object' && v !== null; +} + +export const isArrOfStrings = (v: unknown): v is string[] => + Array.isArray(v) && v.every((i) => typeof i === 'string'); + +export const isValidId = (id: string) => ID_PATTERN.test(id); + +export const isArrOfIds = (v: unknown): v is string[] => isArrOfStrings(v) && v.every(isValidId); + +/** + * This weird map allows us to ensure that every value in the + * `KibanaPackageType` union is represented because the mapped + * type requires that the `PACKAGE_TYPE_MAP` map has a property + * matching every value in the union. + */ +const PACKAGE_TYPE_MAP: { [k in KibanaPackageType]: true } = { + 'functional-tests': true, + 'plugin-browser': true, + 'plugin-server': true, + 'shared-browser': true, + 'shared-common': true, + 'shared-server': true, + 'test-helper': true, + 'shared-scss': true, +}; + +export const PACKAGE_TYPES = Object.keys(PACKAGE_TYPE_MAP) as KibanaPackageType[]; + +export const isValidPkgType = (type: unknown): type is keyof typeof PACKAGE_TYPE_MAP => + typeof type === 'string' && Object.hasOwn(PACKAGE_TYPE_MAP, type); diff --git a/packages/kbn-kibana-manifest-parser/tsconfig.json b/packages/kbn-kibana-manifest-parser/tsconfig.json new file mode 100644 index 0000000000000..9f78bc243ac66 --- /dev/null +++ b/packages/kbn-kibana-manifest-parser/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "allowJs": true, + "checkJs": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-kibana-json-schema/BUILD.bazel b/packages/kbn-kibana-manifest-schema/BUILD.bazel similarity index 94% rename from packages/kbn-kibana-json-schema/BUILD.bazel rename to packages/kbn-kibana-manifest-schema/BUILD.bazel index df7e96d012266..db36bbbb6c26f 100644 --- a/packages/kbn-kibana-json-schema/BUILD.bazel +++ b/packages/kbn-kibana-manifest-schema/BUILD.bazel @@ -2,8 +2,8 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config") load("@build_bazel_rules_nodejs//:index.bzl", "js_library") load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") -PKG_DIRNAME = "kbn-kibana-json-schema" -PKG_REQUIRE_NAME = "@kbn/kibana-json-schema" +PKG_DIRNAME = "kbn-kibana-manifest-schema" +PKG_REQUIRE_NAME = "@kbn/kibana-manifest-schema" SOURCE_FILES = glob( [ @@ -52,7 +52,8 @@ TYPES_DEPS = [ "@npm//@types/dedent", "@npm//@types/node", "@npm//@types/jest", - "@npm//json-schema-typed" + "@npm//@types/json-schema", + "@npm//json-schema-typed", ] jsts_transpiler( diff --git a/packages/kbn-kibana-manifest-schema/README.mdx b/packages/kbn-kibana-manifest-schema/README.mdx new file mode 100644 index 0000000000000..96def9e65ee73 --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/README.mdx @@ -0,0 +1,10 @@ +--- +id: kibDevDocsOpsKibanaManifestSchema +slug: /kibana-dev-docs/ops/kibana-manifest-schema +title: "@kbn/jsonc" +description: The JSON schema for Kibana manifest files +date: 2022-05-24 +tags: ['kibana', 'dev', 'contributor', 'operations', 'json', 'schema', 'manifest'] +--- + +This package exposes JSON-Schema definitions for the `kibana.json` and `kibana.jsonc` manifest files. The JSON-Schemas are not used for validation, but are instead installed in the `.vscode` directory to provide autocomplete for these files in VSCode. \ No newline at end of file diff --git a/packages/kbn-kibana-manifest-schema/jest.config.js b/packages/kbn-kibana-manifest-schema/jest.config.js new file mode 100644 index 0000000000000..90cda1a572f6a --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-kibana-manifest-schema'], +}; diff --git a/packages/kbn-kibana-manifest-schema/package.json b/packages/kbn-kibana-manifest-schema/package.json new file mode 100644 index 0000000000000..3bcb493067c9b --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/kibana-manifest-schema", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-kibana-manifest-schema/src/desc.ts b/packages/kbn-kibana-manifest-schema/src/desc.ts new file mode 100644 index 0000000000000..de286397d73d3 --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/src/desc.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 dedent from 'dedent'; + +export const desc = (str: TemplateStringsArray, vars?: any[]) => { + const sourceLines = dedent(str, vars) + .split('\n') + .map((l) => l.trim()); + const lines: string[] = []; + for (const line of sourceLines) { + if (line === '') { + lines.push('', ''); + continue; + } + + const existing = lines.length ? lines.pop() : ''; + lines.push(existing ? `${existing} ${line}` : line); + } + return lines.join('\n'); +}; diff --git a/packages/kbn-kibana-manifest-schema/src/index.ts b/packages/kbn-kibana-manifest-schema/src/index.ts new file mode 100644 index 0000000000000..5545942012e7e --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { MANIFEST_V1 } from './kibana_json_v1_schema'; +export { MANIFEST_V2 } from './kibana_json_v2_schema'; diff --git a/packages/kbn-kibana-json-schema/src/kibana_json_schema.ts b/packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts similarity index 90% rename from packages/kbn-kibana-json-schema/src/kibana_json_schema.ts rename to packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts index 40686a9230cc6..2b6372649d9a2 100644 --- a/packages/kbn-kibana-json-schema/src/kibana_json_schema.ts +++ b/packages/kbn-kibana-manifest-schema/src/kibana_json_v1_schema.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import type { JSONSchema } from 'json-schema-typed'; -import dedent from 'dedent'; +import { JSONSchema } from 'json-schema-typed'; +import { desc } from './desc'; -export const KibanaJsonSchema: JSONSchema = { +export const MANIFEST_V1: JSONSchema = { type: 'object', required: ['id', 'version', 'owner'], properties: { id: { - description: dedent` + description: desc` Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. @@ -28,7 +28,7 @@ export const KibanaJsonSchema: JSONSchema = { pattern: '^(kibana|v?\\d+(\\.\\d+){0,2})$', }, kibanaVersion: { - description: dedent` + description: desc` The version of Kibana the plugin is compatible with, defaults to the value of the version field. `, type: 'string', @@ -50,7 +50,7 @@ export const KibanaJsonSchema: JSONSchema = { ], }, requiredPlugins: { - description: dedent` + description: desc` An optional list of the other plugins that MUST BE installed and enabled for this plugin to function properly. `, @@ -58,7 +58,7 @@ export const KibanaJsonSchema: JSONSchema = { items: { type: 'string' }, }, optionalPlugins: { - description: dedent` + description: desc` An optional list of the other plugins that if installed and enabled **may be** leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. @@ -67,7 +67,7 @@ export const KibanaJsonSchema: JSONSchema = { items: { type: 'string' }, }, requiredBundles: { - description: dedent` + description: desc` An optional list of the other plugins that if installed and enabled MAY BE leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. @@ -81,20 +81,20 @@ export const KibanaJsonSchema: JSONSchema = { items: { type: 'string' }, }, ui: { - description: dedent` + description: desc` Specifies whether plugin includes some client/browser specific functionality that should be included into client bundle via \`public/ui_plugin.js\` file. `, type: 'boolean', }, server: { - description: dedent` + description: desc` Specifies whether plugin includes some server-side specific functionality. `, type: 'boolean', }, extraPublicDirs: { - description: dedent` + description: desc` Specifies directory names that can be imported by other ui-plugins built using the same instance of the @kbn/optimizer. A temporary measure we plan to replace with better mechanisms for sharing static code between plugins @@ -104,7 +104,7 @@ export const KibanaJsonSchema: JSONSchema = { items: { type: 'string' }, }, serviceFolders: { - description: dedent` + description: desc` Only used for the automatically generated API documentation. Specifying service folders will cause your plugin API reference to be broken up into sub sections. `, @@ -120,7 +120,7 @@ export const KibanaJsonSchema: JSONSchema = { type: 'string', }, githubTeam: { - description: dedent` + description: desc` All internal plugins should have a github team specified. GitHub teams can be viewed here: https://github.com/orgs/elastic/teams `, @@ -129,13 +129,13 @@ export const KibanaJsonSchema: JSONSchema = { }, }, description: { - description: dedent` + description: desc` A brief description of what this plugin does and any capabilities it provides. `, type: 'string', }, enabledOnAnonymousPages: { - description: dedent` + description: desc` Specifies whether this plugin - and its required dependencies - will be enabled for anonymous pages (login page, status page when configured, etc.) Default is false. `, diff --git a/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts new file mode 100644 index 0000000000000..ca02e48f00e2d --- /dev/null +++ b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.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 type { JSONSchema } from 'json-schema-typed'; +import { desc } from './desc'; + +export const PLUGIN_ID_PATTERN = /^[a-z][a-zA-Z_]*$/; + +export const MANIFEST_V2: JSONSchema = { + type: 'object', + required: ['id', 'type', 'owner', 'typeDependencies', 'runtimeDependencies'], + properties: { + id: { + type: 'string', + pattern: '^@kbn/', + }, + owner: { + type: 'string', + description: desc` + Github handle for the person or team who is responsible for this package. + This owner will be used in the codeowners files for this package. + + For additional codeowners, you add additional entries at the end of the + codeowners file. + `, + pattern: '^@', + }, + typeDependencies: { + type: 'array', + description: desc` + Packages which are required for the source code in the package to be + type-checked. This list is updated automatically by the package linter. + `, + items: { + type: 'string', + }, + }, + runtimeDependencies: { + type: 'array', + description: desc` + Packages which are required for the source code in the package to run. This list + is updated automatically by the package linter. + `, + items: { + type: 'string', + }, + }, + }, + oneOf: [ + { + type: 'object', + properties: { + type: { + enum: ['plugin-browser', 'plugin-server'], + }, + plugin: { + type: 'object', + required: ['id'], + properties: { + id: { + type: 'string', + pattern: PLUGIN_ID_PATTERN.source, + }, + configPath: { + description: + 'Root configuration path used by the plugin, defaults to "id" in snake_case format.', + type: 'array', + items: { + type: 'string', + pattern: PLUGIN_ID_PATTERN.source, + }, + }, + requiredPlugins: { + type: 'array', + items: { + type: 'string', + pattern: PLUGIN_ID_PATTERN.source, + }, + }, + optionalPlugins: { + type: 'array', + items: { + type: 'string', + pattern: PLUGIN_ID_PATTERN.source, + }, + }, + description: { + description: desc` + A brief description of what this plugin does and any capabilities it provides. + `, + type: 'string', + }, + enabledOnAnonymousPages: { + description: desc` + Specifies whether this plugin - and its required dependencies - will be enabled for anonymous pages (login page, status page when + configured, etc.) Default is false. + `, + type: 'boolean', + }, + serviceFolders: { + description: desc` + Only used for the automatically generated API documentation. Specifying service + folders will cause your plugin API reference to be broken up into sub sections. + `, + type: 'array', + items: { type: 'string' }, + }, + }, + }, + }, + }, + { + type: 'object', + properties: { + type: { + const: 'shared-browser', + }, + sharedBrowserBundle: { + type: 'boolean', + description: desc` + Set this flag to true for this package to produce it's own bundle that will be loaded + asynchronously when needed. Defaults to false. + `, + }, + }, + }, + { + type: 'object', + properties: { + type: { + enum: [ + 'shared-server', + 'shared-common', + 'functional-tests', + 'test-helper', + 'shared-scss', + ], + }, + }, + }, + ], +}; diff --git a/packages/kbn-kibana-json-schema/tsconfig.json b/packages/kbn-kibana-manifest-schema/tsconfig.json similarity index 100% rename from packages/kbn-kibana-json-schema/tsconfig.json rename to packages/kbn-kibana-manifest-schema/tsconfig.json diff --git a/packages/kbn-scalability-simulation-generator/BUILD.bazel b/packages/kbn-managed-vscode-config-cli/BUILD.bazel similarity index 88% rename from packages/kbn-scalability-simulation-generator/BUILD.bazel rename to packages/kbn-managed-vscode-config-cli/BUILD.bazel index 57464d684874c..f635c88a74fa2 100644 --- a/packages/kbn-scalability-simulation-generator/BUILD.bazel +++ b/packages/kbn-managed-vscode-config-cli/BUILD.bazel @@ -2,8 +2,8 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config") load("@build_bazel_rules_nodejs//:index.bzl", "js_library") load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") -PKG_DIRNAME = "kbn-scalability-simulation-generator" -PKG_REQUIRE_NAME = "@kbn/scalability-simulation-generator" +PKG_DIRNAME = "kbn-managed-vscode-config-cli" +PKG_REQUIRE_NAME = "@kbn/managed-vscode-config-cli" SOURCE_FILES = glob( [ @@ -11,6 +11,7 @@ SOURCE_FILES = glob( ], exclude = [ "**/*.test.*", + "**/*.stories.*", ], ) @@ -36,9 +37,6 @@ NPM_MODULE_EXTRA_FILES = [ # "@npm//name-of-package" # eg. "@npm//lodash" RUNTIME_DEPS = [ - "//packages/kbn-dev-cli-errors", - "//packages/kbn-dev-cli-runner", - "//packages/kbn-tooling-log", ] # In this array place dependencies necessary to build the types, which will include the @@ -51,11 +49,12 @@ RUNTIME_DEPS = [ # # References to NPM packages work the same as RUNTIME_DEPS TYPES_DEPS = [ - "//packages/kbn-dev-cli-errors:npm_module_types", - "//packages/kbn-dev-cli-runner:npm_module_types", - "//packages/kbn-tooling-log:npm_module_types", "@npm//@types/node", "@npm//@types/jest", + "@npm//dedent", + "//packages/kbn-utils:npm_module_types", + "//packages/kbn-dev-cli-runner:npm_module_types", + "//packages/kbn-managed-vscode-config:npm_module_types", ] jsts_transpiler( diff --git a/packages/kbn-managed-vscode-config-cli/README.mdx b/packages/kbn-managed-vscode-config-cli/README.mdx new file mode 100644 index 0000000000000..5cb8a068ebe0d --- /dev/null +++ b/packages/kbn-managed-vscode-config-cli/README.mdx @@ -0,0 +1,12 @@ +--- +id: kibDevDocsOpsManagedVscodeConfigCli +slug: /kibana-dev-docs/ops/managed-vscode-config-cli +title: "@kbn/managed-vscode-config-cli" +description: CLI for updating the .vscode directory +date: 2022-05-24 +tags: ['kibana', 'dev', 'contributor', 'operations', 'vscode'] +--- + +Usage: `node scripts/update_vscode_config` + +Updates the `.vscode` directory to have the current value of the managed configs, and updates the managed config files from \ No newline at end of file diff --git a/packages/kbn-managed-vscode-config-cli/jest.config.js b/packages/kbn-managed-vscode-config-cli/jest.config.js new file mode 100644 index 0000000000000..fe1aa62847e40 --- /dev/null +++ b/packages/kbn-managed-vscode-config-cli/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-managed-vscode-config-cli'], +}; diff --git a/packages/kbn-scalability-simulation-generator/package.json b/packages/kbn-managed-vscode-config-cli/package.json similarity index 52% rename from packages/kbn-scalability-simulation-generator/package.json rename to packages/kbn-managed-vscode-config-cli/package.json index e5865fd1c72f4..c1da1e53dbc04 100644 --- a/packages/kbn-scalability-simulation-generator/package.json +++ b/packages/kbn-managed-vscode-config-cli/package.json @@ -1,6 +1,5 @@ { - "name": "@kbn/scalability-simulation-generator", - "description": "A library to generate scalability benchmarking simulation files from APM traces.", + "name": "@kbn/managed-vscode-config-cli", "private": true, "version": "1.0.0", "main": "./target_node/index.js", diff --git a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts b/packages/kbn-managed-vscode-config-cli/src/index.ts similarity index 53% rename from packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts rename to packages/kbn-managed-vscode-config-cli/src/index.ts index 8a2c85ad6ff27..deb829da76407 100644 --- a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts +++ b/packages/kbn-managed-vscode-config-cli/src/index.ts @@ -14,43 +14,44 @@ import dedent from 'dedent'; import { run } from '@kbn/dev-cli-runner'; -import { MANAGED_CONFIG_KEYS, MANAGED_CONFIG_FILES } from './managed_config_keys'; -import { updateVscodeConfig } from './update_vscode_config'; +import { + MANAGED_CONFIG_KEYS, + MANAGED_CONFIG_FILES, + updateVscodeConfig, +} from '@kbn/managed-vscode-config'; const CONFIG_DIR = Path.resolve(REPO_ROOT, '.vscode'); -export function runUpdateVscodeConfigCli() { - run(async ({ log }) => { - const path = Path.resolve(CONFIG_DIR, 'settings.json'); - - let json; - try { - json = await Fsp.readFile(path, 'utf-8'); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } +run(async ({ log }) => { + const path = Path.resolve(CONFIG_DIR, 'settings.json'); + + let json; + try { + json = await Fsp.readFile(path, 'utf-8'); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; } + } - const updatedJson = updateVscodeConfig( - MANAGED_CONFIG_KEYS, - dedent` + const updatedJson = updateVscodeConfig( + MANAGED_CONFIG_KEYS, + dedent` Some settings in this file are managed by @kbn/dev-utils. When a setting is managed it is preceeded with a comment "// @managed" comment. Replace that with "// self managed" and the scripts will not touch that value. Put a "// self managed" comment at the top of the file, or above a group of settings to disable management of that entire section. `, - json - ); - await Fsp.mkdir(Path.dirname(path), { recursive: true }); + json + ); + await Fsp.mkdir(Path.dirname(path), { recursive: true }); - // write managed config files - for (const { name, content } of MANAGED_CONFIG_FILES) { - await Fsp.writeFile(Path.resolve(CONFIG_DIR, name), content); - } + // write managed config files + for (const { name, content } of MANAGED_CONFIG_FILES) { + await Fsp.writeFile(Path.resolve(CONFIG_DIR, name), content); + } - await Fsp.writeFile(path, updatedJson); + await Fsp.writeFile(path, updatedJson); - log.success('updated', path); - }); -} + log.success('updated', path); +}); diff --git a/packages/kbn-scalability-simulation-generator/tsconfig.json b/packages/kbn-managed-vscode-config-cli/tsconfig.json similarity index 100% rename from packages/kbn-scalability-simulation-generator/tsconfig.json rename to packages/kbn-managed-vscode-config-cli/tsconfig.json diff --git a/packages/kbn-managed-vscode-config/BUILD.bazel b/packages/kbn-managed-vscode-config/BUILD.bazel new file mode 100644 index 0000000000000..255e331c189d0 --- /dev/null +++ b/packages/kbn-managed-vscode-config/BUILD.bazel @@ -0,0 +1,122 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-managed-vscode-config" +PKG_REQUIRE_NAME = "@kbn/managed-vscode-config" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@babel/parser", + "@npm//@babel/types", + "@npm//@types/babel__generator", + "@npm//@types/prettier", + "@npm//tslib", + "//packages/kbn-kibana-manifest-schema: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", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-managed-vscode-config/README.mdx b/packages/kbn-managed-vscode-config/README.mdx new file mode 100644 index 0000000000000..2e0cb5433173d --- /dev/null +++ b/packages/kbn-managed-vscode-config/README.mdx @@ -0,0 +1,14 @@ +--- +id: kibDevDocsOpsManagedVscodeConfig +slug: /kibana-dev-docs/ops/managed-vscode-config +title: "@kbn/managed-vscode-config" +description: Config options for vscode that are automatically setup for contributors +date: 2022-05-24 +tags: ['kibana', 'dev', 'contributor', 'operations', 'vscode'] +--- + +This package contains VSCode settings which are automatically setup for contributors on bootstrap. This is done via the using `node scripts/update_vscode_config`. + +In order to support contributors maintaining workspace-specific configuration of their own, this tool prefixes all managed config settings with `// @managed` comments. If you want to override any config that is `// @managed` then just update that comment to `// self managed` and the update script won't touch that key/value. + +`// self managed` comments are only necessary for keys which are currently or might become managed in the future. Config keys which are not managed are always preserved when updating the `.vscode` directory. \ No newline at end of file diff --git a/packages/kbn-managed-vscode-config/jest.config.js b/packages/kbn-managed-vscode-config/jest.config.js new file mode 100644 index 0000000000000..77993fd762eeb --- /dev/null +++ b/packages/kbn-managed-vscode-config/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-managed-vscode-config'], +}; diff --git a/packages/kbn-managed-vscode-config/package.json b/packages/kbn-managed-vscode-config/package.json new file mode 100644 index 0000000000000..84de677b6f378 --- /dev/null +++ b/packages/kbn-managed-vscode-config/package.json @@ -0,0 +1,10 @@ +{ + "name": "@kbn/managed-vscode-config", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0", + "kibana": { + "devOnly": true + } +} diff --git a/packages/kbn-managed-vscode-config/src/index.ts b/packages/kbn-managed-vscode-config/src/index.ts new file mode 100644 index 0000000000000..1b104fc95fbd4 --- /dev/null +++ b/packages/kbn-managed-vscode-config/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { MANAGED_CONFIG_FILES, MANAGED_CONFIG_KEYS } from './managed_config_keys'; +export { updateVscodeConfig } from './update_vscode_config'; diff --git a/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts b/packages/kbn-managed-vscode-config/src/managed_config_keys.ts similarity index 83% rename from packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts rename to packages/kbn-managed-vscode-config/src/managed_config_keys.ts index 55a44564d29ea..29fae6e7ed38a 100644 --- a/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts +++ b/packages/kbn-managed-vscode-config/src/managed_config_keys.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { KibanaJsonSchema } from '@kbn/kibana-json-schema'; +import { MANIFEST_V1, MANIFEST_V2 } from '@kbn/kibana-manifest-schema'; export interface ManagedConfigKey { key: string; @@ -67,10 +67,21 @@ export const MANAGED_CONFIG_KEYS: ManagedConfigKey[] = [ fileMatch: ['kibana.json'], url: './.vscode/kibana-json-schema.json', }, + { + fileMatch: ['kibana.jsonc'], + url: './.vscode/kibana-manifest-schema-v2.json', + }, ], }, ]; export const MANAGED_CONFIG_FILES = [ - { name: 'kibana-json-schema.json', content: JSON.stringify(KibanaJsonSchema, null, 2) }, + { + name: 'kibana-json-schema.json', + content: JSON.stringify(MANIFEST_V1, null, 2), + }, + { + name: 'kibana-manifest-schema-v2.json', + content: JSON.stringify(MANIFEST_V2, null, 2), + }, ]; diff --git a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.test.ts b/packages/kbn-managed-vscode-config/src/update_vscode_config.test.ts similarity index 100% rename from packages/kbn-dev-utils/src/vscode_config/update_vscode_config.test.ts rename to packages/kbn-managed-vscode-config/src/update_vscode_config.test.ts diff --git a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts b/packages/kbn-managed-vscode-config/src/update_vscode_config.ts similarity index 100% rename from packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts rename to packages/kbn-managed-vscode-config/src/update_vscode_config.ts diff --git a/packages/kbn-managed-vscode-config/tsconfig.json b/packages/kbn-managed-vscode-config/tsconfig.json new file mode 100644 index 0000000000000..789c6b3111115 --- /dev/null +++ b/packages/kbn-managed-vscode-config/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-performance-testing-dataset-extractor/src/cli.ts b/packages/kbn-performance-testing-dataset-extractor/src/cli.ts index 4e24cde4799d5..914a7d587efdf 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/cli.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/cli.ts @@ -58,8 +58,9 @@ export async function runExtractor() { const scalabilitySetup: ScalabilitySetup = config.get('scalabilitySetup'); if (!scalabilitySetup) { - log.error(`'scalabilitySetup' must be defined in config file!`); - return; + log.warning( + `'scalabilitySetup' is not defined in config file, output file for Kibana scalability run won't be generated` + ); } const env = config.get(`kbnTestServer.env`); diff --git a/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts b/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts index 0bd8accd5fd59..948d410c8669e 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts @@ -17,11 +17,6 @@ interface ClientOptions { password: string; } -interface Labels { - journeyName: string; - maxUsersCount: string; -} - export interface Headers { readonly [key: string]: string[]; } @@ -44,21 +39,33 @@ interface Transaction { } export interface Document { - labels: Labels; - character: string; - quote: string; - service: { version: string }; + '@timestamp': string; + labels?: { journeyName: string; maxUsersCount: string }; parent?: { id: string }; - processor: string; + service: { name: string; environment: string }; trace: { id: string }; - '@timestamp': string; - environment: string; + transaction: Transaction; +} + +export interface SpanDocument extends Omit { + transaction: { id: string }; + span: { + id: string; + name: string; + action: string; + duration: { us: number }; + db?: { statement?: string }; + }; +} + +export interface TransactionDocument extends Omit { + service: { name: string; environment: string; version: string }; + processor: string; url: { path: string }; http: { request: Request; response: Response; }; - transaction: Transaction; } const addBooleanFilter = (filter: { field: string; value: string }): QueryDslQueryContainer => { @@ -88,81 +95,86 @@ const addRangeFilter = (range: { startTime: string; endTime: string }): QueryDsl }; }; -export function initClient(options: ClientOptions, log: ToolingLog) { - const client = new Client({ - node: options.node, - auth: { - username: options.username, - password: options.password, - }, - }); +export class ESClient { + client: Client; + log: ToolingLog; - return { - async getKibanaServerTransactions( - buildId: string, - journeyName: string, - range?: { startTime: string; endTime: string } - ) { - const filters = [ - { field: 'transaction.type', value: 'request' }, - { field: 'processor.event', value: 'transaction' }, - { field: 'labels.testBuildId', value: buildId }, - { field: 'labels.journeyName', value: journeyName }, - ]; - const queryFilters = filters.map((filter) => addBooleanFilter(filter)); - if (range) { - queryFilters.push(addRangeFilter(range)); - } - return await this.getTransactions(queryFilters); - }, - async getFtrTransactions(buildId: string, journeyName: string) { - const filters = [ - { field: 'service.name', value: 'functional test runner' }, - { field: 'processor.event', value: 'transaction' }, - { field: 'labels.testBuildId', value: buildId }, - { field: 'labels.journeyName', value: journeyName }, - { field: 'labels.performancePhase', value: 'TEST' }, - ]; - const queryFilters = filters.map((filter) => addBooleanFilter(filter)); - return await this.getTransactions(queryFilters); - }, + constructor(options: ClientOptions, log: ToolingLog) { + this.client = new Client({ + node: options.node, + auth: { + username: options.username, + password: options.password, + }, + }); + this.log = log; + } - async getTransactions(queryFilters: QueryDslQueryContainer[]) { - const searchRequest: SearchRequest = { - body: { - track_total_hits: true, - sort: [ - { - '@timestamp': { - order: 'asc', - unmapped_type: 'boolean', - }, + async getTransactions(queryFilters: QueryDslQueryContainer[]) { + const searchRequest: SearchRequest = { + body: { + sort: [ + { + '@timestamp': { + order: 'asc', + unmapped_type: 'boolean', }, - ], - size: 10000, - stored_fields: ['*'], - _source: true, - query: { - bool: { - must: [], - filter: [ - { - bool: { - filter: queryFilters, - }, + }, + ], + size: 10000, + query: { + bool: { + filter: [ + { + bool: { + filter: queryFilters, }, - ], - should: [], - must_not: [], - }, + }, + ], }, }, - }; + }, + }; - log.debug(`Search request: ${JSON.stringify(searchRequest)}`); - const result = await client.search(searchRequest); - log.debug(`Search result: ${JSON.stringify(result)}`); - return result?.hits?.hits; - }, - }; + this.log.debug(`Search request: ${JSON.stringify(searchRequest)}`); + const result = await this.client.search(searchRequest); + this.log.debug(`Search result: ${JSON.stringify(result)}`); + return result?.hits?.hits; + } + + async getFtrServiceTransactions(buildId: string, journeyName: string) { + const filters = [ + { field: 'service.name', value: 'functional test runner' }, + { field: 'processor.event', value: 'transaction' }, + { field: 'labels.testBuildId', value: buildId }, + { field: 'labels.journeyName', value: journeyName }, + { field: 'labels.performancePhase', value: 'TEST' }, + ]; + const queryFilters = filters.map((filter) => addBooleanFilter(filter)); + return await this.getTransactions(queryFilters); + } + + async getKibanaServerTransactions( + buildId: string, + journeyName: string, + range?: { startTime: string; endTime: string } + ) { + const filters = [ + { field: 'transaction.type', value: 'request' }, + { field: 'processor.event', value: 'transaction' }, + { field: 'labels.testBuildId', value: buildId }, + { field: 'labels.journeyName', value: journeyName }, + ]; + const queryFilters = filters.map((filter) => addBooleanFilter(filter)); + if (range) { + queryFilters.push(addRangeFilter(range)); + } + return await this.getTransactions(queryFilters); + } + + async getSpans(transactionId: string) { + const filters = [{ field: 'parent.id', value: transactionId }]; + const queryFilters = filters.map((filter) => addBooleanFilter(filter)); + return await this.getTransactions(queryFilters); + } } diff --git a/packages/kbn-performance-testing-dataset-extractor/src/es_request.ts b/packages/kbn-performance-testing-dataset-extractor/src/es_request.ts new file mode 100644 index 0000000000000..5160d1cf1b0cc --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/es_request.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 { ESClient, SpanDocument } from './es_client'; +import { KibanaRequest } from './server_request'; + +const httpMethodRegExp = /(GET|POST|DELETE|HEAD|PUT|OPTIONS)/; +const httpPathRegExp = /(?<=GET|POST|DELETE|HEAD|PUT|OPTIONS).*/; + +interface Request { + id: string; + transactionId: string; + name: string; + action: string; + request: { + method?: string; + path?: string; + params?: string; + body?: JSON; + }; + date: string; + duration: number; +} + +interface Stream { + startTime: number; + endTime: number; + requests: Request[]; +} + +const strToJSON = (str: string): JSON | undefined => { + try { + return JSON.parse(str); + } catch (e) { + return; + } +}; + +const findFirstMatch = (regExp: RegExp, testString: string) => { + const found = regExp.exec(testString); + return found ? found[0] : undefined; +}; + +const parseQueryStatement = (statement: string): { params?: string; body?: JSON } => { + // github.com/elastic/apm-agent-nodejs/blob/5ba1b2609d18b12a64e1e559236717dd38d64a51/lib/instrumentation/elasticsearch-shared.js#L27-L29 + // Some ES endpoints support both query params and a body, statement string might contain both of it + const split = statement.split('\n\n'); + if (split.length === 2) { + return { params: split[0], body: strToJSON(split[1]) }; + } else { + const body = strToJSON(split[0]); + return body ? { body } : { params: split[0] }; + } +}; + +export const fetchRequests = async (esClient: ESClient, requests: KibanaRequest[]) => { + const esRequests = new Array(); + for (const request of requests) { + const transactionId = request.transaction.id; + const hits = await esClient.getSpans(transactionId); + const spans = hits + .map((hit) => hit!._source as SpanDocument) + .map((hit) => { + const query = hit?.span.db?.statement ? parseQueryStatement(hit?.span.db?.statement) : {}; + return { + id: hit.span.id, + transactionId: hit.transaction.id, + name: hit.span.name, + action: hit.span?.action, + request: { + method: findFirstMatch(httpMethodRegExp, hit.span.name), + path: findFirstMatch(httpPathRegExp, hit.span.name.replace(/\s+/g, '')), + params: query?.params, + body: query?.body, + }, + date: hit['@timestamp'], + duration: hit.span?.duration?.us, + }; + }) + // filter out requests without method, path and POST/PUT/DELETE without body + .filter( + (hit) => + hit && + hit.request?.method && + hit.request?.path && + (hit.request?.method === 'GET' || hit.request?.body) + ); + esRequests.push(...spans); + } + + return esRequests; +}; + +export const requestsToStreams = (requests: Request[]) => { + const sorted = requests.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + const streams = new Map(); + + for (const request of sorted) { + const startTime = new Date(request.date).getTime(); + const endTime = new Date(request.date).getTime() + request.duration / 1000; + // searching if query starts before any existing stream ended + const match = Array.from(streams.keys()).filter((key) => { + const streamEndTime = streams.get(key)?.endTime; + return streamEndTime ? startTime < streamEndTime : false; + }); + const stream = streams.get(match[0]); + if (stream) { + // adding query to the existing stream + stream.requests.push(request); + // updating the stream endTime if needed + if (endTime > stream.endTime) { + stream.endTime = endTime; + } + // saving updated stream + streams.set(match[0], stream); + } else { + // add a new stream + streams.set(request.date, { + startTime, + endTime, + requests: [request], + }); + } + } + + const values = Array.from(streams.values()); + return values.map((stream) => { + return { + startTime: new Date(stream.startTime).toISOString(), + endTime: new Date(stream.endTime).toISOString(), + requests: stream.requests, + }; + }); +}; diff --git a/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts b/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts index 957b6ad4669d7..e0587af1637d8 100644 --- a/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts +++ b/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts @@ -12,10 +12,11 @@ import { existsSync } from 'fs'; import path from 'path'; import { ToolingLog } from '@kbn/tooling-log'; import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; -import { initClient, Document, Headers } from './es_client'; +import { ESClient, Document, TransactionDocument } from './es_client'; +import { getRequests } from './server_request'; +import { fetchRequests, requestsToStreams } from './es_request'; const DATE_FORMAT = `YYYY-MM-DD'T'HH:mm:ss.SSS'Z'`; -const STATIC_RESOURCES_PATTERN = /\.(css|ico|js|json|jpeg|jpg|gif|png|otf|ttf|woff|woff2)$/; interface CLIParams { param: { @@ -45,23 +46,6 @@ export interface ScalabilitySetup { maxDuration: string; } -const parsePayload = (payload: string, traceId: string, log: ToolingLog): string | undefined => { - let body; - try { - body = JSON.parse(payload); - } catch (error) { - log.error(`Failed to parse payload - trace_id: '${traceId}'`); - } - return body; -}; - -const combineHeaderFieldValues = (headers: Headers) => { - return Object.assign( - {}, - ...Object.keys(headers).map((key) => ({ [key]: headers[key].join(', ') })) - ); -}; - const calculateTransactionTimeRage = (hit: SearchHit) => { const trSource = hit._source as Document; const startTime = trSource['@timestamp']; @@ -70,39 +54,14 @@ const calculateTransactionTimeRage = (hit: SearchHit) => { return { startTime, endTime }; }; -const getTraceItems = ( - hits: Array>, - withoutStaticResources: boolean, - log: ToolingLog -) => { - const data = hits - .map((hit) => hit!._source as Document) - .map((hit) => { - const payload = hit.http.request?.body?.original; - return { - traceId: hit.trace.id, - parentId: hit?.parent?.id, - processor: hit.processor, - environment: hit.environment, - request: { - timestamp: hit['@timestamp'], - method: hit.http.request.method, - path: hit.url.path, - headers: combineHeaderFieldValues(hit.http.request.headers), - body: payload ? JSON.stringify(parsePayload(payload, hit.trace.id, log)) : undefined, - statusCode: hit.http.response.status_code, - }, - transaction: { - id: hit.transaction.id, - name: hit.transaction.name, - type: hit.transaction.type, - }, - }; - }); - - return withoutStaticResources - ? data.filter((item) => !STATIC_RESOURCES_PATTERN.test(item.request.path)) - : data; +const saveFile = async (output: any, outputDir: string, fileName: string, log: ToolingLog) => { + const filePath = path.resolve(outputDir, fileName); + + if (!existsSync(outputDir)) { + await fs.mkdir(outputDir, { recursive: true }); + } + await fs.writeFile(filePath, JSON.stringify(output, null, 2), 'utf8'); + log.info(`Output file saved: ${filePath}`); }; export const extractor = async ({ param, client, log }: CLIParams) => { @@ -115,11 +74,11 @@ export const extractor = async ({ param, client, log }: CLIParams) => { log.info( `Searching transactions with 'labels.testBuildId=${buildId}' and 'labels.journeyName=${journeyName}'` ); - const esClient = initClient(authOptions, log); - const ftrTransactionHits = await esClient.getFtrTransactions(buildId, journeyName); + const esClient = new ESClient(authOptions, log); + const ftrTransactionHits = await esClient.getFtrServiceTransactions(buildId, journeyName); if (!ftrTransactionHits || ftrTransactionHits.length === 0) { log.warning( - `No transactions found. Can't calculate journey time range, output file won't be generated.` + `No 'functional test runner' transactions found. Can't calculate journey time range, output file won't be generated.` ); return; } @@ -134,27 +93,45 @@ export const extractor = async ({ param, client, log }: CLIParams) => { // Filtering out setup/teardown related transactions by time range from 'functional test runner' transaction const hits = await esClient.getKibanaServerTransactions(buildId, journeyName, timeRange); if (!hits || hits.length === 0) { - log.warning(`No transactions found. Output file won't be generated.`); + log.warning(`No Kibana server transactions found. Output file won't be generated.`); return; } - const source = hits[0]!._source as Document; + const source = hits[0]!._source as TransactionDocument; const kibanaVersion = source.service.version; - const output = { - journeyName, - kibanaVersion, - scalabilitySetup, - requests: getTraceItems(hits, withoutStaticResources, log), - }; + const kibanaRequests = getRequests(hits, withoutStaticResources, log); + const esRequests = await fetchRequests(esClient, kibanaRequests); + log.info( + `Found ${kibanaRequests.length} Kibana server and ${esRequests.length} Elasticsearch requests` + ); + const streams = requestsToStreams(esRequests); const outputDir = path.resolve('target/scalability_traces'); - const fileName = `${output.journeyName.replace(/ /g, '')}-${buildId}.json`; - const filePath = path.resolve(outputDir, fileName); - - log.info(`Found ${output.requests.length} transactions, output file: ${filePath}`); - if (!existsSync(outputDir)) { - await fs.mkdir(outputDir, { recursive: true }); + const fileName = `${journeyName.replace(/ /g, '')}-${buildId}.json`; + + if (scalabilitySetup) { + await saveFile( + { + journeyName, + kibanaVersion, + scalabilitySetup, + requests: kibanaRequests, + }, + path.resolve(outputDir, 'server'), + fileName, + log + ); } - await fs.writeFile(filePath, JSON.stringify(output, null, 2), 'utf8'); + + await saveFile( + { + journeyName, + kibanaVersion, + streams: Array.from(streams.values()), + }, + path.resolve(outputDir, 'es'), + fileName, + log + ); }; diff --git a/packages/kbn-performance-testing-dataset-extractor/src/server_request.ts b/packages/kbn-performance-testing-dataset-extractor/src/server_request.ts new file mode 100644 index 0000000000000..dd553b83848c8 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/server_request.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import { ToolingLog } from '@kbn/tooling-log'; +import { TransactionDocument, Headers } from './es_client'; + +const staticResourcesRegExp = /\.(css|ico|js|json|jpeg|jpg|gif|png|otf|ttf|woff|woff2)$/; + +export interface KibanaRequest { + traceId: string; + parentId?: string; + processor: string; + environment: string; + request: { + timestamp: string; + method: string; + path: string; + headers: { [key: string]: string }; + body?: string; + statusCode: number; + }; + transaction: { + id: string; + name: string; + type: string; + }; +} + +const parsePayload = (payload: string, traceId: string, log: ToolingLog): string | undefined => { + let body; + try { + body = JSON.parse(payload); + } catch (error) { + log.error(`Failed to parse payload - trace_id: '${traceId}'`); + } + return body; +}; + +const combineHeaderFieldValues = (headers: Headers): { [key: string]: string } => { + return Object.assign( + {}, + ...Object.keys(headers).map((key) => ({ [key]: headers[key].join(', ') })) + ); +}; + +export const getRequests = ( + hits: Array>, + withoutStaticResources: boolean, + log: ToolingLog +): KibanaRequest[] => { + const data = hits + .map((hit) => hit!._source as TransactionDocument) + .map((hit) => { + const payload = hit.http.request?.body?.original; + return { + traceId: hit.trace.id, + parentId: hit?.parent?.id, + processor: hit.processor, + environment: hit.service.environment, + request: { + timestamp: hit['@timestamp'], + method: hit.http.request.method, + path: hit.url.path, + headers: combineHeaderFieldValues(hit.http.request.headers), + body: payload ? JSON.stringify(parsePayload(payload, hit.trace.id, log)) : undefined, + statusCode: hit.http.response.status_code, + }, + transaction: { + id: hit.transaction.id, + name: hit.transaction.name, + type: hit.transaction.type, + }, + }; + }); + + return withoutStaticResources + ? data.filter((item) => !staticResourcesRegExp.test(item.request.path)) + : data; +}; diff --git a/packages/kbn-plugin-discovery/src/find_kibana_json_files.js b/packages/kbn-plugin-discovery/src/find_files.js similarity index 75% rename from packages/kbn-plugin-discovery/src/find_kibana_json_files.js rename to packages/kbn-plugin-discovery/src/find_files.js index f1687859627a8..6f9e09cbfa3d1 100644 --- a/packages/kbn-plugin-discovery/src/find_kibana_json_files.js +++ b/packages/kbn-plugin-discovery/src/find_files.js @@ -27,19 +27,20 @@ function safeReadDir(path) { } /** - * Given an iterable of paths with optoinal "*" segments, expand the path to the - * list of actual absolute paths, removing all "*" segments, and then return the - * set of paths which end up pointing to actual files. + * Search for files named `name` in `dir`, up to `depth` levels deep. If a directory has a + * matching file its children are not iterated, otherwise if depth > 0 then all child + * directories are checked recursively with depth-1 * * @param {string} dir * @param {number} depth + * @param {string} name * @returns {string[]} */ -function findKibanaJsonFiles(dir, depth) { +function findFiles(dir, depth, name) { // if depth = 0 then we just need to determine if there is a kibana.json file in this directory // and return either that path or an empty array if (depth === 0) { - const path = Path.resolve(dir, 'kibana.json'); + const path = Path.resolve(dir, name); return Fs.existsSync(path) ? [path] : []; } @@ -51,7 +52,7 @@ function findKibanaJsonFiles(dir, depth) { const childDirs = []; for (const ent of files) { if (ent.isFile()) { - if (ent.name === 'kibana.json') { + if (ent.name === name) { return [Path.resolve(dir, ent.name)]; } } else if (ent.isDirectory()) { @@ -59,7 +60,7 @@ function findKibanaJsonFiles(dir, depth) { } } - return childDirs.flatMap((dir) => findKibanaJsonFiles(dir, depth - 1)); + return childDirs.flatMap((dir) => findFiles(dir, depth - 1, name)); } -module.exports = { findKibanaJsonFiles }; +module.exports = { findFiles }; diff --git a/packages/kbn-plugin-discovery/src/simple_kibana_platform_plugin_discovery.js b/packages/kbn-plugin-discovery/src/simple_kibana_platform_plugin_discovery.js index 8ea7bb6d563ab..8d5da4e519355 100644 --- a/packages/kbn-plugin-discovery/src/simple_kibana_platform_plugin_discovery.js +++ b/packages/kbn-plugin-discovery/src/simple_kibana_platform_plugin_discovery.js @@ -7,7 +7,7 @@ */ const { parseKibanaPlatformPlugin } = require('./parse_kibana_platform_plugin'); -const { findKibanaJsonFiles } = require('./find_kibana_json_files'); +const { findFiles } = require('./find_files'); /** * Helper to find the new platform plugins. @@ -19,9 +19,9 @@ function simpleKibanaPlatformPluginDiscovery(scanDirs, pluginPaths) { return Array.from( new Set([ // find kibana.json files up to 5 levels within each scan dir - ...scanDirs.flatMap((dir) => findKibanaJsonFiles(dir, 5)), + ...scanDirs.flatMap((dir) => findFiles(dir, 5, 'kibana.json')), // find kibana.json files at the root of each plugin path - ...pluginPaths.flatMap((path) => findKibanaJsonFiles(path, 0)), + ...pluginPaths.flatMap((path) => findFiles(path, 0, 'kibana.json')), ]) ).map(parseKibanaPlatformPlugin); } diff --git a/packages/kbn-scalability-simulation-generator/README.md b/packages/kbn-scalability-simulation-generator/README.md deleted file mode 100644 index 14bb68132b97c..0000000000000 --- a/packages/kbn-scalability-simulation-generator/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# @kbn/scalability-simulation-generator - -A library to generate scalability benchmarking simulation file, that can be run by Gatling performance testing tool. - -## Usage - -There are 2 ways to run auto-generated simulation files, using: - - Gatling bundle - - kibana-load-testing project - -If you plan to use Gatling-bundle, generate simulation using this command: - -``` - node scripts/generate_scalability_simulations.js \ - --dir "" \ - --baseUrl "" -``` - -If you plan to use [kibana-load-testing](https://github.com/elastic/kibana-load-testing), use the following command: - -``` - node scripts/generate_scalability_simulations.js \ - --dir "" \ - --baseUrl "" \ - --packageName "org.kibanaLoadTest" -``` - -To run the generated simulation: -- Move file to `src/test/scala/org/kibanaLoadTest` -- Compile source code `mvn clean compile` -- Run simulation `mvn gatling:test -Dgatling.simulationClass=org.kibanaLoadTest.` diff --git a/packages/kbn-scalability-simulation-generator/src/build_simulation.ts b/packages/kbn-scalability-simulation-generator/src/build_simulation.ts deleted file mode 100644 index 7462ef13e5b2c..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/build_simulation.ts +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Stage } from './types/journey'; -import { Header, Request, Simulation } from './types/simulation'; - -const AUTH_PATH = '/internal/security/login'; -const B_SEARCH_PATH = '/internal/bsearch'; - -const getHeaders = (headers: readonly Header[]) => - headers - .map( - (header) => - `"${header.name}" -> ${JSON.stringify( - header.name !== 'Cookie' ? header.value : '${Cookie}' - )}` - ) - .join(',') - .replace(/^/, 'Map(') + ')'; - -const getPayload = (body: string) => JSON.stringify(body).replace(/"/g, '\\"'); - -const getDuration = (duration: string) => { - const value = duration.replace(/\D+/, ''); - return duration.endsWith('m') ? `${value} * 60` : value; -}; - -/** - * Builds Gatling simulation content from common template - * @param packageName scala package name, where simulation file will be placed - * @param simulationName scala class name - * @param protocol Gatling protocol string - * @param scenario Gatling scenario string - * @param setup Gatling simulation injection setup string - * @returns Gatling simulation content as string - */ -const buildSimulation = ( - packageName: string, - simulationName: string, - protocol: string, - scenario: string, - setup: string -) => - `package ${packageName} - -import scala.concurrent.duration._ - -import io.gatling.core.Predef._ -import io.gatling.http.Predef._ -import io.gatling.jdbc.Predef._ - -class ${simulationName} extends Simulation { -${protocol} - -${scenario} - -${setup} -}`; - -const buildAuthenticationExec = (path: string, headers: string, payload: string) => - ` .exec( - http("${path}") - .post("${path}") - .body(StringBody("${payload}")) - .asJson - .headers(${headers}) - .check(headerRegex("set-cookie", ".+?(?=;)").saveAs("Cookie")) - )`; - -const buildBSearchExec = (path: string, headers: string, payload: string) => - ` .exec( - http("${path}") - .post("${path}") - .headers(${headers}) - .body(StringBody(${payload})) - .asJson - .check(status.is(200).saveAs("status")) - .check(jsonPath("$.result.id").find.saveAs("requestId")) - .check(jsonPath("$.result.isPartial").find.saveAs("isPartial")) - ) - .exitHereIfFailed - // First response might be “partial”. Then we continue to fetch for the results - // using the request id returned from the first response - .asLongAs(session => - session("status").as[Int] == 200 - && session("isPartial").as[Boolean] - ) { - exec( - http("${path}") - .post("${path}") - .headers(${headers}) - .body(StringBody(${payload})) - .asJson - .check(status.is(200).saveAs("status")) - .check(jsonPath("$.result.isPartial").saveAs("isPartial")) - ) - .exitHereIfFailed - .pause(1) - }`; - -const buildCommonHttpExec = (path: string, method: string, headers: string) => - ` .exec( - http("${path}") - .${method}("${path}") - .headers(${headers}) - )`; - -const buildCommonHttpBodyExec = (path: string, method: string, headers: string, payload: string) => - ` .exec( - http("${path}") - .${method}("${path}") - .body(StringBody("${payload}")) - .asJson - .headers(${headers}) - )`; - -const addPause = (delay: number) => ` .pause(${delay}.milliseconds)`; - -const buildProtocol = (baseUrl: string) => - ` val httpProtocol = http - .baseUrl("${baseUrl}") - .inferHtmlResources() - .acceptHeader("*/*") - .acceptEncodingHeader("gzip, deflate") - .acceptLanguageHeader("en-US,en;q=0.9,ru;q=0.8,de;q=0.7") - .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36")`; - -const buildScenarioDefinition = (phase: string, scenarioName: string) => - ` val ${phase} = scenario("${scenarioName} ${phase}") - .exec(steps)`; - -/** - * Builds Gatling simulation setUp section, that defines injection for warmup and test scenarios - * @param warmupStages - * @param testStages - * @param maxDuration - * @returns Gatling simulation setUp as a string - */ -const buildSetup = (warmupStages: string, testStages: string, maxDuration: string) => - ` setUp( - warmup - .inject(${warmupStages}) - .protocols(httpProtocol) - .andThen( - test - .inject(${testStages}) - .protocols(httpProtocol) - ) - ).maxDuration(${maxDuration})`; - -const buildExecStep = (request: Request) => { - const headers = getHeaders(request.headers); - const method = request.method.toLowerCase(); - if (!request.body) { - return buildCommonHttpExec(request.path, method, headers); - } else if (request.path.includes(AUTH_PATH)) { - return buildAuthenticationExec(request.path, headers, getPayload(request.body)); - } else if (request.path.includes(B_SEARCH_PATH)) { - return buildBSearchExec(request.path, headers, getPayload(request.body)); - } else { - return buildCommonHttpBodyExec(request.path, method, headers, getPayload(request.body)); - } -}; - -/** - * Builds Gatling scenario body - * @param scenarioName scenario name - * @param requests Kibana API requests - * @returns Gatling scenario as a string - */ -const buildScenario = (scenarioName: string, requests: readonly Request[]): string => { - const warmupScn = buildScenarioDefinition('warmup', scenarioName); - const testScn = buildScenarioDefinition('test', scenarioName); - // convert requests into array of Gatling exec http calls - const execs = requests.map((request, index, reqArray) => { - // construct Gatling exec http calls - const exec = buildExecStep(request); - // add delay between requests - if (index < reqArray.length - 1) { - const delay = reqArray[index + 1].timestamp - request.timestamp; - if (delay > 0) { - return exec + '\n' + addPause(delay); - } - } - return exec; - }); - const steps = execs.join('\n'); - const finalSteps = steps.slice(0, steps.indexOf('.')) + steps.slice(steps.indexOf('.') + 1); - - return ' val steps =\n' + finalSteps + '\n\n' + warmupScn + '\n' + testScn + '\n'; -}; - -/** - * Builds injection setup for scenario - * @param stages Array of actions to be executed with users count and duration - * @returns scenario injection as a string - */ -const buildBenchmarkingModel = (stages: readonly Stage[]) => { - return stages - .map((stage) => { - return stage.action === 'constantConcurrentUsers' - ? `${stage.action}(${stage.maxUsersCount}) during (${getDuration(stage.duration)})` - : `${stage.action}(${stage.minUsersCount}) to ${stage.maxUsersCount} during (${getDuration( - stage.duration - )})`; - }) - .join(', '); -}; - -/** - * Generates Gatling-compatible simulation content - * @param params Simulation parameters - * @returns Gatling simulation content as string - */ -export const generateSimulationContent = (params: Simulation) => { - const { simulationName, packageName, scenarioName, baseUrl, requests, scalabilitySetup } = params; - const protocol = buildProtocol(baseUrl); - const scenario = buildScenario(scenarioName, requests); - const setup = buildSetup( - buildBenchmarkingModel(scalabilitySetup.warmup.stages), - buildBenchmarkingModel(scalabilitySetup.test.stages), - getDuration(scalabilitySetup.maxDuration) - ); - - return buildSimulation(packageName, simulationName, protocol, scenario, setup); -}; diff --git a/packages/kbn-scalability-simulation-generator/src/cli.ts b/packages/kbn-scalability-simulation-generator/src/cli.ts deleted file mode 100644 index b2386f7fbeaaf..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/cli.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** *********************************************************** - * - * Run `node scripts/generate_scalability_simulations --help` for usage information - * - *************************************************************/ - -import { run } from '@kbn/dev-cli-runner'; -import { createFlagError } from '@kbn/dev-cli-errors'; -import path from 'path'; -import fs from 'fs'; -import { generator } from './generate_files'; - -const gatlingBundlePackageName = 'computerdatabase'; - -export async function generateScalabilitySimulations() { - run( - async ({ log, flags }) => { - const baseUrl = flags.baseUrl; - if (baseUrl && typeof baseUrl !== 'string') { - throw createFlagError('--baseUrl must be a string'); - } - if (!baseUrl) { - throw createFlagError('--baseUrl must be defined'); - } - - if (typeof flags.dir !== 'string') { - throw createFlagError('--dir must be a string'); - } - - const dir = path.resolve(flags.dir); - if (!dir) { - throw createFlagError('--dir must be defined'); - } - if (!fs.existsSync(path.resolve(dir))) { - throw createFlagError('--dir must be an existing folder path'); - } - - if (typeof flags.packageName !== 'undefined' && typeof flags.packageName !== 'string') { - throw createFlagError('--packageName is optional, but must be a string'); - } - - const packageName = !flags.packageName ? gatlingBundlePackageName : flags.packageName; - - return generator({ - dir, - baseUrl, - packageName, - log, - }); - }, - { - description: `CLI to get scalability simulation file out of single user performance journey APM traces`, - flags: { - string: ['dir', 'baseUrl', 'packageName'], - help: ` - --dir Path to json files with APM traces, generated using kbn-performance-testing-dataset-extractor - --baseUrl Kibana server base url to use for scalability testing - --packageName Simulation file package reference: ${gatlingBundlePackageName} is used by default and assumes - a run with Gatling bundle. Use 'org.kibanaLoadTest' to run with 'kibana-load-testing' project. - `, - }, - usage: '--dir target/scalability_traces --baseUrl http://localhost:5620', - } - ); -} diff --git a/packages/kbn-scalability-simulation-generator/src/generate_files.ts b/packages/kbn-scalability-simulation-generator/src/generate_files.ts deleted file mode 100644 index 48ab6033af4e6..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/generate_files.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 { ToolingLog } from '@kbn/tooling-log'; -import fsp from 'fs/promises'; -import fs from 'fs'; -import path from 'path'; - -import { generateSimulationContent } from './build_simulation'; -import { getHttpRequests } from './parse_traces'; -import { Journey } from './types/journey'; - -export interface CLIParams { - dir: string; - baseUrl: string; - packageName: string; - log: ToolingLog; -} - -export const generator = async ({ dir, baseUrl, packageName, log }: CLIParams) => { - const jsonInDir = fs.readdirSync(dir).filter((file) => path.extname(file) === '.json'); - log.info(`Found ${jsonInDir.length} json files in path: ${jsonInDir}`); - - for (const file of jsonInDir) { - const jsonPath = path.resolve(dir, file); - const journey: Journey = JSON.parse(fs.readFileSync(jsonPath).toString()); - - if (!journey.traceItems || journey.traceItems.length === 0) { - log.error(`No 'traceItems' found in ${jsonPath}, skipping file`); - return; - } - - if (!journey.scalabilitySetup) { - log.error(`No 'scalabilitySetup' found in ${jsonPath}, skipping file`); - return; - } - - const requests = getHttpRequests(journey.traceItems); - requests.forEach((req) => - log.debug(`${req.date} ${req.transactionId} ${req.method} ${req.path}`) - ); - - const simulationName = journey.journeyName - .replace(/[^a-zA-Z ]/g, ' ') - .replace(/\b(\w)/g, (match, capture) => capture.toUpperCase()) - .replace(/\s+/g, ''); - - const fileName = `${simulationName}.scala`; - const outputDir = path.resolve('target/scalability_simulations'); - const filePath = path.resolve(outputDir, fileName); - - const fileContent = generateSimulationContent({ - simulationName, - packageName, - scenarioName: `${journey.journeyName} ${journey.kibanaVersion}`, - baseUrl, - scalabilitySetup: journey.scalabilitySetup, - requests, - }); - - if (!fs.existsSync(outputDir)) { - await fsp.mkdir(outputDir, { recursive: true }); - } - - const stream = fs.createWriteStream(filePath); - stream.write(fileContent); - stream.end(() => log.info(`Gatling simulation '${simulationName}' was saved in '${filePath}'`)); - } -}; diff --git a/packages/kbn-scalability-simulation-generator/src/parse_traces.ts b/packages/kbn-scalability-simulation-generator/src/parse_traces.ts deleted file mode 100644 index a6059a3b046dc..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/parse_traces.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { TraceItem } from './types/journey'; -import { Request } from './types/simulation'; - -export const getHttpRequests = (traces: readonly TraceItem[]): Request[] => { - return traces - .map((trace) => { - const timestamp = new Date(trace.timestamp).getTime(); - const date = trace.timestamp; - const transactionId = trace.transaction.id; - const method = trace.request.method; - const path = trace.request.url.path; - const rawHeaders = trace.request.headers; - const headers = Object.keys(rawHeaders).map((key) => ({ - name: key, - value: String(rawHeaders[key].join('')), - })); - const body = trace.request.body; - return { timestamp, date, transactionId, method, path, headers, body }; - }) - .sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1)); -}; diff --git a/packages/kbn-scalability-simulation-generator/src/types/journey.ts b/packages/kbn-scalability-simulation-generator/src/types/journey.ts deleted file mode 100644 index a9e3d19657955..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/types/journey.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 interface Headers { - readonly [key: string]: string[]; -} - -export interface HttpRequest { - readonly headers?: Headers[]; - readonly method?: string; - readonly body?: string; -} - -export interface HttpResponse { - readonly headers?: Headers[]; - readonly status_code?: number; -} - -export interface Http { - readonly request?: HttpRequest; - readonly response?: HttpResponse; -} - -export interface Trace { - readonly '@timestamp': string; - readonly transaction_id: string; - readonly url_path: string; - readonly url_base: string; - readonly span_id?: string; - readonly http?: Http; - readonly children?: readonly Trace[]; -} - -export interface Transaction { - readonly transactionName: string; - readonly transactionType: string; - readonly service: string; - readonly traces: readonly Trace[]; -} - -export interface Request { - readonly url: { - readonly path: string; - }; - readonly headers: Headers; - readonly method: string; - readonly body?: string; -} - -export interface TransactionItem { - readonly id: string; - readonly name: string; - readonly type: string; -} - -export interface TraceItem { - readonly traceId: string; - readonly timestamp: string; - readonly request: Request; - readonly response: { - readonly status: string; - }; - readonly transaction: TransactionItem; -} - -export interface Stage { - readonly action: string; - readonly minUsersCount?: number; - readonly maxUsersCount: number; - readonly duration: string; -} - -export interface Setup { - readonly warmup: { readonly stages: readonly Stage[] }; - readonly test: { readonly stages: readonly Stage[] }; - readonly maxDuration: string; -} - -export interface Journey { - readonly journeyName: string; - readonly kibanaVersion: string; - readonly scalabilitySetup: Setup; - readonly traceItems: readonly TraceItem[]; -} diff --git a/packages/kbn-scalability-simulation-generator/src/types/simulation.ts b/packages/kbn-scalability-simulation-generator/src/types/simulation.ts deleted file mode 100644 index c5fa394ebcc0c..0000000000000 --- a/packages/kbn-scalability-simulation-generator/src/types/simulation.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Setup } from './journey'; - -export interface Header { - readonly name: string; - readonly value: string; -} - -export interface Request { - readonly timestamp: number; - readonly date: string; - readonly transactionId: string; - readonly method: string; - readonly path: string; - readonly headers: readonly Header[]; - readonly body?: string; -} - -export interface Simulation { - readonly simulationName: string; - readonly packageName: string; - readonly scenarioName: string; - readonly baseUrl: string; - readonly scalabilitySetup: Setup; - readonly requests: readonly Request[]; -} diff --git a/packages/kbn-user-profile-components/BUILD.bazel b/packages/kbn-user-profile-components/BUILD.bazel new file mode 100644 index 0000000000000..876d6d2c84dd0 --- /dev/null +++ b/packages/kbn-user-profile-components/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_BASE_NAME = "kbn-user-profile-components" +PKG_REQUIRE_NAME = "@kbn/user-profile-components" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/kbn-i18n", + "//packages/kbn-i18n-react", + "@npm//@elastic/eui", +] + +TYPES_DEPS = [ + "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-i18n-react:npm_module_types", + "@npm//@elastic/eui", + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/enzyme", + "@npm//tslib", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-user-profile-components/jest.config.js b/packages/kbn-user-profile-components/jest.config.js new file mode 100644 index 0000000000000..2f8c630fc3e21 --- /dev/null +++ b/packages/kbn-user-profile-components/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-user-profile-components'], +}; diff --git a/packages/kbn-user-profile-components/package.json b/packages/kbn-user-profile-components/package.json new file mode 100644 index 0000000000000..86d4c48e2e163 --- /dev/null +++ b/packages/kbn-user-profile-components/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/user-profile-components", + "version": "1.0.0", + "private": true, + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "types": "./target_types/index.d.ts", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-user-profile-components/src/index.ts b/packages/kbn-user-profile-components/src/index.ts new file mode 100644 index 0000000000000..e36215e36896a --- /dev/null +++ b/packages/kbn-user-profile-components/src/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 { UserAvatarProps, UserProfileWithAvatar } from './user_avatar'; +export type { UserProfilesSelectableProps } from './user_profiles_selectable'; +export type { UserProfilesPopoverProps } from './user_profiles_popover'; +export { UserAvatar } from './user_avatar'; +export { UserProfilesSelectable } from './user_profiles_selectable'; +export { UserProfilesPopover } from './user_profiles_popover'; +export type { UserProfile, UserProfileUserInfo, UserProfileAvatarData } from './user_profile'; diff --git a/packages/kbn-user-profile-components/src/user_avatar.test.tsx b/packages/kbn-user-profile-components/src/user_avatar.test.tsx new file mode 100644 index 0000000000000..9c654d25f70fe --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_avatar.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 { shallow } from 'enzyme'; +import React from 'react'; + +import { UserAvatar } from './user_avatar'; + +describe('UserAvatar', () => { + it('should render `EuiAvatar` correctly with image avatar', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchInlineSnapshot(` + + `); + }); + + it('should render `EuiAvatar` correctly with initials avatar', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchInlineSnapshot(` + + `); + }); + + it('should render `EuiAvatar` correctly without avatar data', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchInlineSnapshot(` + + `); + }); + + it('should render `EuiAvatar` correctly without user data', () => { + const wrapper = shallow(); + expect(wrapper).toMatchInlineSnapshot(` + + `); + }); +}); diff --git a/x-pack/plugins/security/public/components/user_avatar.tsx b/packages/kbn-user-profile-components/src/user_avatar.tsx similarity index 53% rename from x-pack/plugins/security/public/components/user_avatar.tsx rename to packages/kbn-user-profile-components/src/user_avatar.tsx index c15792382f299..2413694317c27 100644 --- a/x-pack/plugins/security/public/components/user_avatar.tsx +++ b/packages/kbn-user-profile-components/src/user_avatar.tsx @@ -1,30 +1,59 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not 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 { EuiAvatarProps } from '@elastic/eui'; import { EuiAvatar, useEuiTheme } from '@elastic/eui'; -import type { FunctionComponent, HTMLAttributes } from 'react'; +import type { FunctionComponent } from 'react'; import React from 'react'; -import type { UserProfileAvatarData, UserProfileUserInfoWithSecurity } from '../../common'; +import type { UserProfile, UserProfileUserInfo, UserProfileAvatarData } from './user_profile'; import { getUserAvatarColor, getUserAvatarInitials, getUserDisplayName, USER_AVATAR_MAX_INITIALS, -} from '../../common/model'; +} from './user_profile'; -export interface UserAvatarProps extends Omit, 'color'> { - user?: Pick; +/** + * Convenience type for a {@link UserProfile} with avatar data + */ +export type UserProfileWithAvatar = UserProfile<{ avatar?: UserProfileAvatarData }>; + +/** + * Props of {@link UserAvatar} component + */ +export interface UserAvatarProps + extends Omit< + EuiAvatarProps, + | 'initials' + | 'initialsLength' + | 'imageUrl' + | 'iconType' + | 'iconSize' + | 'iconColor' + | 'name' + | 'color' + | 'type' + > { + /** + * User to be rendered + */ + user?: UserProfileUserInfo; + + /** + * Avatar data of user to be rendered + */ avatar?: UserProfileAvatarData; - size?: EuiAvatarProps['size']; - isDisabled?: EuiAvatarProps['isDisabled']; } +/** + * Renders an avatar given a user profile + */ export const UserAvatar: FunctionComponent = ({ user, avatar, ...rest }) => { const { euiTheme } = useEuiTheme(); diff --git a/packages/kbn-user-profile-components/src/user_profile.ts b/packages/kbn-user-profile-components/src/user_profile.ts new file mode 100644 index 0000000000000..59c5f2f5a12a8 --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_profile.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { VISUALIZATION_COLORS } from '@elastic/eui'; + +/** + * IMPORTANT: + * + * The types in this file have been imported from + * `x-pack/plugins/security/common/model/user_profile.ts` + * + * When making changes please ensure to keep both files in sync. + */ + +/** + * Describes basic properties stored in user profile. + */ +export interface UserProfile { + /** + * Unique ID for of the user profile. + */ + uid: string; + + /** + * Information about the user that owns profile. + */ + user: UserProfileUserInfo; + + /** + * User specific data associated with the profile. + */ + data: Partial; +} + +/** + * Basic user information returned in user profile. + */ +export interface UserProfileUserInfo { + /** + * Username of the user. + */ + username: string; + /** + * Optional email of the user. + */ + email?: string; + /** + * Optional full name of the user. + */ + full_name?: string; + /** + * Optional display name of the user. + */ + display_name?: string; +} + +/** + * Placeholder for data stored in user profile. + */ +export type UserProfileData = Record; + +/** + * Avatar stored in user profile. + */ +export interface UserProfileAvatarData { + /** + * Optional initials (two letters) of the user to use as avatar if avatar picture isn't specified. + */ + initials?: string; + /** + * Background color of the avatar when initials are used. + */ + color?: string; + /** + * Base64 data URL for the user avatar image. + */ + imageUrl?: string; +} + +export const USER_AVATAR_FALLBACK_CODE_POINT = 97; // code point for lowercase "a" +export const USER_AVATAR_MAX_INITIALS = 2; + +/** + * Determines the color for the provided user profile. + * If a color is present on the user profile itself, then that is used. + * Otherwise, a color is provided from EUI's Visualization Colors based on the display name. + * + * @param {UserProfileUserInfo} user User info + * @param {UserProfileAvatarData} avatar User avatar + */ +export function getUserAvatarColor( + user: Pick, + avatar?: UserProfileAvatarData +) { + if (avatar && avatar.color) { + return avatar.color; + } + + const firstCodePoint = getUserDisplayName(user).codePointAt(0) || USER_AVATAR_FALLBACK_CODE_POINT; + + return VISUALIZATION_COLORS[firstCodePoint % VISUALIZATION_COLORS.length]; +} + +/** + * Determines the initials for the provided user profile. + * If initials are present on the user profile itself, then that is used. + * Otherwise, the initials are calculated based off the words in the display name, with a max length of 2 characters. + * + * @param {UserProfileUserInfo} user User info + * @param {UserProfileAvatarData} avatar User avatar + */ +export function getUserAvatarInitials( + user: Pick, + avatar?: UserProfileAvatarData +) { + if (avatar && avatar.initials) { + return avatar.initials; + } + + const words = getUserDisplayName(user).split(' '); + const numInitials = Math.min(USER_AVATAR_MAX_INITIALS, words.length); + + words.splice(numInitials, words.length); + + return words.map((word) => word.substring(0, 1)).join(''); +} + +/** + * Determines the display name for the provided user profile. + * + * @param {UserProfileUserInfo} user User info + */ +export function getUserDisplayName(user: Pick) { + return user.full_name || user.username; +} diff --git a/packages/kbn-user-profile-components/src/user_profiles_popover.test.tsx b/packages/kbn-user-profile-components/src/user_profiles_popover.test.tsx new file mode 100644 index 0000000000000..608ea32f7caa6 --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_profiles_popover.test.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { shallow } from 'enzyme'; +import React from 'react'; + +import { UserProfilesPopover } from './user_profiles_popover'; + +const userProfiles = [ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@profiles.elastic.co', + full_name: 'Delighted Nightingale', + }, + }, + { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + data: {}, + user: { + username: 'damaged_raccoon', + email: 'damaged_raccoon@profiles.elastic.co', + full_name: 'Damaged Raccoon', + }, + }, + { + uid: 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + data: {}, + user: { + username: 'physical_dinosaur', + email: 'physical_dinosaur@profiles.elastic.co', + full_name: 'Physical Dinosaur', + }, + }, + { + uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + data: {}, + user: { + username: 'wet_dingo', + email: 'wet_dingo@profiles.elastic.co', + full_name: 'Wet Dingo', + }, + }, +]; + +describe('UserProfilesPopover', () => { + it('should render `EuiPopover` and `UserProfilesSelectable` correctly', () => { + const [firstOption, secondOption] = userProfiles; + const wrapper = shallow( + Toggle} + closePopover={jest.fn()} + selectableProps={{ + selectedOptions: [firstOption], + defaultOptions: [secondOption], + }} + /> + ); + expect(wrapper).toMatchInlineSnapshot(` + + Toggle + + } + closePopover={[MockFunction]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > + + + + + `); + }); +}); diff --git a/packages/kbn-user-profile-components/src/user_profiles_popover.tsx b/packages/kbn-user-profile-components/src/user_profiles_popover.tsx new file mode 100644 index 0000000000000..9fc553d9be689 --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_profiles_popover.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { EuiPopoverProps, EuiContextMenuPanelProps } from '@elastic/eui'; +import type { FunctionComponent } from 'react'; +import React from 'react'; +import { EuiPopover, EuiContextMenuPanel } from '@elastic/eui'; + +import { UserProfilesSelectable, UserProfilesSelectableProps } from './user_profiles_selectable'; + +/** + * Props of {@link UserProfilesPopover} component + */ +export interface UserProfilesPopoverProps extends EuiPopoverProps { + /** + * Title of the popover + * @see EuiContextMenuPanelProps + */ + title?: EuiContextMenuPanelProps['title']; + + /** + * Props forwarded to selectable component + * @see UserProfilesSelectableProps + */ + selectableProps: UserProfilesSelectableProps; +} + +/** + * Renders a selectable component inside a popover given a list of user profiles + */ +export const UserProfilesPopover: FunctionComponent = ({ + title, + selectableProps, + ...popoverProps +}) => { + return ( + + + + + + ); +}; diff --git a/packages/kbn-user-profile-components/src/user_profiles_selectable.test.tsx b/packages/kbn-user-profile-components/src/user_profiles_selectable.test.tsx new file mode 100644 index 0000000000000..6231a3c2f1509 --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_profiles_selectable.test.tsx @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mount } from 'enzyme'; +import React from 'react'; + +import { UserProfilesSelectable } from './user_profiles_selectable'; + +const userProfiles = [ + { + uid: 'u_BOulL4QMPSyV9jg5lQI2JmCkUnokHTazBnet3xVHNv0_0', + data: {}, + user: { + username: 'delighted_nightingale', + email: 'delighted_nightingale@profiles.elastic.co', + full_name: 'Delighted Nightingale', + }, + }, + { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + data: {}, + user: { + username: 'damaged_raccoon', + email: 'damaged_raccoon@profiles.elastic.co', + full_name: 'Damaged Raccoon', + }, + }, + { + uid: 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + data: {}, + user: { + username: 'physical_dinosaur', + email: 'physical_dinosaur@profiles.elastic.co', + full_name: 'Physical Dinosaur', + }, + }, + { + uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + data: {}, + user: { + username: 'wet_dingo', + email: 'wet_dingo@profiles.elastic.co', + full_name: 'Wet Dingo', + }, + }, +]; + +describe('UserProfilesSelectable', () => { + it('should render `selectedOptions` before `defaultOptions` separated by a group label', () => { + const [firstOption, secondOption, thirdOption] = userProfiles; + const wrapper = mount( + + ); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: firstOption.uid, + checked: 'on', + }), + expect.objectContaining({ + isGroupLabel: true, + label: 'Suggested', + }), + expect.objectContaining({ + key: secondOption.uid, + checked: undefined, + }), + expect.objectContaining({ + key: thirdOption.uid, + checked: undefined, + }), + ]); + }); + + it('should hide `selectedOptions` and `defaultOptions` when `options` has been provided', () => { + const [firstOption, secondOption, thirdOption] = userProfiles; + const wrapper = mount( + + ); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: thirdOption.uid, + checked: undefined, + }), + ]); + }); + + it('should hide `selectedOptions` and `defaultOptions` when `options` gets updated', () => { + const [firstOption, secondOption, thirdOption] = userProfiles; + const wrapper = mount( + + ); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: firstOption.uid, + checked: 'on', + }), + expect.objectContaining({ + isGroupLabel: true, + label: 'Suggested', + }), + expect.objectContaining({ + key: secondOption.uid, + checked: undefined, + }), + ]); + + wrapper.setProps({ options: [thirdOption] }).update(); + + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: thirdOption.uid, + checked: undefined, + }), + ]); + }); + + it('should render `options` with correct checked status', () => { + const [firstOption, secondOption] = userProfiles; + const wrapper = mount( + + ); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: firstOption.uid, + checked: 'on', + }), + expect.objectContaining({ + key: secondOption.uid, + checked: undefined, + }), + ]); + }); + + it('should trigger `onChange` callback when selection changes', () => { + const onChange = jest.fn(); + const [firstOption, secondOption] = userProfiles; + const wrapper = mount( + + ); + wrapper.find('EuiSelectableListItem').last().simulate('click'); + expect(onChange).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + uid: firstOption.uid, + }), + expect.objectContaining({ + uid: secondOption.uid, + }), + ]) + ); + }); + + it('should continue to display `selectedOptions` when getting unchecked', () => { + const onChange = jest.fn(); + const [firstOption] = userProfiles; + const wrapper = mount( + + ); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: firstOption.uid, + checked: 'on', + }), + ]); + wrapper.setProps({ selectedOptions: [] }).update(); + expect(wrapper.find('EuiSelectable').prop('options')).toEqual([ + expect.objectContaining({ + key: firstOption.uid, + checked: undefined, + }), + ]); + }); + + it('should trigger `onSearchChange` callback when search term changes', () => { + const onSearchChange = jest.fn(); + const wrapper = mount(); + wrapper.find('input[type="search"]').simulate('change', { target: { value: 'search' } }); + expect(onSearchChange).toHaveBeenCalledWith('search', []); + }); +}); diff --git a/packages/kbn-user-profile-components/src/user_profiles_selectable.tsx b/packages/kbn-user-profile-components/src/user_profiles_selectable.tsx new file mode 100644 index 0000000000000..14a44f66650aa --- /dev/null +++ b/packages/kbn-user-profile-components/src/user_profiles_selectable.tsx @@ -0,0 +1,311 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { EuiSelectableOption, EuiSelectableProps } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiPanel, + EuiSelectable, + EuiSpacer, + EuiText, + EuiTextColor, +} from '@elastic/eui'; +import type { FunctionComponent, ReactNode } from 'react'; +import React, { useEffect, useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { getUserDisplayName } from './user_profile'; +import type { UserProfileWithAvatar } from './user_avatar'; +import { UserAvatar } from './user_avatar'; + +/** + * Props of {@link UserProfilesSelectable} component + */ +export interface UserProfilesSelectableProps + extends Pick< + EuiSelectableProps, + | 'height' + | 'singleSelection' + | 'loadingMessage' + | 'noMatchesMessage' + | 'emptyMessage' + | 'errorMessage' + > { + /** + * List of users to be rendered as suggestions. + */ + defaultOptions?: UserProfileWithAvatar[]; + + /** + * List of selected users. + */ + selectedOptions?: UserProfileWithAvatar[]; + + /** + * List of users from search results. Should be updated based on the search term provided by `onSearchChange` callback. + */ + options?: UserProfileWithAvatar[]; + + /** + * Passes back the list of selected users. + * @param options List of selected users + */ + onChange?(options: UserProfileWithAvatar[]): void; + + /** + * Passes back the search term. + * @param searchTerm Search term + */ + onSearchChange?(searchTerm: string): void; + + /** + * Loading indicator for asynchronous search operations. + */ + isLoading?: boolean; + + /** + * Placeholder text for search box. + */ + searchPlaceholder?: string; + + /** + * Returns text for selected status. + * @param selectedCount Number of selected users + */ + selectedStatusMessage?(selectedCount: number): ReactNode; + + /** + * Text for label of clear button. + */ + clearButtonLabel?: ReactNode; +} + +/** + * Renders a selectable component given a list of user profiles + */ +export const UserProfilesSelectable: FunctionComponent = ({ + selectedOptions, + defaultOptions, + options, + onChange, + onSearchChange, + isLoading = false, + singleSelection = false, + height, + loadingMessage, + noMatchesMessage, + emptyMessage, + errorMessage, + searchPlaceholder, + selectedStatusMessage, + clearButtonLabel, +}) => { + const [displayedOptions, setDisplayedOptions] = useState([]); + + // Resets all displayed options + const resetDisplayedOptions = () => { + if (options) { + setDisplayedOptions(options.map(toSelectableOption)); + return; + } + + setDisplayedOptions([]); + updateDisplayedOptions(); + }; + + const ensureSeparator = (values: SelectableOption[]) => { + let index = values.findIndex((option) => option.isGroupLabel); + if (index === -1) { + const length = values.push({ + label: i18n.translate('userProfileComponents.userProfilesSelectable.suggestedLabel', { + defaultMessage: 'Suggested', + }), + isGroupLabel: true, + } as SelectableOption); + index = length - 1; + } + return index; + }; + + // Updates displayed options without removing or resorting exiting options + const updateDisplayedOptions = () => { + if (options) { + return; + } + + setDisplayedOptions((values) => { + // Copy all displayed options + const nextOptions: SelectableOption[] = [...values]; + + // Get any newly added selected options + const selectedOptionsToAdd: SelectableOption[] = selectedOptions + ? selectedOptions + .filter((profile) => !nextOptions.find((option) => option.key === profile.uid)) + .map(toSelectableOption) + : []; + + // Get any newly added default options + const defaultOptionsToAdd: SelectableOption[] = defaultOptions + ? defaultOptions + .filter( + (profile) => + !nextOptions.find((option) => option.key === profile.uid) && + !selectedOptionsToAdd.find((option) => option.key === profile.uid) + ) + .map(toSelectableOption) + : []; + + // Merge in any new options and add group separator if necessary + if (defaultOptionsToAdd.length) { + const separatorIndex = ensureSeparator(nextOptions); + nextOptions.splice(separatorIndex, 0, ...selectedOptionsToAdd); + nextOptions.push(...defaultOptionsToAdd); + } else { + nextOptions.push(...selectedOptionsToAdd); + } + + return nextOptions; + }); + }; + + // Marks displayed options as checked or unchecked depending on `props.selectedOptions` + const updateCheckedStatus = () => { + setDisplayedOptions((values) => + values.map((option) => { + if (selectedOptions) { + const match = selectedOptions.find((p) => p.uid === option.key); + return { ...option, checked: match ? 'on' : undefined }; + } + return { ...option, checked: undefined }; + }) + ); + }; + + useEffect(resetDisplayedOptions, [options]); // eslint-disable-line react-hooks/exhaustive-deps + useEffect(updateDisplayedOptions, [defaultOptions, selectedOptions]); // eslint-disable-line react-hooks/exhaustive-deps + useEffect(updateCheckedStatus, [options, defaultOptions, selectedOptions]); + + const selectedCount = selectedOptions ? selectedOptions.length : 0; + + return ( + >) => { + if (!onChange) { + return; + } + + // Take all selected options from `nextOptions` unless already in `props.selectedOptions` + const values: UserProfileWithAvatar[] = nextOptions + .filter((option) => { + if (option.isGroupLabel || option.checked !== 'on') { + return false; + } + if (selectedOptions && selectedOptions.find((p) => p.uid === option.key)) { + return false; + } + return true; + }) + .map((option) => option.data); + + // Add all options from `props.selectedOptions` unless they have been deselected in `nextOptions` + if (selectedOptions && !singleSelection) { + selectedOptions.forEach((profile) => { + const match = nextOptions.find((o) => o.key === profile.uid); + if (!match || match.checked === 'on') { + values.push(profile); + } + }); + } + + onChange(values); + }} + style={{ maxHeight: height }} + singleSelection={singleSelection} + searchable + searchProps={{ + placeholder: + searchPlaceholder ?? + i18n.translate('userProfileComponents.userProfilesSelectable.searchPlaceholder', { + defaultMessage: 'Search', + }), + onChange: onSearchChange, + isLoading, + isClearable: !isLoading, + }} + isPreFiltered + listProps={{ onFocusBadge: false }} + loadingMessage={loadingMessage} + noMatchesMessage={noMatchesMessage} + emptyMessage={emptyMessage} + errorMessage={errorMessage} + > + {(list, search) => ( + <> + + {search} + + + + + {selectedStatusMessage ? ( + selectedStatusMessage(selectedCount) + ) : ( + + )} + + + + {selectedCount ? ( + onChange?.([])} + style={{ height: '1rem' }} + > + {clearButtonLabel ?? ( + + )} + + ) : undefined} + + + + + {list} + + )} + + ); +}; + +type SelectableOption = EuiSelectableOption; + +function toSelectableOption(userProfile: UserProfileWithAvatar): SelectableOption { + // @ts-ignore: `isGroupLabel` is not required here but TS complains + return { + key: userProfile.uid, + prepend: , + label: getUserDisplayName(userProfile.user), + append: {userProfile.user.email}, + data: userProfile, + }; +} diff --git a/packages/kbn-user-profile-components/tsconfig.json b/packages/kbn-user-profile-components/tsconfig.json new file mode 100644 index 0000000000000..525d036bdcd49 --- /dev/null +++ b/packages/kbn-user-profile-components/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "./target_types", + "stripInternal": true, + "types": [ + "jest", + "node" + ] + }, + "include": ["src/**/*"] +} diff --git a/scripts/update_vscode_config.js b/scripts/update_vscode_config.js index d39e4ce757ce5..2b23d8004f793 100644 --- a/scripts/update_vscode_config.js +++ b/scripts/update_vscode_config.js @@ -7,4 +7,4 @@ */ require('../src/setup_node_env/no_transpilation'); -require('@kbn/dev-utils').runUpdateVscodeConfigCli(); +require('@kbn/managed-vscode-config-cli'); diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index eb8b564c2e9ea..f9f9f6e90afdd 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -41,6 +41,15 @@ import { } from './core_system.test.mocks'; import { CoreSystem } from './core_system'; +import { + KIBANA_LOADED_EVENT, + LOAD_START, + LOAD_BOOTSTRAP_START, + LOAD_CORE_CREATED, + LOAD_FIRST_NAV, + LOAD_SETUP_DONE, + LOAD_START_DONE, +} from './events'; jest.spyOn(CoreSystem.prototype, 'stop'); @@ -75,12 +84,28 @@ beforeEach(() => { window.performance.clearMarks = jest.fn(); window.performance.getEntriesByName = jest.fn().mockReturnValue([ { - detail: 'load_started', - startTime: 456, + detail: LOAD_START, + startTime: 111, + }, + { + detail: LOAD_BOOTSTRAP_START, + startTime: 222, + }, + { + detail: LOAD_CORE_CREATED, + startTime: 333, }, { - detail: 'bootstrap_started', - startTime: 123, + detail: LOAD_SETUP_DONE, + startTime: 444, + }, + { + detail: LOAD_START_DONE, + startTime: 555, + }, + { + detail: LOAD_FIRST_NAV, + startTime: 666, }, ]); }); @@ -248,38 +273,71 @@ describe('#start()', () => { ); }); - it('reports the event Loaded Kibana and clears marks', async () => { + it('reports the deprecated event Loaded Kibana', async () => { await startCore(); - expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(1); - expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(2); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenNthCalledWith(1, 'Loaded Kibana', { kibana_version: '1.2.3', - load_started: 456, - bootstrap_started: 123, protocol: 'http:', }); expect(window.performance.clearMarks).toHaveBeenCalledTimes(1); }); - it('reports the event Loaded Kibana (with memory)', async () => { - fetchOptionalMemoryInfoMock.mockReturnValue({ - load_started: 456, - bootstrap_started: 123, - memory_js_heap_size_limit: 3, - memory_js_heap_size_total: 2, - memory_js_heap_size_used: 1, + it('reports the metric event kibana-loaded and clears marks', async () => { + await startCore(); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(2); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenNthCalledWith(2, 'performance_metric', { + eventName: KIBANA_LOADED_EVENT, + meta: { + kibana_version: '1.2.3', + protocol: 'http:', + }, + key1: LOAD_START, + key2: LOAD_BOOTSTRAP_START, + key3: LOAD_CORE_CREATED, + key4: LOAD_SETUP_DONE, + key5: LOAD_START_DONE, + value1: 111, + value2: 222, + value3: 333, + value4: 444, + value5: 555, + duration: 666, }); + expect(window.performance.clearMarks).toHaveBeenCalledTimes(1); + }); + + it('reports the event kibana-loaded (with memory)', async () => { + const performanceMemory = { + usedJSHeapSize: 1, + jsHeapSizeLimit: 3, + totalJSHeapSize: 4, + }; + fetchOptionalMemoryInfoMock.mockReturnValue(performanceMemory); + await startCore(); - expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(1); - expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { - load_started: 456, - bootstrap_started: 123, - kibana_version: '1.2.3', - memory_js_heap_size_limit: 3, - memory_js_heap_size_total: 2, - memory_js_heap_size_used: 1, - protocol: 'http:', + + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(2); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenNthCalledWith(2, 'performance_metric', { + eventName: KIBANA_LOADED_EVENT, + meta: { + kibana_version: '1.2.3', + protocol: 'http:', + ...performanceMemory, + }, + key1: LOAD_START, + key2: LOAD_BOOTSTRAP_START, + key3: LOAD_CORE_CREATED, + key4: LOAD_SETUP_DONE, + key5: LOAD_START_DONE, + value1: 111, + value2: 222, + value3: 333, + value4: 444, + value5: 555, + duration: 666, }); }); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 0ba7d4f1fc285..c32a10704db06 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -26,18 +26,29 @@ import { HttpService } from '@kbn/core-http-browser-internal'; import { UiSettingsService } from '@kbn/core-ui-settings-browser-internal'; import { DeprecationsService } from '@kbn/core-deprecations-browser-internal'; import { IntegrationsService } from '@kbn/core-integrations-browser-internal'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { OverlayService } from '@kbn/core-overlays-browser-internal'; import { KBN_LOAD_MARKS } from '@kbn/core-mount-utils-browser-internal'; +import { SavedObjectsService } from '@kbn/core-saved-objects-browser-internal'; import { NotificationsService } from '@kbn/core-notifications-browser-internal'; +import { fetchOptionalMemoryInfo } from './fetch_optional_memory_info'; import { CoreSetup, CoreStart } from '.'; import { ChromeService } from './chrome'; import { PluginsService } from './plugins'; import { ApplicationService } from './application'; import { RenderingService } from './rendering'; -import { SavedObjectsService } from './saved_objects'; import { CoreApp } from './core_app'; import type { InternalApplicationSetup, InternalApplicationStart } from './application/types'; -import { fetchOptionalMemoryInfo } from './fetch_optional_memory_info'; + +import { + LOAD_SETUP_DONE, + LOAD_START_DONE, + KIBANA_LOADED_EVENT, + LOAD_CORE_CREATED, + LOAD_FIRST_NAV, + LOAD_BOOTSTRAP_START, + LOAD_START, +} from './events'; interface Params { rootDomElement: HTMLElement; @@ -129,12 +140,12 @@ export class CoreSystem { this.coreApp = new CoreApp(this.coreContext); performance.mark(KBN_LOAD_MARKS, { - detail: 'core_created', + detail: LOAD_CORE_CREATED, }); } - private getLoadMarksInfo() { - if (!performance) return []; + private getLoadMarksInfo(): Record { + if (!performance) return {}; const reportData: Record = {}; const marks = performance.getEntriesByName(KBN_LOAD_MARKS); for (const mark of marks) { @@ -145,11 +156,33 @@ export class CoreSystem { } private reportKibanaLoadedEvent(analytics: AnalyticsServiceStart) { + /** + * @deprecated here for backwards compatibility in FullStory + **/ analytics.reportEvent('Loaded Kibana', { kibana_version: this.coreContext.env.packageInfo.version, protocol: window.location.protocol, - ...fetchOptionalMemoryInfo(), - ...this.getLoadMarksInfo(), + }); + + const timing = this.getLoadMarksInfo(); + reportPerformanceMetricEvent(analytics, { + eventName: KIBANA_LOADED_EVENT, + meta: { + kibana_version: this.coreContext.env.packageInfo.version, + protocol: window.location.protocol, + ...fetchOptionalMemoryInfo(), + }, + duration: timing[LOAD_FIRST_NAV], + key1: LOAD_START, + value1: timing[LOAD_START], + key2: LOAD_BOOTSTRAP_START, + value2: timing[LOAD_BOOTSTRAP_START], + key3: LOAD_CORE_CREATED, + value3: timing[LOAD_CORE_CREATED], + key4: LOAD_SETUP_DONE, + value4: timing[LOAD_SETUP_DONE], + key5: LOAD_START_DONE, + value5: timing[LOAD_START_DONE], }); performance.clearMarks(KBN_LOAD_MARKS); } @@ -170,6 +203,7 @@ export class CoreSystem { this.docLinks.setup(); const analytics = this.analytics.setup({ injectedMetadata }); + this.registerLoadedKibanaEventType(analytics); const executionContext = this.executionContext.setup({ analytics }); @@ -200,7 +234,7 @@ export class CoreSystem { await this.plugins.setup(core); performance.mark(KBN_LOAD_MARKS, { - detail: 'setup_done', + detail: LOAD_SETUP_DONE, }); return { fatalErrors: this.fatalErrorsSetup }; @@ -300,13 +334,13 @@ export class CoreSystem { }); performance.mark(KBN_LOAD_MARKS, { - detail: 'start_done', + detail: LOAD_START_DONE, }); // Wait for the first app navigation to report Kibana Loaded firstValueFrom(application.currentAppId$.pipe(filter(Boolean))).then(() => { performance.mark(KBN_LOAD_MARKS, { - detail: 'first_app_nav', + detail: LOAD_FIRST_NAV, }); this.reportKibanaLoadedEvent(analytics); }); @@ -342,6 +376,9 @@ export class CoreSystem { this.rootDomElement.textContent = ''; } + /** + * @deprecated + */ private registerLoadedKibanaEventType(analytics: AnalyticsServiceSetup) { analytics.registerEventType({ eventType: 'Loaded Kibana', @@ -350,45 +387,6 @@ export class CoreSystem { type: 'keyword', _meta: { description: 'The version of Kibana' }, }, - memory_js_heap_size_limit: { - type: 'long', - _meta: { description: 'The maximum size of the heap', optional: true }, - }, - memory_js_heap_size_total: { - type: 'long', - _meta: { description: 'The total size of the heap', optional: true }, - }, - memory_js_heap_size_used: { - type: 'long', - _meta: { description: 'The used size of the heap', optional: true }, - }, - load_started: { - type: 'long', - _meta: { description: 'When the render template starts loading assets', optional: true }, - }, - bootstrap_started: { - type: 'long', - _meta: { description: 'When kbnBootstrap callback is called', optional: true }, - }, - core_created: { - type: 'long', - _meta: { description: 'When core system is created', optional: true }, - }, - setup_done: { - type: 'long', - _meta: { description: 'When core system setup is complete', optional: true }, - }, - start_done: { - type: 'long', - _meta: { description: 'When core system start is complete', optional: true }, - }, - first_app_nav: { - type: 'long', - _meta: { - description: 'When the application emits the first app navigation', - optional: true, - }, - }, protocol: { type: 'keyword', _meta: { diff --git a/src/core/public/events.ts b/src/core/public/events.ts new file mode 100644 index 0000000000000..e2f5d48ddfe3d --- /dev/null +++ b/src/core/public/events.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal */ +export const KBN_LOAD_MARKS = 'kbnLoad'; + +export const KIBANA_LOADED_EVENT = 'kibana_loaded'; + +export const LOAD_START = 'load_started'; +export const LOAD_BOOTSTRAP_START = 'bootstrap_started'; +export const LOAD_CORE_CREATED = 'core_created'; +export const LOAD_SETUP_DONE = 'setup_done'; +export const LOAD_START_DONE = 'start_done'; +export const LOAD_FIRST_NAV = 'first_app_nav'; diff --git a/src/core/public/fetch_optional_memory_info.test.ts b/src/core/public/fetch_optional_memory_info.test.ts index f92fad9c14d63..4eca4cf0d11de 100644 --- a/src/core/public/fetch_optional_memory_info.test.ts +++ b/src/core/public/fetch_optional_memory_info.test.ts @@ -27,9 +27,9 @@ describe('fetchOptionalMemoryInfo', () => { }, }; expect(fetchOptionalMemoryInfo()).toEqual({ - memory_js_heap_size_limit: 3, - memory_js_heap_size_total: 2, - memory_js_heap_size_used: 1, + jsHeapSizeLimit: 3, + totalJSHeapSize: 2, + usedJSHeapSize: 1, }); }); }); diff --git a/src/core/public/fetch_optional_memory_info.ts b/src/core/public/fetch_optional_memory_info.ts index b18f3ca2698da..957388d55453d 100644 --- a/src/core/public/fetch_optional_memory_info.ts +++ b/src/core/public/fetch_optional_memory_info.ts @@ -14,15 +14,15 @@ export interface BrowserPerformanceMemoryInfo { /** * The maximum size of the heap, in bytes, that is available to the context. */ - memory_js_heap_size_limit: number; + jsHeapSizeLimit: number; /** * The total allocated heap size, in bytes. */ - memory_js_heap_size_total: number; + totalJSHeapSize: number; /** * The currently active segment of JS heap, in bytes. */ - memory_js_heap_size_used: number; + usedJSHeapSize: number; } /** @@ -34,9 +34,9 @@ export function fetchOptionalMemoryInfo(): BrowserPerformanceMemoryInfo | undefi const memory = window.performance.memory; if (memory) { return { - memory_js_heap_size_limit: memory.jsHeapSizeLimit, - memory_js_heap_size_total: memory.totalJSHeapSize, - memory_js_heap_size_used: memory.usedJSHeapSize, + jsHeapSizeLimit: memory.jsHeapSizeLimit, + totalJSHeapSize: memory.totalJSHeapSize, + usedJSHeapSize: memory.usedJSHeapSize, }; } } diff --git a/src/core/public/index.ts b/src/core/public/index.ts index fe18a711123e6..1cf8f30136d4b 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -46,6 +46,7 @@ import type { UiSettingsState, IUiSettingsClient } from '@kbn/core-ui-settings-b import type { DeprecationsServiceStart } from '@kbn/core-deprecations-browser'; import type { Capabilities } from '@kbn/core-capabilities-common'; import type { OverlayStart } from '@kbn/core-overlays-browser'; +import type { SavedObjectsStart } from '@kbn/core-saved-objects-browser'; import type { NotificationsSetup, NotificationsStart } from '@kbn/core-notifications-browser'; import type { ChromeBadge, @@ -76,7 +77,6 @@ import type { PluginOpaqueId, } from './plugins'; import type { ApplicationSetup, ApplicationStart } from './application'; -import type { SavedObjectsStart } from './saved_objects'; export type { PackageInfo, EnvironmentMode } from '@kbn/config'; export type { DomainDeprecationDetails } from '@kbn/core-deprecations-common'; @@ -128,8 +128,6 @@ export type { NavigateToAppOptions, NavigateToUrlOptions, } from './application'; - -export { SimpleSavedObjectImpl } from './saved_objects'; export type { SavedObjectsClientContract, SimpleSavedObject, diff --git a/src/core/public/kbn_bootstrap.ts b/src/core/public/kbn_bootstrap.ts index e97c7e58a48d5..0359f9e4d6520 100644 --- a/src/core/public/kbn_bootstrap.ts +++ b/src/core/public/kbn_bootstrap.ts @@ -11,10 +11,12 @@ import { KBN_LOAD_MARKS } from '@kbn/core-mount-utils-browser-internal'; import { CoreSystem } from './core_system'; import { ApmSystem } from './apm_system'; +import { LOAD_BOOTSTRAP_START } from './events'; + /** @internal */ export async function __kbnBootstrap__() { performance.mark(KBN_LOAD_MARKS, { - detail: 'bootstrap_started', + detail: LOAD_BOOTSTRAP_START, }); const injectedMetadata = JSON.parse( diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 38d9431e6d0e6..377bf7d7c6885 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -20,14 +20,13 @@ import { httpServiceMock } from '@kbn/core-http-browser-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { deprecationsServiceMock } from '@kbn/core-deprecations-browser-mocks'; import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; +import { savedObjectsServiceMock } from '@kbn/core-saved-objects-browser-mocks'; import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; import type { PluginInitializerContext, AppMountParameters } from '.'; // Import values from their individual modules instead. import { ScopedHistory } from './application'; import { applicationServiceMock } from './application/application_service.mock'; import { chromeServiceMock } from './chrome/chrome_service.mock'; -import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; - export { injectedMetadataServiceMock } from '@kbn/core-injected-metadata-browser-mocks'; export { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks'; export { themeServiceMock } from '@kbn/core-theme-browser-mocks'; @@ -40,7 +39,10 @@ export { i18nServiceMock } from '@kbn/core-i18n-browser-mocks'; export { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; export { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; export { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; -export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; +export { + savedObjectsServiceMock, + simpleSavedObjectMock, +} from '@kbn/core-saved-objects-browser-mocks'; export { scopedHistoryMock } from './application/scoped_history.mock'; export { applicationServiceMock } from './application/application_service.mock'; export { deprecationsServiceMock } from '@kbn/core-deprecations-browser-mocks'; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index bd2c5f5fb9705..d2faa81231401 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -37,7 +37,7 @@ import { fatalErrorsServiceMock } from '@kbn/core-fatal-errors-browser-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { httpServiceMock } from '@kbn/core-http-browser-mocks'; import type { CoreSetup, CoreStart, PluginInitializerContext } from '..'; -import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; +import { savedObjectsServiceMock } from '@kbn/core-saved-objects-browser-mocks'; import { deprecationsServiceMock } from '@kbn/core-deprecations-browser-mocks'; export let mockPluginInitializers: Map; diff --git a/src/core/server/integration_tests/saved_objects/migrations/incompatible_cluster_routing_allocation.test.ts b/src/core/server/integration_tests/saved_objects/migrations/incompatible_cluster_routing_allocation.test.ts index cf00a15c795f2..95f6445812d81 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/incompatible_cluster_routing_allocation.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/incompatible_cluster_routing_allocation.test.ts @@ -91,8 +91,7 @@ async function updateRoutingAllocations( }); } -// FLAKY: https://github.com/elastic/kibana/issues/136990 -describe.skip('incompatible_cluster_routing_allocation', () => { +describe('incompatible_cluster_routing_allocation', () => { let client: ElasticsearchClient; let root: Root; diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 2b0180796b604..42878ca66e614 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -23,7 +23,8 @@ import { } from '@kbn/core-config-server-internal'; import { NodeService, nodeConfig } from '@kbn/core-node-server-internal'; import { AnalyticsService } from '@kbn/core-analytics-server-internal'; -import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; +import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-server'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { EnvironmentService, pidConfig } from '@kbn/core-environment-server-internal'; import { ExecutionContextService, @@ -402,7 +403,7 @@ export class Server { startTransaction?.end(); this.uptimePerStep.start = { start: startStartUptime, end: process.uptime() }; - analyticsStart.reportEvent(KIBANA_STARTED_EVENT, { uptime_per_step: this.uptimePerStep }); + this.reportKibanaStartedEvents(analyticsStart); return this.coreStart; } @@ -464,6 +465,11 @@ export class Server { } } + /** + * Register the legacy KIBANA_STARTED_EVENT. + * @param analyticsSetup The {@link AnalyticsServiceSetup} + * @private + */ private registerKibanaStartedEventType(analyticsSetup: AnalyticsServiceSetup) { analyticsSetup.registerEventType<{ uptime_per_step: UptimeSteps }>({ eventType: KIBANA_STARTED_EVENT, @@ -551,4 +557,33 @@ export class Server { }, }); } + + /** + * Reports the new and legacy KIBANA_STARTED_EVENT. + * @param analyticsStart The {@link AnalyticsServiceStart}. + * @private + */ + private reportKibanaStartedEvents(analyticsStart: AnalyticsServiceStart) { + // Report the legacy KIBANA_STARTED_EVENT. + analyticsStart.reportEvent(KIBANA_STARTED_EVENT, { uptime_per_step: this.uptimePerStep }); + + const ups = this.uptimePerStep; + + const toMs = (sec: number) => Math.round(sec * 1000); + // Report the metric-shaped KIBANA_STARTED_EVENT. + reportPerformanceMetricEvent(analyticsStart, { + eventName: KIBANA_STARTED_EVENT, + duration: toMs(ups.start!.end - ups.constructor!.start), + key1: 'time_to_constructor', + value1: toMs(ups.constructor!.start), + key2: 'constructor_time', + value2: toMs(ups.constructor!.end - ups.constructor!.start), + key3: 'preboot_time', + value3: toMs(ups.preboot!.end - ups.preboot!.start), + key4: 'setup_time', + value4: toMs(ups.setup!.end - ups.setup!.start), + key5: 'start_time', + value5: toMs(ups.start!.end - ups.start!.start), + }); + } } diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index 6361b6b618b11..6a7436284b2ea 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -27,6 +27,7 @@ it('build default and oss dist for current platform, without packages, by defaul expect(readCliArgs(['node', 'scripts/build'])).toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": false, "createDockerCloud": false, @@ -60,6 +61,7 @@ it('builds packages if --all-platforms is passed', () => { expect(readCliArgs(['node', 'scripts/build', '--all-platforms'])).toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": true, "createDockerCloud": true, @@ -93,6 +95,7 @@ it('limits packages if --rpm passed with --all-platforms', () => { expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--rpm'])).toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": false, "createDockerCloud": false, @@ -126,6 +129,7 @@ it('limits packages if --deb passed with --all-platforms', () => { expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--deb'])).toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": true, "createDockerCloud": false, @@ -160,6 +164,7 @@ it('limits packages if --docker passed with --all-platforms', () => { .toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": false, "createDockerCloud": true, @@ -201,6 +206,7 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform ).toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": false, "createDockerCloud": true, @@ -235,6 +241,7 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => .toMatchInlineSnapshot(` Object { "buildOptions": Object { + "buildCanvasShareableRuntime": true, "createArchives": true, "createDebPackage": true, "createDockerCloud": true, diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index 42274cda867a7..c3d1a19f82a3e 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -20,6 +20,7 @@ export function readCliArgs(argv: string[]) { 'skip-generic-folders', 'skip-platform-folders', 'skip-os-packages', + 'skip-canvas-shareable-runtime', 'rpm', 'deb', 'docker-context-use-local-artifact', @@ -136,6 +137,7 @@ export function readCliArgs(argv: string[]) { createDockerContexts: !Boolean(flags['skip-docker-contexts']), targetAllPlatforms: Boolean(flags['all-platforms']), eprRegistry: flags['epr-registry'], + buildCanvasShareableRuntime: !Boolean(flags['skip-canvas-shareable-runtime']), }; return { diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index 893985926bd78..a9a7a5c571a7c 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -20,6 +20,7 @@ export interface BuildOptions { downloadFreshNode: boolean; downloadCloudDependencies: boolean; initialize: boolean; + buildCanvasShareableRuntime: boolean; createGenericFolders: boolean; createPlatformFolders: boolean; createArchives: boolean; @@ -74,7 +75,9 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreateEmptyDirsAndFiles); await run(Tasks.CreateReadme); await run(Tasks.BuildBazelPackages); - await run(Tasks.BuildXpack); + if (options.buildCanvasShareableRuntime) { + await run(Tasks.BuildCanvasShareableRuntime); + } await run(Tasks.BuildKibanaPlatformPlugins); await run(Tasks.TranspileBabel); await run(Tasks.CreatePackageJson); diff --git a/src/dev/build/cli.ts b/src/dev/build/cli.ts index 5acdc4b37423f..b7c3c32d496fb 100644 --- a/src/dev/build/cli.ts +++ b/src/dev/build/cli.ts @@ -42,6 +42,7 @@ if (showHelp) { --docker-context-use-local-artifact {dim Use a local artifact when building the Docker context} --docker-cross-compile {dim Produce arm64 and amd64 Docker images} --docker-contexts {dim Only build the Docker build contexts} + --skip-canvas-shareable-runtime {dim Don't build the Canvas shareable runtime} --skip-docker-ubi {dim Don't build the docker ubi image} --skip-docker-ubuntu {dim Don't build the docker ubuntu image} --release {dim Produce a release-ready distributable} diff --git a/src/dev/build/tasks/build_canvas_shareable_runtime.ts b/src/dev/build/tasks/build_canvas_shareable_runtime.ts new file mode 100644 index 0000000000000..6eb43b4dc7070 --- /dev/null +++ b/src/dev/build/tasks/build_canvas_shareable_runtime.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import del from 'del'; +import { Task, exec } from '../lib'; + +export const BuildCanvasShareableRuntime: Task = { + description: 'Build Canvas shareable runtime', + + async run(config, log, build) { + await del(config.resolveFromRepo('x-pack/plugins/canvas/shareable_runtime/build')); + + await exec(log, process.execPath, ['plugins/canvas/scripts/shareable_runtime'], { + cwd: config.resolveFromRepo('x-pack'), + level: 'info', + }); + }, +}; diff --git a/src/dev/build/tasks/build_packages_task.ts b/src/dev/build/tasks/build_packages_task.ts index bae1807f1f4c3..607e0ac0f08ba 100644 --- a/src/dev/build/tasks/build_packages_task.ts +++ b/src/dev/build/tasks/build_packages_task.ts @@ -6,13 +6,12 @@ * Side Public License, v 1. */ -import cpy from 'cpy'; import Path from 'path'; import { discoverBazelPackages } from '@kbn/bazel-packages'; import { runBazel } from '@kbn/bazel-runner'; -import { Task, scanCopy, write, exec } from '../lib'; +import { Task, scanCopy, write } from '../lib'; export const BuildBazelPackages: Task = { description: 'Building distributable versions of Bazel packages', @@ -40,20 +39,3 @@ export const BuildBazelPackages: Task = { } }, }; - -export const BuildXpack: Task = { - description: 'Building distributable versions of x-pack', - async run(config, log, build) { - log.info('running x-pack build task'); - await exec(log, 'yarn', ['build'], { - level: 'debug', - cwd: config.resolveFromRepo('x-pack'), - }); - - log.info('copying built x-pack into build dir'); - await cpy('**/{.,}*', build.resolvePath('x-pack'), { - cwd: config.resolveFromRepo('x-pack/build/plugin/kibana/x-pack'), - parents: true, - }); - }, -}; diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index a970ae0e456eb..4140b142d177f 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -19,35 +19,49 @@ export const CopySource: Task = { select: [ 'yarn.lock', '.npmrc', + 'kibana.d.ts', + 'config/kibana.yml', + 'config/node.options', + 'typings/**', + 'tsconfig*.json', + '.i18nrc.json', 'src/**', - '!src/**/*.{test,test.mocks,mock}.{js,ts,tsx}', - '!src/**/mocks.ts', // special file who imports .mock files - '!src/**/{target,tests,__jest__,test_data,__tests__,__snapshots__,__mocks__,integration_tests,__fixtures__}/**', - '!src/core/server/core_app/assets/favicons/favicon.distribution.ico', - '!src/core/server/core_app/assets/favicons/favicon.distribution.png', - '!src/core/server/core_app/assets/favicons/favicon.distribution.svg', - '!src/test_utils/**', - '!src/fixtures/**', + + 'x-pack/plugins/**', + 'x-pack/.i18nrc.json', + 'x-pack/package.json', + + '!{src,x-pack}/**/*.{test,test.mocks,mock,mocks}.*', + '!{src,x-pack}/**/target/**', + '!{src,x-pack}/**/{__stories__,storybook,.storybook}/**', + '!{src,x-pack}/**/{test,tests,test_resources,test_data,__tests__,manual_tests,__jest__,__snapshots__,__mocks__,mock_responses,mocks,fixtures,__fixtures__,cypress,integration_tests}/**', + + '!src/dev/**', + '!src/**/mocks.{js,ts}', '!src/cli/repl/**', '!src/cli*/dev.js', + '!src/plugins/telemetry/schema/**', + '!src/core/server/core_app/assets/favicons/favicon.distribution.{ico,png,svg}', '!src/functional_test_runner/**', - '!src/dev/**', + '!src/setup_node_env/index.js', + + '!x-pack/plugins/**/{ftr_e2e,e2e}/**', + '!x-pack/plugins/**/scripts/**', + '!x-pack/plugins/telemetry_collection_xpack/schema/**', + '!**/jest.config.js', + '!**/jest.config.dev.js', '!**/jest.integration.config.js', - '!**/mocks.js', - '!**/test_utils.js', - '!**/test_helpers.js', - '!**/*.{md,mdx,asciidoc}', - '!src/plugins/telemetry/schema/**', // Skip telemetry schemas - // this is the dev-only entry - '!src/setup_node_env/index.js', - '!**/public/**/*.{js,ts,tsx,json,scss}', - 'typings/**', - 'config/kibana.yml', - 'config/node.options', - 'tsconfig*.json', - '.i18nrc.json', - 'kibana.d.ts', + '!**/jest_setup.{js,ts}', + '!**/*.{story,stories}.{js,ts}', + '!**/test_mocks.ts', + '!**/*.{sh,md,mdx,asciidoc}', + '!**/*.console', + '!**/*.scss', + '!**/*.docnav.json', + '!**/{dev_docs,docs}/**', + '!**/public/**/*.{js,ts,tsx,json}', + // explicitly ignore all bazel package locations, even if they're not selected by previous patterns ...(await discoverBazelPackages()).map((pkg) => `!${pkg.normalizedRepoRelativeDir}/**`), ], diff --git a/src/dev/build/tasks/index.ts b/src/dev/build/tasks/index.ts index 94e6d107ff8da..22bf499ec24ba 100644 --- a/src/dev/build/tasks/index.ts +++ b/src/dev/build/tasks/index.ts @@ -7,6 +7,7 @@ */ export * from './bin'; +export * from './build_canvas_shareable_runtime'; export * from './build_kibana_example_plugins'; export * from './build_kibana_platform_plugins'; export * from './build_packages_task'; diff --git a/src/dev/build/tasks/nodejs/clean_node_builds_task.ts b/src/dev/build/tasks/nodejs/clean_node_builds_task.ts index 42d2cedc51e09..350395f94c355 100644 --- a/src/dev/build/tasks/nodejs/clean_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/clean_node_builds_task.ts @@ -18,6 +18,7 @@ export const CleanNodeBuilds: Task = { build.resolvePathForPlatform(platform, 'node/lib/node_modules'), build.resolvePathForPlatform(platform, 'node/bin/npm'), build.resolvePathForPlatform(platform, 'node/bin/npx'), + build.resolvePathForPlatform(platform, 'node/bin/corepack'), ], log ); 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 ed32735800bc6..232b522838b8e 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 @@ -27,6 +27,7 @@ import { getValueFromAccessor, getSubtypeByGaugeType, getGoalConfig, + computeMinMax, } from './utils'; import './index.scss'; import { GaugeCentralMajorMode } from '../../common/types'; @@ -119,27 +120,6 @@ function getTitle( return major || fallbackTitle || ''; } -const calculateRealRangeValueMin = ( - relativeRangeValue: number, - { min, max }: { min: number; max: number } -) => { - if (isFinite(relativeRangeValue)) { - return relativeRangeValue * ((max - min) / 100); - } - return min; -}; - -const calculateRealRangeValueMax = ( - relativeRangeValue: number, - { min, max }: { min: number; max: number } -) => { - if (isFinite(relativeRangeValue)) { - return relativeRangeValue * ((max - min) / 100); - } - - return max; -}; - const getPreviousSectionValue = (value: number, bands: number[]) => { // bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band. // So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section. @@ -176,24 +156,13 @@ export const GaugeComponent: FC = memo( bands: number[], percentageMode?: boolean ) => { - const { rangeMin, rangeMax, range }: CustomPaletteState = paletteConfig.params!; - const minRealValue = bands[0]; - const maxRealValue = bands[bands.length - 1]; - let min = rangeMin; - let max = rangeMax; - let stops = paletteConfig.params?.stops ?? []; if (percentageMode) { stops = bands.map((v) => v * 100); } - if (range === 'percent') { - const minMax = { min: minRealValue, max: maxRealValue }; - - min = calculateRealRangeValueMin(min, minMax); - max = calculateRealRangeValueMax(max, minMax); - } + const { min, max } = computeMinMax(paletteConfig, bands); return paletteService .get(paletteConfig?.name ?? 'custom') diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.test.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.test.ts new file mode 100644 index 0000000000000..825f74d3b68cb --- /dev/null +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { PaletteOutput } from '@kbn/coloring'; +import type { CustomPaletteState } from '@kbn/charts-plugin/public'; +import { computeMinMax } from './helpers'; + +describe('computeMinMax', () => { + it('returns the correct min max for percent palette', () => { + const palette = { + type: 'palette' as const, + name: 'custom', + params: { + colors: ['#aaa', '#bbb', '#ccc'], + gradient: false, + stops: [20, 60, 80], + range: 'percent', + rangeMin: 0, + rangeMax: 100, + }, + } as PaletteOutput; + const bands = [0, 2, 6, 8, 10]; + expect(computeMinMax(palette, bands)).toEqual({ min: 0, max: 10 }); + }); + + it('returns the correct min max for percent palette and Infinite bounds', () => { + const palette = { + type: 'palette' as const, + name: 'custom', + params: { + colors: ['#aaa', '#bbb', '#ccc'], + gradient: false, + stops: [], + range: 'percent', + rangeMin: -Infinity, + rangeMax: Infinity, + }, + } as PaletteOutput; + const bands = [0, 2, 6, 8, 10]; + expect(computeMinMax(palette, bands)).toEqual({ min: 0, max: 10 }); + }); + + it('returns the correct min max for number palette', () => { + const palette = { + type: 'palette' as const, + name: 'custom', + params: { + colors: ['#aaa', '#bbb', '#ccc'], + gradient: false, + stops: [0, 6, 10], + range: 'number', + rangeMin: 0, + rangeMax: 20, + }, + } as PaletteOutput; + const bands = [0, 2, 6, 8, 10]; + expect(computeMinMax(palette, bands)).toEqual({ min: 0, max: 20 }); + }); + + it('returns the correct min max for number palette and Infinite bounds', () => { + const palette = { + type: 'palette' as const, + name: 'custom', + params: { + colors: ['#aaa', '#bbb', '#ccc'], + gradient: false, + stops: [], + range: 'number', + rangeMin: -Infinity, + rangeMax: Infinity, + }, + } as PaletteOutput; + const bands = [0, 2, 6, 8, 10]; + expect(computeMinMax(palette, bands)).toEqual({ min: 0, max: 10 }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.ts new file mode 100644 index 0000000000000..59ebc6b9a1b61 --- /dev/null +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils/helpers.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { PaletteOutput } from '@kbn/coloring'; +import type { CustomPaletteState } from '@kbn/charts-plugin/public'; + +const calculateRealRangeValueMin = ( + relativeRangeValue: number, + { min, max }: { min: number; max: number } +) => { + if (isFinite(relativeRangeValue)) { + return relativeRangeValue * ((max - min) / 100); + } + return min; +}; + +const calculateRealRangeValueMax = ( + relativeRangeValue: number, + { min, max }: { min: number; max: number } +) => { + if (isFinite(relativeRangeValue)) { + return relativeRangeValue * ((max - min) / 100); + } + + return max; +}; + +export const computeMinMax = ( + paletteConfig: PaletteOutput, + bands: number[] +) => { + const { rangeMin, rangeMax, range }: CustomPaletteState = paletteConfig.params!; + const minRealValue = bands[0]; + const maxRealValue = bands[bands.length - 1]; + let min = rangeMin; + let max = rangeMax; + + if (range === 'percent') { + const minMax = { min: minRealValue, max: maxRealValue }; + + min = calculateRealRangeValueMin(min, minMax); + max = calculateRealRangeValueMax(max, minMax); + } + + if (range === 'number') { + min = isFinite(min) ? min : minRealValue; + max = isFinite(max) ? max : maxRealValue; + } + + return { + min, + max, + }; +}; diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils/index.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils/index.ts index 8892863d625d7..87f6a520ff9eb 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/utils/index.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils/index.ts @@ -10,3 +10,5 @@ export * from './accessors'; export * from './icons'; export * from './gauge_types'; export * from './goal_config'; + +export { computeMinMax } from './helpers'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 45d6eb42a9693..5def8a696df2f 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -229,7 +229,10 @@ export class LegacyCoreEditor implements CoreEditor { } detachCompleter() { - return (this.editor as unknown as { completer: { detach(): void } }).completer.detach(); + // In some situations we need to detach the autocomplete suggestions element manually, + // such as when navigating away from Console when the suggestions list is open. + const completer = (this.editor as unknown as { completer: { detach(): void } }).completer; + return completer?.detach(); } private forceRetokenize() { diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index aa2588c1b3b54..9f59e8637ba1c 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -12,6 +12,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import uuid from 'uuid'; import { CoreStart, IUiSettingsClient, KibanaExecutionContext } from '@kbn/core/public'; import { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { ControlGroupContainer } from '@kbn/controls-plugin/public'; import { Filter, TimeRange } from '@kbn/es-query'; @@ -31,7 +32,7 @@ import { } from '../../services/embeddable'; import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; import { createPanelState } from './panel'; -import { DashboardLoadedEvent, DashboardPanelState } from './types'; +import { DashboardPanelState } from './types'; import { DashboardViewport } from './viewport/dashboard_viewport'; import { KibanaContextProvider, @@ -40,6 +41,7 @@ import { KibanaThemeProvider, } from '../../services/kibana_react'; import { PLACEHOLDER_EMBEDDABLE } from './placeholder'; +import { DASHBOARD_LOADED_EVENT } from '../../events'; import { DashboardAppCapabilities, DashboardContainerInput } from '../../types'; import { PresentationUtilPluginStart } from '../../services/presentation_util'; import type { ScreenshotModePluginStart } from '../../services/screenshot_mode'; @@ -66,6 +68,13 @@ export interface DashboardContainerServices { analytics?: CoreStart['analytics']; } +export interface DashboardLoadedInfo { + timeToData: number; + timeToDone: number; + numOfPanels: number; + status: string; +} + interface IndexSignature { [key: string]: unknown; } @@ -155,10 +164,17 @@ export class DashboardContainer extends Container void; + onDataLoaded?: (data: DashboardLoadedInfo) => void; } interface State { @@ -270,7 +274,7 @@ class DashboardGridUi extends React.Component { doneCount++; if (doneCount === panelsInOrder.length) { const doneTime = performance.now(); - const data: DashboardLoadedEvent = { + const data: DashboardLoadedInfo = { timeToData: (lastTimeToData || doneTime) - loadStartTime, timeToDone: doneTime - loadStartTime, numOfPanels: panelsInOrder.length, diff --git a/src/plugins/dashboard/public/application/embeddable/types.ts b/src/plugins/dashboard/public/application/embeddable/types.ts index f8e37b07d721c..b64fe70f9eb9c 100644 --- a/src/plugins/dashboard/public/application/embeddable/types.ts +++ b/src/plugins/dashboard/public/application/embeddable/types.ts @@ -10,11 +10,6 @@ export * from '../../../common/types'; export type DashboardLoadedEventStatus = 'done' | 'error'; -export interface DashboardLoadedEvent { - // Time from start to when data is loaded - timeToData: number; - // Time from start until render or error - timeToDone: number; - numOfPanels: number; +export interface DashboardLoadedEventMeta { status: DashboardLoadedEventStatus; } diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx index 318db746fbe42..ebb91ba57fd46 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -14,18 +14,21 @@ import { LazyControlsCallout, } from '@kbn/controls-plugin/public'; import { ViewMode } from '../../../services/embeddable'; -import { DashboardContainer, DashboardReactContextValue } from '../dashboard_container'; +import { + DashboardContainer, + DashboardReactContextValue, + DashboardLoadedInfo, +} from '../dashboard_container'; import { DashboardGrid } from '../grid'; import { context } from '../../../services/kibana_react'; import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; import { withSuspense } from '../../../services/presentation_util'; -import { DashboardLoadedEvent } from '../types'; export interface DashboardViewportProps { container: DashboardContainer; controlGroup?: ControlGroupContainer; controlsEnabled?: boolean; - onDataLoaded?: (data: DashboardLoadedEvent) => void; + onDataLoaded?: (data: DashboardLoadedInfo) => void; } interface State { diff --git a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap index d4513837efa11..006ddb9595a97 100644 --- a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -60,7 +60,7 @@ exports[`after fetch When given a title that matches multiple dashboards, filter Create your first dashboard @@ -208,7 +209,7 @@ exports[`after fetch initialFilter 1`] = ` Create your first dashboard @@ -355,7 +357,7 @@ exports[`after fetch renders all table rows 1`] = ` Create your first dashboard @@ -502,7 +505,7 @@ exports[`after fetch renders call to action when no dashboards exist 1`] = ` Create your first dashboard @@ -673,7 +677,7 @@ exports[`after fetch renders call to action with continue when no dashboards exi Dashboard in progress @@ -807,7 +812,7 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` Create your first dashboard @@ -957,6 +963,7 @@ exports[`after fetch showWriteControls 1`] = ` iconType="glasses" title={

No dashboards to view diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index 80005b3bebdd3..17433562bcdab 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -151,7 +151,11 @@ export const DashboardListing = ({ return ( {noItemsStrings.getReadonlyTitle()}

} + title={ +

+ {noItemsStrings.getReadonlyTitle()} +

+ } body={

{noItemsStrings.getReadonlyBody()}

} /> ); @@ -183,7 +187,7 @@ export const DashboardListing = ({ iconType="pencil" color="primary" onClick={() => redirectTo({ destination: 'dashboard' })} - data-test-subj="createDashboardPromptButton" + data-test-subj="newItemButton" aria-label={dashboardUnsavedListingStrings.getEditAriaLabel(getNewDashboardTitle())} > {dashboardUnsavedListingStrings.getEditTitle()} @@ -191,12 +195,7 @@ export const DashboardListing = ({
) : ( - + {noItemsStrings.getCreateNewDashboardText()} ); @@ -205,7 +204,7 @@ export const DashboardListing = ({ +

{isEditingFirstDashboard ? noItemsStrings.getReadEditInProgressTitle() : noItemsStrings.getReadEditTitle()} diff --git a/src/plugins/dashboard/public/events.ts b/src/plugins/dashboard/public/events.ts new file mode 100644 index 0000000000000..aa28cb109abec --- /dev/null +++ b/src/plugins/dashboard/public/events.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const DASHBOARD_LOADED_EVENT = 'dashboard_loaded'; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index a9564ac0db49a..ba06709d69f7b 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -71,7 +71,6 @@ import { LibraryNotificationAction, CopyToDashboardAction, DashboardCapabilities, - DashboardLoadedEvent, } from './application'; import { DashboardAppLocatorDefinition, DashboardAppLocator } from './locator'; import { createSavedDashboardLoader } from './saved_dashboards'; @@ -139,30 +138,6 @@ export class DashboardPlugin private dashboardFeatureFlagConfig?: DashboardFeatureFlagConfig; private locator?: DashboardAppLocator; - private registerEvents(analytics: CoreSetup['analytics']) { - analytics.registerEventType({ - eventType: 'dashboard-data-loaded', - schema: { - timeToData: { - type: 'long', - _meta: { description: 'Time all embeddables took to load data' }, - }, - timeToDone: { - type: 'long', - _meta: { description: 'Time all embeddables took to load data' }, - }, - status: { - type: 'keyword', - _meta: { description: 'Error ok' }, - }, - numOfPanels: { - type: 'long', - _meta: { description: 'Number of panels loaded' }, - }, - }, - }); - } - public setup( core: CoreSetup, { @@ -312,8 +287,6 @@ export class DashboardPlugin }, }; - this.registerEvents(core.analytics); - core.application.register(app); urlForwarding.forwardApp( DashboardConstants.DASHBOARDS_ID, diff --git a/src/plugins/data/common/search/aggs/buckets/filter.test.ts b/src/plugins/data/common/search/aggs/buckets/filter.test.ts new file mode 100644 index 0000000000000..79089013e08b1 --- /dev/null +++ b/src/plugins/data/common/search/aggs/buckets/filter.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { AggConfigs } from '../agg_configs'; +import { mockAggTypesRegistry } from '../test_helpers'; +import { BUCKET_TYPES } from './bucket_agg_types'; +import moment from 'moment'; +import { SerializableRecord } from '@kbn/utility-types'; + +describe('Filter Agg', () => { + let aggConfigs: AggConfigs; + + const depMocks = { + getConfig: () => ({} as T), + calculateBounds: () => { + return { + max: moment('2022-05-05T00:00:00.000Z'), + min: undefined, + }; + }, + getFieldFormatsStart: jest.fn(), + }; + + function init(params?: SerializableRecord) { + const indexPattern = { + id: '1234', + title: 'logstash-*', + timeFieldName: 'timestamp', + fields: { + getByName: () => field, + filter: () => [field], + find: () => field, + }, + } as any; + + const field = { + name: 'A', + indexPattern, + }; + + aggConfigs = new AggConfigs( + indexPattern, + [ + { + id: BUCKET_TYPES.FILTER, + type: BUCKET_TYPES.FILTER, + schema: 'segment', + params, + }, + ], + { + typesRegistry: mockAggTypesRegistry(depMocks), + }, + jest.fn() + ); + aggConfigs.setTimeRange({ + from: '2022-05-01T00:00:00.000Z', + to: '2022-05-05T00:00:00.000Z', + }); + } + + it('should build filter agg with time window and filter and time shift', () => { + init({ + filter: { + language: 'kuery', + query: 'A: B', + }, + timeWindow: '5m', + timeShift: '1d', + }); + expect(aggConfigs.aggs[0].write(aggConfigs)).toMatchInlineSnapshot(` + Object { + "params": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "timestamp": Object { + "format": "strict_date_optional_time", + "gte": "2022-05-03T23:55:00.000Z", + "lte": "2022-05-04T00:00:00.000Z", + }, + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "A": "B", + }, + }, + ], + }, + }, + ], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + ], + }, + }, + } + `); + }); + + it('should build filter agg with only time window', () => { + init({ + timeWindow: '5m', + }); + expect(aggConfigs.aggs[0].write(aggConfigs)).toMatchInlineSnapshot(` + Object { + "params": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "timestamp": Object { + "format": "strict_date_optional_time", + "gte": "2022-05-04T23:55:00.000Z", + "lte": "2022-05-05T00:00:00.000Z", + }, + }, + }, + ], + }, + }, + } + `); + }); +}); diff --git a/src/plugins/data/common/search/aggs/buckets/filter.ts b/src/plugins/data/common/search/aggs/buckets/filter.ts index 66a702d06144b..0edba29c24094 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter.ts @@ -10,12 +10,15 @@ import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { buildEsQuery, Query } from '@kbn/es-query'; +import moment from 'moment'; import { GeoBoundingBox, QueryFilter, geoBoundingBoxToAst, queryToAst } from '../../expressions'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { aggFilterFnName } from './filter_fn'; import { BaseAggParams } from '../types'; import { getEsQueryConfig } from '../../../es_query'; +import { parseInterval } from '../utils'; +import { CalculateBoundsFn } from '.'; const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { defaultMessage: 'Filter', @@ -24,9 +27,16 @@ const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { export interface AggParamsFilter extends BaseAggParams { geo_bounding_box?: GeoBoundingBox; filter?: QueryFilter; + timeWindow?: string; } -export const getFilterBucketAgg = ({ getConfig }: { getConfig: (key: string) => any }) => +export const getFilterBucketAgg = ({ + getConfig, + calculateBounds, +}: { + getConfig: (key: string) => T; + calculateBounds: CalculateBoundsFn; +}) => new BucketAggType({ name: BUCKET_TYPES.FILTER, expressionName: aggFilterFnName, @@ -37,26 +47,73 @@ export const getFilterBucketAgg = ({ getConfig }: { getConfig: (key: st name: 'geo_bounding_box', toExpressionAst: geoBoundingBoxToAst, }, + { + name: 'timeShift', + type: 'string', + write: () => {}, + }, + { + name: 'timeWindow', + write: () => {}, + }, { name: 'filter', - write(aggConfig, output) { + write(aggConfig, output, aggConfigs) { const filter: Query = aggConfig.params.filter; + const timeWindow: string | undefined = aggConfig.params.timeWindow; const input = cloneDeep(filter); - if (!input) { + if (!input && !timeWindow) { return; } - const esQueryConfigs = getEsQueryConfig({ get: getConfig }); - const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], esQueryConfigs); - - if (!query) { - console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console + if (!aggConfigs) { return; } - output.params = query; + let query: object | undefined; + + if (input) { + const esQueryConfigs = getEsQueryConfig({ get: getConfig }); + query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], esQueryConfigs); + + if (!query) { + console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console + return; + } + } + + const timeRangeAnchor = aggConfigs.timeRange + ? moment(calculateBounds(aggConfigs.timeRange).max) + : undefined; + + output.params = + !timeWindow || !timeRangeAnchor || !aggConfig.getIndexPattern().timeFieldName + ? query + : { + bool: { + filter: [ + { + range: { + [aggConfig.getIndexPattern().timeFieldName!]: { + format: 'strict_date_optional_time', + gte: timeRangeAnchor + .clone() + .subtract(parseInterval(timeWindow)) + .subtract(aggConfig.getTimeShift()) + .toISOString(), + lte: timeRangeAnchor + .clone() + .subtract(aggConfig.getTimeShift()) + .toISOString(), + }, + }, + }, + query ? query : undefined, + ].filter(Boolean), + }, + }; }, toExpressionAst: queryToAst, }, diff --git a/src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts index f00c654fa7b27..ae97f95615e47 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts @@ -26,6 +26,8 @@ describe('agg_expression_functions', () => { "filter": undefined, "geo_bounding_box": undefined, "json": undefined, + "timeShift": undefined, + "timeWindow": undefined, }, "schema": undefined, "type": "filter", @@ -53,6 +55,8 @@ describe('agg_expression_functions', () => { "wkt": "BBOX (-74.1, -71.12, 40.73, 40.01)", }, "json": undefined, + "timeShift": undefined, + "timeWindow": undefined, }, "schema": undefined, "type": "filter", diff --git a/src/plugins/data/common/search/aggs/buckets/filter_fn.ts b/src/plugins/data/common/search/aggs/buckets/filter_fn.ts index 0390213f6c16c..01aa8ab630e73 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter_fn.ts @@ -71,6 +71,14 @@ export const aggFilter = (): FunctionDefinition => ({ 'Filter results based on a kql or lucene query. Do not use together with geo_bounding_box', }), }, + timeWindow: { + types: ['string'], + help: '', + }, + timeShift: { + types: ['string'], + help: '', + }, json: { types: ['string'], help: i18n.translate('data.search.aggs.buckets.filter.json.help', { diff --git a/src/plugins/data/common/search/aggs/metrics/filtered_metric.ts b/src/plugins/data/common/search/aggs/metrics/filtered_metric.ts index beddf6b623110..22ae42b9130a5 100644 --- a/src/plugins/data/common/search/aggs/metrics/filtered_metric.ts +++ b/src/plugins/data/common/search/aggs/metrics/filtered_metric.ts @@ -46,6 +46,7 @@ export const getFilteredMetricAgg = ({ getConfig }: FiltersMetricAggDependencies hasNoDslParams: true, getSerializedFormat, createFilter: (agg, inputState) => { + if (!agg.params.customBucket.params.filter) return; const esQueryConfigs = getEsQueryConfig({ get: getConfig }); return buildQueryFilter( buildEsQuery( diff --git a/src/plugins/data_views/common/constants.ts b/src/plugins/data_views/common/constants.ts index b79eef45e7fc8..2c766115bac7c 100644 --- a/src/plugins/data_views/common/constants.ts +++ b/src/plugins/data_views/common/constants.ts @@ -22,15 +22,16 @@ export const RUNTIME_FIELD_TYPES = [ ] as const; /** - * Used to determine if the instance has some user created index patterns by filtering index patterns - * that are created and backed only by Fleet server data - * Should be revised after https://github.com/elastic/kibana/issues/82851 is fixed - * For more background see: https://github.com/elastic/kibana/issues/107020 + * Used to optimize on-boarding experience to determine if the instance has some user created data views or data indices/streams by filtering data sources + * that are created by default by elastic in ese. + * We should somehow prevent creating initial data for the users without their explicit action + * instead of relying on these hardcoded assets */ export const DEFAULT_ASSETS_TO_IGNORE = { - LOGS_INDEX_PATTERN: 'logs-*', - LOGS_DATA_STREAM_TO_IGNORE: 'logs-elastic_agent', // ignore ds created by Fleet server itself - ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE: 'logs-enterprise_search.api-default', // ignore ds created by enterprise search + DATA_STREAMS_TO_IGNORE: [ + 'logs-enterprise_search.api-default', // https://github.com/elastic/kibana/issues/134918 + `logs-enterprise_search.audit-default`, // https://github.com/elastic/kibana/issues/134918 + ], }; /** 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 8c98f7f18f0de..02c9278c6d88f 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -468,7 +468,7 @@ export class DataViewsService { * Checks if current user has a user created index pattern ignoring fleet's server default index patterns. */ async hasUserDataView(): Promise { - return this.apiClient.hasUserIndexPattern(); + return this.apiClient.hasUserDataView(); } /** diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index ee39678761023..25f3564aa1eb8 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -319,7 +319,7 @@ export interface GetFieldsOptions { export interface IDataViewsApiClient { getFieldsForWildcard: (options: GetFieldsOptions) => Promise; - hasUserIndexPattern: () => Promise; + hasUserDataView: () => Promise; } export type AggregationRestrictions = Record< diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.ts b/src/plugins/data_views/public/data_views/data_views_api_client.ts index b02894d1afb63..b2b8c169c3257 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.ts @@ -63,7 +63,7 @@ export class DataViewsApiClient implements IDataViewsApiClient { /** * Does a user created data view exist? */ - async hasUserIndexPattern(): Promise { + async hasUserDataView(): Promise { const response = await this._request<{ result: boolean }>( this._getUrl(['has_user_index_pattern']) ); diff --git a/src/plugins/data_views/public/services/has_data.test.ts b/src/plugins/data_views/public/services/has_data.test.ts index c13fffe9fc475..b2ccf3828af6b 100644 --- a/src/plugins/data_views/public/services/has_data.test.ts +++ b/src/plugins/data_views/public/services/has_data.test.ts @@ -32,11 +32,11 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasESData(); + const response = hasDataService.hasESData(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(true); + expect(await response).toBe(true); }); it('should return false for hasESData when no indices exist', async () => { @@ -54,11 +54,83 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasESData(); + const response = hasDataService.hasESData(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(false); + expect(await response).toBe(false); + }); + + it('should return false for hasESData when only automatically created sources exist', async () => { + const coreStart = coreMock.createStart(); + const http = coreStart.http; + + // Mock getIndices + const spy = jest.spyOn(http, 'get').mockImplementation((path: any) => + Promise.resolve({ + aliases: [], + data_streams: path.includes('*:*') + ? [] // return empty on remote cluster call + : [ + { + name: 'logs-enterprise_search.api-default', + timestamp_field: '@timestamp', + backing_indices: ['.ds-logs-enterprise_search.api-default-2022.03.07-000001'], + }, + ], + indices: [], + }) + ); + + const hasData = new HasData(); + const hasDataService = hasData.start(coreStart); + const response = hasDataService.hasESData(); + + expect(spy).toHaveBeenCalledTimes(1); + + expect(await response).toBe(false); + }); + + it('should hit search api in case resolve api throws', async () => { + const coreStart = coreMock.createStart(); + const http = coreStart.http; + + const spyGetIndices = jest + .spyOn(http, 'get') + .mockImplementation(() => Promise.reject(new Error('oops'))); + + const spySearch = jest + .spyOn(http, 'post') + .mockImplementation(() => Promise.resolve({ total: 10 })); + const hasData = new HasData(); + const hasDataService = hasData.start(coreStart); + const response = await hasDataService.hasESData(); + + expect(response).toBe(true); + + expect(spyGetIndices).toHaveBeenCalledTimes(1); + expect(spySearch).toHaveBeenCalledTimes(1); + }); + + it('should return false in case search api throws', async () => { + const coreStart = coreMock.createStart(); + const http = coreStart.http; + + const spyGetIndices = jest + .spyOn(http, 'get') + .mockImplementation(() => Promise.reject(new Error('oops'))); + + const spySearch = jest + .spyOn(http, 'post') + .mockImplementation(() => Promise.reject(new Error('oops'))); + const hasData = new HasData(); + const hasDataService = hasData.start(coreStart); + const response = await hasDataService.hasESData(); + + expect(response).toBe(true); + + expect(spyGetIndices).toHaveBeenCalledTimes(1); + expect(spySearch).toHaveBeenCalledTimes(1); }); it('should return true for hasDataView when server returns true', async () => { @@ -75,11 +147,11 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasDataView(); + const response = hasDataService.hasDataView(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(true); + expect(await response).toBe(true); }); it('should return false for hasDataView when server returns false', async () => { @@ -96,11 +168,27 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasDataView(); + const response = hasDataService.hasDataView(); + + expect(spy).toHaveBeenCalledTimes(1); + + expect(await response).toBe(false); + }); + + it('should return true for hasDataView when server throws an error', async () => { + const coreStart = coreMock.createStart(); + const http = coreStart.http; + + // Mock getIndices + const spy = jest.spyOn(http, 'get').mockImplementation(() => Promise.reject(new Error('Oops'))); + + const hasData = new HasData(); + const hasDataService = hasData.start(coreStart); + const response = hasDataService.hasDataView(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(false); + expect(await response).toBe(true); }); it('should return false for hasUserDataView when server returns false', async () => { @@ -117,11 +205,11 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasUserDataView(); + const response = hasDataService.hasUserDataView(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(false); + expect(await response).toBe(false); }); it('should return true for hasUserDataView when server returns true', async () => { @@ -138,10 +226,26 @@ describe('when calling hasData service', () => { const hasData = new HasData(); const hasDataService = hasData.start(coreStart); - const reponse = hasDataService.hasUserDataView(); + const response = hasDataService.hasUserDataView(); + + expect(spy).toHaveBeenCalledTimes(1); + + expect(await response).toBe(true); + }); + + it('should return true for hasUserDataView when server throws an error', async () => { + const coreStart = coreMock.createStart(); + const http = coreStart.http; + + // Mock getIndices + const spy = jest.spyOn(http, 'get').mockImplementation(() => Promise.reject(new Error('Oops'))); + + const hasData = new HasData(); + const hasDataService = hasData.start(coreStart); + const response = hasDataService.hasUserDataView(); expect(spy).toHaveBeenCalledTimes(1); - expect(await reponse).toBe(true); + expect(await response).toBe(true); }); }); diff --git a/src/plugins/data_views/public/services/has_data.ts b/src/plugins/data_views/public/services/has_data.ts index 45f44e04e23a8..64a561620c82e 100644 --- a/src/plugins/data_views/public/services/has_data.ts +++ b/src/plugins/data_views/public/services/has_data.ts @@ -14,14 +14,12 @@ import { IndicesResponse, IndicesResponseModified } from '../types'; export class HasData { private removeAliases = (source: IndicesResponseModified): boolean => !source.item.indices; - private isUserDataIndex = (source: IndicesResponseModified): boolean => { + private isUserDataSource = (source: IndicesResponseModified): boolean => { // filter out indices that start with `.` if (source.name.startsWith('.')) return false; // filter out sources from DEFAULT_ASSETS_TO_IGNORE - if (source.name === DEFAULT_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE) return false; - if (source.name === DEFAULT_ASSETS_TO_IGNORE.ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE) - return false; + if (DEFAULT_ASSETS_TO_IGNORE.DATA_STREAMS_TO_IGNORE.includes(source.name)) return false; // filter out data streams that we know are created automatically during on-boarding return true; }; @@ -44,14 +42,14 @@ export class HasData { * Check to see if a data view exists */ hasDataView: async (): Promise => { - const dataViewsCheck = await this.findDataViews(http); + const dataViewsCheck = await this.hasDataViews(http); return dataViewsCheck; }, /** * Check to see if user created data views exist */ hasUserDataView: async (): Promise => { - const userDataViewsCheck = await this.findUserDataViews(http); + const userDataViewsCheck = await this.hasUserDataViews(http); return userDataViewsCheck; }, }; @@ -106,7 +104,11 @@ export class HasData { .then((resp) => { return !!(resp && resp.total >= 0); }) - .catch(() => false); + .catch((e) => { + // eslint-disable-next-line no-console + console.warn(`getIndicesViaSearch failed with error, assuming there is data`, e); + return true; + }); private getIndices = async ({ http, @@ -136,7 +138,7 @@ export class HasData { showAllIndices: false, }) .then((dataSources: IndicesResponseModified[]) => { - return dataSources.some(this.isUserDataIndex); + return dataSources.some(this.isUserDataSource); }) .catch(() => this.getIndicesViaSearch({ http, pattern: '*', showAllIndices: false })); @@ -156,22 +158,33 @@ export class HasData { private getHasDataViews = async ({ http }: { http: HttpStart }): Promise => http.get(`/internal/data_views/has_data_views`); - private findDataViews = (http: HttpStart): Promise => { + private hasDataViews = (http: HttpStart): Promise => { return this.getHasDataViews({ http }) .then((response: HasDataViewsResponse) => { const { hasDataView } = response; return hasDataView; }) - .catch(() => false); + .catch((e) => { + // eslint-disable-next-line no-console + console.warn(`hasDataViews failed with error, assuming there are data views`, e); + return true; + }); }; - private findUserDataViews = (http: HttpStart): Promise => { + private hasUserDataViews = (http: HttpStart): Promise => { return this.getHasDataViews({ http }) .then((response: HasDataViewsResponse) => { const { hasUserDataView } = response; return hasUserDataView; }) - .catch(() => false); + .catch((e) => { + // eslint-disable-next-line no-console + console.warn( + `hasUserDataViews failed with error, assuming there are user-created data views`, + e + ); + return true; + }); }; } diff --git a/src/plugins/data_views/server/has_user_data_view.test.ts b/src/plugins/data_views/server/has_user_data_view.test.ts new file mode 100644 index 0000000000000..a5f3dbb1ced24 --- /dev/null +++ b/src/plugins/data_views/server/has_user_data_view.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { hasUserDataView } from './has_user_data_view'; +import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; + +describe('hasUserDataView', () => { + const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; + const soClient = savedObjectsClientMock.create(); + + beforeEach(() => jest.resetAllMocks()); + + it('returns false when there are no data views', async () => { + soClient.find.mockResolvedValue({ + page: 1, + per_page: 100, + total: 0, + saved_objects: [], + }); + expect(await hasUserDataView({ esClient, soClient })).toEqual(false); + }); + + it('returns true when there are data views', async () => { + soClient.find.mockResolvedValue({ + page: 1, + per_page: 100, + total: 1, + saved_objects: [ + { + id: '1', + references: [], + type: 'index-pattern', + score: 99, + attributes: { title: 'my-pattern-*' }, + }, + ], + }); + expect(await hasUserDataView({ esClient, soClient })).toEqual(true); + }); + + it('can shortcut using api internally', async () => { + const dataViewsFindResponse = { + page: 1, + per_page: 100, + total: 1, + saved_objects: [ + { + id: '1', + references: [], + type: 'index-pattern', + score: 99, + attributes: { title: 'my-pattern-*' }, + }, + ], + }; + expect(await hasUserDataView({ esClient, soClient }, dataViewsFindResponse)).toEqual(true); + expect(soClient.find).not.toBeCalled(); + }); +}); diff --git a/src/plugins/data_views/server/has_user_index_pattern.ts b/src/plugins/data_views/server/has_user_data_view.ts similarity index 52% rename from src/plugins/data_views/server/has_user_index_pattern.ts rename to src/plugins/data_views/server/has_user_data_view.ts index fd0c856c285c7..e822b7cb35a49 100644 --- a/src/plugins/data_views/server/has_user_index_pattern.ts +++ b/src/plugins/data_views/server/has_user_data_view.ts @@ -12,14 +12,13 @@ import { SavedObjectsFindResponse, } from '@kbn/core/server'; import { DataViewSavedObjectAttrs } from '../common/data_views'; -import { DEFAULT_ASSETS_TO_IGNORE } from '../common/constants'; interface Deps { esClient: ElasticsearchClient; soClient: SavedObjectsClientContract; } -export const getIndexPattern = async ({ +export const getDataViews = async ({ soClient, }: Deps): Promise> => soClient.find({ @@ -30,31 +29,27 @@ export const getIndexPattern = async ({ perPage: 100, }); -export const hasUserIndexPattern = async ( +/** + * Checks if user has access to any data view, + * excluding those that are automatically created by ese (hardcoded) + * @param esClient + * @param soClient + * @param dataViews + */ +export const hasUserDataView = async ( { esClient, soClient }: Deps, - indexPatterns?: SavedObjectsFindResponse + dataViews?: SavedObjectsFindResponse ): Promise => { - if (!indexPatterns) { - indexPatterns = await getIndexPattern({ esClient, soClient }); + if (!dataViews) { + dataViews = await getDataViews({ esClient, soClient }); } - if (indexPatterns.total > 0) { - return true; - } + if (dataViews.total === 0) { + return false; + } else { + // filter here data views that we know are not created by user during on-boarding for smoother on-boarding experience + // currently there is no such data views, - const resolveResponse = await esClient.indices.resolveIndex({ - name: `${DEFAULT_ASSETS_TO_IGNORE.LOGS_INDEX_PATTERN}`, - }); - - if (resolveResponse) { - if (resolveResponse.indices.length > 0) return true; - - const hasAnyNonDefaultFleetDataStreams = resolveResponse.data_streams.some( - (ds) => - ds.name !== DEFAULT_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE && - ds.name !== DEFAULT_ASSETS_TO_IGNORE.ENT_SEARCH_LOGS_DATA_STREAM_TO_IGNORE - ); - if (hasAnyNonDefaultFleetDataStreams) return true; + return true; } - return false; }; diff --git a/src/plugins/data_views/server/has_user_index_pattern.test.ts b/src/plugins/data_views/server/has_user_index_pattern.test.ts deleted file mode 100644 index 3560d9bfec8d2..0000000000000 --- a/src/plugins/data_views/server/has_user_index_pattern.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { hasUserIndexPattern } from './has_user_index_pattern'; -import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; - -describe('hasUserIndexPattern', () => { - const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; - const soClient = savedObjectsClientMock.create(); - - beforeEach(() => jest.resetAllMocks()); - - it('returns false when there are no index patterns', async () => { - soClient.find.mockResolvedValue({ - page: 1, - per_page: 100, - total: 0, - saved_objects: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); - }); - - describe('when no index patterns exist', () => { - beforeEach(() => { - soClient.find.mockResolvedValue({ - page: 1, - per_page: 100, - total: 0, - saved_objects: [], - }); - }); - - it('calls indices.resolveIndex for the index patterns', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [], - aliases: [], - }); - await hasUserIndexPattern({ esClient, soClient }); - expect(esClient.indices.resolveIndex).toHaveBeenCalledWith({ - name: 'logs-*', - }); - }); - - it('returns false if no data_streams exists', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); - }); - - it('returns true if any index exists', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [{ name: 'logs', attributes: [] }], - data_streams: [], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(true); - }); - - it('returns false if only logs-elastic_agent data stream exists', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [ - { - name: 'logs-elastic_agent', - timestamp_field: '@timestamp', - backing_indices: ['.ds-logs-elastic_agent'], - }, - ], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); - }); - - it('returns false if only logs-enterprise_search.api-default data stream exists', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [ - { - name: 'logs-enterprise_search.api-default', - timestamp_field: '@timestamp', - backing_indices: ['.ds-logs-enterprise_search.api-default-2022.03.07-000001'], - }, - ], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); - }); - - it('returns true if any other data stream exists', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [ - { - name: 'other', - timestamp_field: '@timestamp', - backing_indices: ['.ds-other'], - }, - ], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(true); - }); - - it('returns true if any other data stream exists with logs-enterprise_search.api-default and logs-elastic_agent', async () => { - esClient.indices.resolveIndex.mockResponse({ - indices: [], - data_streams: [ - { - name: 'other', - timestamp_field: '@timestamp', - backing_indices: ['.ds-other'], - }, - { - name: 'logs-enterprise_search.api-default', - timestamp_field: '@timestamp', - backing_indices: ['.ds-logs-enterprise_search.api-default-2022.03.07-000001'], - }, - { - name: 'logs-elastic_agent', - timestamp_field: '@timestamp', - backing_indices: ['.ds-logs-elastic_agent'], - }, - ], - aliases: [], - }); - expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(true); - }); - }); -}); diff --git a/src/plugins/data_views/server/index_patterns_api_client.ts b/src/plugins/data_views/server/index_patterns_api_client.ts index 0cdcb55a61667..787d3330b6285 100644 --- a/src/plugins/data_views/server/index_patterns_api_client.ts +++ b/src/plugins/data_views/server/index_patterns_api_client.ts @@ -10,7 +10,7 @@ import { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/serve import { GetFieldsOptions, IDataViewsApiClient } from '../common/types'; import { DataViewMissingIndices } from '../common/lib'; import { IndexPatternsFetcher } from './fetcher'; -import { hasUserIndexPattern } from './has_user_index_pattern'; +import { hasUserDataView } from './has_user_data_view'; export class IndexPatternsApiServer implements IDataViewsApiClient { esClient: ElasticsearchClient; @@ -52,8 +52,8 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { /** * Is there a user created data view? */ - async hasUserIndexPattern() { - return hasUserIndexPattern({ + async hasUserDataView() { + return hasUserDataView({ esClient: this.esClient, soClient: this.savedObjectsClient, }); diff --git a/src/plugins/data_views/server/routes/has_data_views.ts b/src/plugins/data_views/server/routes/has_data_views.ts index c4982311ce156..97295939aec63 100644 --- a/src/plugins/data_views/server/routes/has_data_views.ts +++ b/src/plugins/data_views/server/routes/has_data_views.ts @@ -7,7 +7,7 @@ */ import { IRouter } from '@kbn/core/server'; -import { getIndexPattern, hasUserIndexPattern } from '../has_user_index_pattern'; +import { getDataViews, hasUserDataView } from '../has_user_data_view'; export const registerHasDataViewsRoute = (router: IRouter): void => { router.get( @@ -19,11 +19,11 @@ export const registerHasDataViewsRoute = (router: IRouter): void => { const core = await ctx.core; const savedObjectsClient = core.savedObjects.client; const elasticsearchClient = core.elasticsearch.client.asCurrentUser; - const dataViews = await getIndexPattern({ + const dataViews = await getDataViews({ esClient: elasticsearchClient, soClient: savedObjectsClient, }); - const checkDataPattern = await hasUserIndexPattern( + const hasUserDataViewResult = await hasUserDataView( { esClient: elasticsearchClient, soClient: savedObjectsClient, @@ -31,8 +31,8 @@ export const registerHasDataViewsRoute = (router: IRouter): void => { dataViews ); const response = { - hasDataView: !!dataViews.total, - hasUserDataView: !!checkDataPattern, + hasDataView: dataViews.total > 0, + hasUserDataView: hasUserDataViewResult, }; return res.ok({ body: response }); } diff --git a/src/plugins/discover/public/application/main/discover_main_route.test.tsx b/src/plugins/discover/public/application/main/discover_main_route.test.tsx new file mode 100644 index 0000000000000..6734864210ee6 --- /dev/null +++ b/src/plugins/discover/public/application/main/discover_main_route.test.tsx @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { waitFor } from '@testing-library/react'; +import { setHeaderActionMenuMounter } from '../../kibana_services'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { discoverServiceMock } from '../../__mocks__/services'; +import { DiscoverMainRoute } from './discover_main_route'; +import { MemoryRouter } from 'react-router-dom'; +import { dataViewMock } from '../../__mocks__/data_view'; +import { SavedObject } from '@kbn/core/public'; +import { DataViewSavedObjectAttrs } from '@kbn/data-views-plugin/common'; +import { DiscoverMainApp } from './discover_main_app'; +import { SearchSource } from '@kbn/data-plugin/common'; +import { searchSourceInstanceMock } from '@kbn/data-plugin/common/search/search_source/mocks'; +import { findTestSubject } from '@elastic/eui/lib/test'; +jest.mock('./discover_main_app', () => { + return { + DiscoverMainApp: jest.fn(), + }; +}); + +describe('DiscoverMainRoute', () => { + test('renders the main app when hasESData=true & hasUserDataView=true ', async () => { + const component = mountComponent(true, true); + + await waitFor(() => { + component.update(); + expect(component.find(DiscoverMainApp).exists()).toBe(true); + }); + }); + + test('renders no data page when hasESData=false & hasUserDataView=false', async () => { + const component = mountComponent(false, false); + + await waitFor(() => { + component.update(); + expect(findTestSubject(component, 'kbnNoDataPage').length).toBe(1); + }); + }); + test('renders no data view when hasESData=true & hasUserDataView=false', async () => { + const component = mountComponent(true, false); + + await waitFor(() => { + component.update(); + expect(findTestSubject(component, 'noDataViewsPrompt').length).toBe(1); + }); + }); + // skipped because this is the case that never ever should happen, it happened once and was fixed in + // https://github.com/elastic/kibana/pull/137824 + test.skip('renders no data page when hasESData=false & hasUserDataView=true', async () => { + const component = mountComponent(false, true); + + await waitFor(() => { + component.update(); + expect(findTestSubject(component, 'kbnNoDataPage').length).toBe(1); + }); + }); +}); +const mountComponent = (hasESData = true, hasUserDataView = true) => { + const props = { + isDev: false, + }; + + return mountWithIntl( + + + + + + ); +}; +function getServicesMock(hasESData = true, hasUserDataView = true) { + const dataViewsMock = discoverServiceMock.data.dataViews; + dataViewsMock.getCache = jest.fn(() => { + return Promise.resolve([dataViewMock as unknown as SavedObject]); + }); + dataViewsMock.get = jest.fn(() => Promise.resolve(dataViewMock)); + dataViewsMock.getDefaultDataView = jest.fn(() => Promise.resolve(dataViewMock)); + dataViewsMock.hasData = { + hasESData: jest.fn(() => Promise.resolve(hasESData)), + hasUserDataView: jest.fn(() => Promise.resolve(hasUserDataView)), + hasDataView: jest.fn(() => Promise.resolve(true)), + }; + dataViewsMock.refreshFields = jest.fn(); + + discoverServiceMock.data.search.searchSource.createEmpty = jest.fn(() => { + const fields: Record = {}; + const empty = { + ...searchSourceInstanceMock, + setField: (key: string, value: unknown) => (fields[key] = value), + getField: (key: string) => fields[key], + }; + return empty as unknown as SearchSource; + }); + return discoverServiceMock; +} + +setHeaderActionMenuMounter(jest.fn()); diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index 6cb8ef93d360e..af8d5800fdae5 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -165,6 +165,7 @@ export class SavedSearchEmbeddable const { searchSource } = this.savedSearch; + const prevAbortController = this.abortController; // Abort any in-progress requests if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); @@ -266,7 +267,8 @@ export class SavedSearchEmbeddable this.searchProps!.totalHitCount = resp.hits.total as number; this.searchProps!.isLoading = false; } catch (error) { - if (!this.destroyed) { + const cancelled = !!prevAbortController?.signal.aborted; + if (!this.destroyed && !cancelled) { this.updateOutput({ ...this.getOutput(), loading: false, diff --git a/src/plugins/home/public/assets/activemq_logs/screenshot.png b/src/plugins/home/public/assets/activemq_logs/screenshot.png deleted file mode 100644 index 3b75889a1bb13..0000000000000 Binary files a/src/plugins/home/public/assets/activemq_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/activemq_logs/screenshot.webp b/src/plugins/home/public/assets/activemq_logs/screenshot.webp new file mode 100644 index 0000000000000..d748b0609b331 Binary files /dev/null and b/src/plugins/home/public/assets/activemq_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/apache_logs/screenshot.png b/src/plugins/home/public/assets/apache_logs/screenshot.png deleted file mode 100644 index a2039096ce041..0000000000000 Binary files a/src/plugins/home/public/assets/apache_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/apache_logs/screenshot.webp b/src/plugins/home/public/assets/apache_logs/screenshot.webp new file mode 100644 index 0000000000000..f52ecc6dafa3f Binary files /dev/null and b/src/plugins/home/public/assets/apache_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/apache_metrics/screenshot.png b/src/plugins/home/public/assets/apache_metrics/screenshot.png deleted file mode 100644 index ac40d5a637a5f..0000000000000 Binary files a/src/plugins/home/public/assets/apache_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/apache_metrics/screenshot.webp b/src/plugins/home/public/assets/apache_metrics/screenshot.webp new file mode 100644 index 0000000000000..a5c9c17c5a266 Binary files /dev/null and b/src/plugins/home/public/assets/apache_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/auditbeat/screenshot.png b/src/plugins/home/public/assets/auditbeat/screenshot.png deleted file mode 100644 index 2d70cc274a2e8..0000000000000 Binary files a/src/plugins/home/public/assets/auditbeat/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/auditbeat/screenshot.webp b/src/plugins/home/public/assets/auditbeat/screenshot.webp new file mode 100644 index 0000000000000..3485ebace7400 Binary files /dev/null and b/src/plugins/home/public/assets/auditbeat/screenshot.webp differ diff --git a/src/plugins/home/public/assets/auditd_logs/screenshot.png b/src/plugins/home/public/assets/auditd_logs/screenshot.png deleted file mode 100644 index 732afa18dc11c..0000000000000 Binary files a/src/plugins/home/public/assets/auditd_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/auditd_logs/screenshot.webp b/src/plugins/home/public/assets/auditd_logs/screenshot.webp new file mode 100644 index 0000000000000..fc6242eeefd9e Binary files /dev/null and b/src/plugins/home/public/assets/auditd_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/aws_logs/screenshot.png b/src/plugins/home/public/assets/aws_logs/screenshot.png deleted file mode 100644 index fc895ceab20ba..0000000000000 Binary files a/src/plugins/home/public/assets/aws_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/aws_logs/screenshot.webp b/src/plugins/home/public/assets/aws_logs/screenshot.webp new file mode 100644 index 0000000000000..5753725b66155 Binary files /dev/null and b/src/plugins/home/public/assets/aws_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/aws_metrics/screenshot.png b/src/plugins/home/public/assets/aws_metrics/screenshot.png deleted file mode 100644 index 9d179622189a2..0000000000000 Binary files a/src/plugins/home/public/assets/aws_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/aws_metrics/screenshot.webp b/src/plugins/home/public/assets/aws_metrics/screenshot.webp new file mode 100644 index 0000000000000..625f48d9c461c Binary files /dev/null and b/src/plugins/home/public/assets/aws_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/azure_logs/screenshot.png b/src/plugins/home/public/assets/azure_logs/screenshot.png deleted file mode 100644 index 32c5a7202d883..0000000000000 Binary files a/src/plugins/home/public/assets/azure_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/azure_logs/screenshot.webp b/src/plugins/home/public/assets/azure_logs/screenshot.webp new file mode 100644 index 0000000000000..60732c0532480 Binary files /dev/null and b/src/plugins/home/public/assets/azure_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/azure_metrics/screenshot.png b/src/plugins/home/public/assets/azure_metrics/screenshot.png deleted file mode 100644 index 22136049b494a..0000000000000 Binary files a/src/plugins/home/public/assets/azure_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/azure_metrics/screenshot.webp b/src/plugins/home/public/assets/azure_metrics/screenshot.webp new file mode 100644 index 0000000000000..e15201a7efd53 Binary files /dev/null and b/src/plugins/home/public/assets/azure_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/cisco_logs/screenshot.png b/src/plugins/home/public/assets/cisco_logs/screenshot.png deleted file mode 100644 index 6108b1a99ecfd..0000000000000 Binary files a/src/plugins/home/public/assets/cisco_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/cisco_logs/screenshot.webp b/src/plugins/home/public/assets/cisco_logs/screenshot.webp new file mode 100644 index 0000000000000..2ae994d65c00a Binary files /dev/null and b/src/plugins/home/public/assets/cisco_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.png b/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.png deleted file mode 100644 index 4b3020d91d57d..0000000000000 Binary files a/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.webp b/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.webp new file mode 100644 index 0000000000000..eafe75da03e34 Binary files /dev/null and b/src/plugins/home/public/assets/cockroachdb_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/consul_metrics/screenshot.png b/src/plugins/home/public/assets/consul_metrics/screenshot.png deleted file mode 100644 index 90aaa7477e8eb..0000000000000 Binary files a/src/plugins/home/public/assets/consul_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/consul_metrics/screenshot.webp b/src/plugins/home/public/assets/consul_metrics/screenshot.webp new file mode 100644 index 0000000000000..a426cb181c003 Binary files /dev/null and b/src/plugins/home/public/assets/consul_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/coredns_logs/screenshot.jpg b/src/plugins/home/public/assets/coredns_logs/screenshot.jpg deleted file mode 100644 index 70921fa9bafb2..0000000000000 Binary files a/src/plugins/home/public/assets/coredns_logs/screenshot.jpg and /dev/null differ diff --git a/src/plugins/home/public/assets/coredns_logs/screenshot.png b/src/plugins/home/public/assets/coredns_logs/screenshot.png deleted file mode 100644 index 70921fa9bafb2..0000000000000 Binary files a/src/plugins/home/public/assets/coredns_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/coredns_logs/screenshot.webp b/src/plugins/home/public/assets/coredns_logs/screenshot.webp new file mode 100644 index 0000000000000..b27c4a6277695 Binary files /dev/null and b/src/plugins/home/public/assets/coredns_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/coredns_metrics/screenshot.png b/src/plugins/home/public/assets/coredns_metrics/screenshot.png deleted file mode 100644 index c30d35d0cca8a..0000000000000 Binary files a/src/plugins/home/public/assets/coredns_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/coredns_metrics/screenshot.webp b/src/plugins/home/public/assets/coredns_metrics/screenshot.webp new file mode 100644 index 0000000000000..25a1627a6d230 Binary files /dev/null and b/src/plugins/home/public/assets/coredns_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/couchdb_metrics/screenshot.png b/src/plugins/home/public/assets/couchdb_metrics/screenshot.png deleted file mode 100644 index cccd14189491b..0000000000000 Binary files a/src/plugins/home/public/assets/couchdb_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/couchdb_metrics/screenshot.webp b/src/plugins/home/public/assets/couchdb_metrics/screenshot.webp new file mode 100644 index 0000000000000..dba62b5d3fbb8 Binary files /dev/null and b/src/plugins/home/public/assets/couchdb_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/crowdstrike_logs/screenshot.png b/src/plugins/home/public/assets/crowdstrike_logs/screenshot.png deleted file mode 100644 index b74edfe2293f9..0000000000000 Binary files a/src/plugins/home/public/assets/crowdstrike_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/crowdstrike_logs/screenshot.webp b/src/plugins/home/public/assets/crowdstrike_logs/screenshot.webp new file mode 100644 index 0000000000000..984ef12e0563e Binary files /dev/null and b/src/plugins/home/public/assets/crowdstrike_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/docker_metrics/screenshot.png b/src/plugins/home/public/assets/docker_metrics/screenshot.png deleted file mode 100644 index 9dc1a229ef142..0000000000000 Binary files a/src/plugins/home/public/assets/docker_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/docker_metrics/screenshot.webp b/src/plugins/home/public/assets/docker_metrics/screenshot.webp new file mode 100644 index 0000000000000..254660288372d Binary files /dev/null and b/src/plugins/home/public/assets/docker_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/envoyproxy_logs/screenshot.png b/src/plugins/home/public/assets/envoyproxy_logs/screenshot.png deleted file mode 100644 index 87f589b4e3c66..0000000000000 Binary files a/src/plugins/home/public/assets/envoyproxy_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/envoyproxy_logs/screenshot.webp b/src/plugins/home/public/assets/envoyproxy_logs/screenshot.webp new file mode 100644 index 0000000000000..ff46ef4e622af Binary files /dev/null and b/src/plugins/home/public/assets/envoyproxy_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/googlecloud_logs/screenshot.png b/src/plugins/home/public/assets/googlecloud_logs/screenshot.png deleted file mode 100644 index 4f68932e9f709..0000000000000 Binary files a/src/plugins/home/public/assets/googlecloud_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/googlecloud_logs/screenshot.webp b/src/plugins/home/public/assets/googlecloud_logs/screenshot.webp new file mode 100644 index 0000000000000..902c1323a5b6d Binary files /dev/null and b/src/plugins/home/public/assets/googlecloud_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/googlecloud_metrics/screenshot.png b/src/plugins/home/public/assets/googlecloud_metrics/screenshot.png deleted file mode 100644 index d4d90d27ad302..0000000000000 Binary files a/src/plugins/home/public/assets/googlecloud_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/googlecloud_metrics/screenshot.webp b/src/plugins/home/public/assets/googlecloud_metrics/screenshot.webp new file mode 100644 index 0000000000000..d46b47ca2baf1 Binary files /dev/null and b/src/plugins/home/public/assets/googlecloud_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/haproxy_logs/screenshot.png b/src/plugins/home/public/assets/haproxy_logs/screenshot.png deleted file mode 100644 index 85a24bf01f3aa..0000000000000 Binary files a/src/plugins/home/public/assets/haproxy_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/haproxy_logs/screenshot.webp b/src/plugins/home/public/assets/haproxy_logs/screenshot.webp new file mode 100644 index 0000000000000..def39259c19aa Binary files /dev/null and b/src/plugins/home/public/assets/haproxy_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/ibmmq_logs/screenshot.png b/src/plugins/home/public/assets/ibmmq_logs/screenshot.png deleted file mode 100644 index 100a8b6ae367c..0000000000000 Binary files a/src/plugins/home/public/assets/ibmmq_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/ibmmq_logs/screenshot.webp b/src/plugins/home/public/assets/ibmmq_logs/screenshot.webp new file mode 100644 index 0000000000000..db12cd433dfad Binary files /dev/null and b/src/plugins/home/public/assets/ibmmq_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/ibmmq_metrics/screenshot.png b/src/plugins/home/public/assets/ibmmq_metrics/screenshot.png deleted file mode 100644 index c4f202ad13bb7..0000000000000 Binary files a/src/plugins/home/public/assets/ibmmq_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/ibmmq_metrics/screenshot.webp b/src/plugins/home/public/assets/ibmmq_metrics/screenshot.webp new file mode 100644 index 0000000000000..6579c13fb0152 Binary files /dev/null and b/src/plugins/home/public/assets/ibmmq_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/icinga_logs/screenshot.png b/src/plugins/home/public/assets/icinga_logs/screenshot.png deleted file mode 100644 index 013b20fcf166e..0000000000000 Binary files a/src/plugins/home/public/assets/icinga_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/icinga_logs/screenshot.webp b/src/plugins/home/public/assets/icinga_logs/screenshot.webp new file mode 100644 index 0000000000000..a74d945845d6c Binary files /dev/null and b/src/plugins/home/public/assets/icinga_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/iis_logs/screenshot.png b/src/plugins/home/public/assets/iis_logs/screenshot.png deleted file mode 100644 index 1e30a2d5c90fa..0000000000000 Binary files a/src/plugins/home/public/assets/iis_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/iis_logs/screenshot.webp b/src/plugins/home/public/assets/iis_logs/screenshot.webp new file mode 100644 index 0000000000000..8d76366c0fe75 Binary files /dev/null and b/src/plugins/home/public/assets/iis_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/iis_metrics/screenshot.png b/src/plugins/home/public/assets/iis_metrics/screenshot.png deleted file mode 100644 index 35e04c49b43f0..0000000000000 Binary files a/src/plugins/home/public/assets/iis_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/iis_metrics/screenshot.webp b/src/plugins/home/public/assets/iis_metrics/screenshot.webp new file mode 100644 index 0000000000000..c7009a1afb6ee Binary files /dev/null and b/src/plugins/home/public/assets/iis_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/iptables_logs/screenshot.png b/src/plugins/home/public/assets/iptables_logs/screenshot.png deleted file mode 100644 index e01ba5275c762..0000000000000 Binary files a/src/plugins/home/public/assets/iptables_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/iptables_logs/screenshot.webp b/src/plugins/home/public/assets/iptables_logs/screenshot.webp new file mode 100644 index 0000000000000..3eceae76c1f32 Binary files /dev/null and b/src/plugins/home/public/assets/iptables_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/kafka_logs/screenshot.png b/src/plugins/home/public/assets/kafka_logs/screenshot.png deleted file mode 100644 index ba485093c127f..0000000000000 Binary files a/src/plugins/home/public/assets/kafka_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/kafka_logs/screenshot.webp b/src/plugins/home/public/assets/kafka_logs/screenshot.webp new file mode 100644 index 0000000000000..c67ebf961bb4a Binary files /dev/null and b/src/plugins/home/public/assets/kafka_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/kubernetes_metrics/screenshot.png b/src/plugins/home/public/assets/kubernetes_metrics/screenshot.png deleted file mode 100644 index ffebda1c21d32..0000000000000 Binary files a/src/plugins/home/public/assets/kubernetes_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/kubernetes_metrics/screenshot.webp b/src/plugins/home/public/assets/kubernetes_metrics/screenshot.webp new file mode 100644 index 0000000000000..6a2c9861a7a70 Binary files /dev/null and b/src/plugins/home/public/assets/kubernetes_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/logstash_logs/screenshot.png b/src/plugins/home/public/assets/logstash_logs/screenshot.png deleted file mode 100644 index 392a2fb9c89eb..0000000000000 Binary files a/src/plugins/home/public/assets/logstash_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/logstash_logs/screenshot.webp b/src/plugins/home/public/assets/logstash_logs/screenshot.webp new file mode 100644 index 0000000000000..47615bbc0f1e6 Binary files /dev/null and b/src/plugins/home/public/assets/logstash_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/microsoft_logs/screenshot.png b/src/plugins/home/public/assets/microsoft_logs/screenshot.png deleted file mode 100644 index 7df250e2ae885..0000000000000 Binary files a/src/plugins/home/public/assets/microsoft_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/microsoft_logs/screenshot.webp b/src/plugins/home/public/assets/microsoft_logs/screenshot.webp new file mode 100644 index 0000000000000..25cc6d5d8c4df Binary files /dev/null and b/src/plugins/home/public/assets/microsoft_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/misp_logs/screenshot.png b/src/plugins/home/public/assets/misp_logs/screenshot.png deleted file mode 100644 index a02068ddf3038..0000000000000 Binary files a/src/plugins/home/public/assets/misp_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/misp_logs/screenshot.webp b/src/plugins/home/public/assets/misp_logs/screenshot.webp new file mode 100644 index 0000000000000..91fc636919743 Binary files /dev/null and b/src/plugins/home/public/assets/misp_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/mongodb_logs/screenshot.png b/src/plugins/home/public/assets/mongodb_logs/screenshot.png deleted file mode 100644 index c77c37d5ce05b..0000000000000 Binary files a/src/plugins/home/public/assets/mongodb_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/mongodb_logs/screenshot.webp b/src/plugins/home/public/assets/mongodb_logs/screenshot.webp new file mode 100644 index 0000000000000..c86617566f3c1 Binary files /dev/null and b/src/plugins/home/public/assets/mongodb_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/mongodb_metrics/screenshot.png b/src/plugins/home/public/assets/mongodb_metrics/screenshot.png deleted file mode 100644 index 88ec14c8caf26..0000000000000 Binary files a/src/plugins/home/public/assets/mongodb_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/mongodb_metrics/screenshot.webp b/src/plugins/home/public/assets/mongodb_metrics/screenshot.webp new file mode 100644 index 0000000000000..4bb6dd7f5d34e Binary files /dev/null and b/src/plugins/home/public/assets/mongodb_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/mssql_metrics/screenshot.png b/src/plugins/home/public/assets/mssql_metrics/screenshot.png deleted file mode 100644 index fdb99617689a4..0000000000000 Binary files a/src/plugins/home/public/assets/mssql_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/mssql_metrics/screenshot.webp b/src/plugins/home/public/assets/mssql_metrics/screenshot.webp new file mode 100644 index 0000000000000..f76c59c78bf98 Binary files /dev/null and b/src/plugins/home/public/assets/mssql_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/mysql_logs/screenshot.png b/src/plugins/home/public/assets/mysql_logs/screenshot.png deleted file mode 100644 index d380042ca72e8..0000000000000 Binary files a/src/plugins/home/public/assets/mysql_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/mysql_logs/screenshot.webp b/src/plugins/home/public/assets/mysql_logs/screenshot.webp new file mode 100644 index 0000000000000..2bd300b7c3d83 Binary files /dev/null and b/src/plugins/home/public/assets/mysql_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/mysql_metrics/screenshot.png b/src/plugins/home/public/assets/mysql_metrics/screenshot.png deleted file mode 100644 index e3091f5156c5a..0000000000000 Binary files a/src/plugins/home/public/assets/mysql_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/mysql_metrics/screenshot.webp b/src/plugins/home/public/assets/mysql_metrics/screenshot.webp new file mode 100644 index 0000000000000..5c77a70eca5b4 Binary files /dev/null and b/src/plugins/home/public/assets/mysql_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/nats_logs/screenshot.png b/src/plugins/home/public/assets/nats_logs/screenshot.png deleted file mode 100644 index 9aa5b4c129ac0..0000000000000 Binary files a/src/plugins/home/public/assets/nats_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/nats_logs/screenshot.webp b/src/plugins/home/public/assets/nats_logs/screenshot.webp new file mode 100644 index 0000000000000..d2c24b7962a12 Binary files /dev/null and b/src/plugins/home/public/assets/nats_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/nats_metrics/screenshot.png b/src/plugins/home/public/assets/nats_metrics/screenshot.png deleted file mode 100644 index 04f71345ef494..0000000000000 Binary files a/src/plugins/home/public/assets/nats_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/nats_metrics/screenshot.webp b/src/plugins/home/public/assets/nats_metrics/screenshot.webp new file mode 100644 index 0000000000000..39aef55bdf544 Binary files /dev/null and b/src/plugins/home/public/assets/nats_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/nginx_logs/screenshot.png b/src/plugins/home/public/assets/nginx_logs/screenshot.png deleted file mode 100644 index 10522377112cb..0000000000000 Binary files a/src/plugins/home/public/assets/nginx_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/nginx_logs/screenshot.webp b/src/plugins/home/public/assets/nginx_logs/screenshot.webp new file mode 100644 index 0000000000000..e8cbc9157cd59 Binary files /dev/null and b/src/plugins/home/public/assets/nginx_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/nginx_metrics/screenshot.png b/src/plugins/home/public/assets/nginx_metrics/screenshot.png deleted file mode 100644 index 003ea39191cdf..0000000000000 Binary files a/src/plugins/home/public/assets/nginx_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/nginx_metrics/screenshot.webp b/src/plugins/home/public/assets/nginx_metrics/screenshot.webp new file mode 100644 index 0000000000000..851b9fd9a414e Binary files /dev/null and b/src/plugins/home/public/assets/nginx_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/o365_logs/screenshot.png b/src/plugins/home/public/assets/o365_logs/screenshot.png deleted file mode 100644 index a2413e7b909bc..0000000000000 Binary files a/src/plugins/home/public/assets/o365_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/o365_logs/screenshot.webp b/src/plugins/home/public/assets/o365_logs/screenshot.webp new file mode 100644 index 0000000000000..11ce5a8627db6 Binary files /dev/null and b/src/plugins/home/public/assets/o365_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/okta_logs/screenshot.png b/src/plugins/home/public/assets/okta_logs/screenshot.png deleted file mode 100644 index 6a28b4363b05b..0000000000000 Binary files a/src/plugins/home/public/assets/okta_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/okta_logs/screenshot.webp b/src/plugins/home/public/assets/okta_logs/screenshot.webp new file mode 100644 index 0000000000000..4797663acd94b Binary files /dev/null and b/src/plugins/home/public/assets/okta_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/osquery_logs/screenshot.png b/src/plugins/home/public/assets/osquery_logs/screenshot.png deleted file mode 100644 index 4082d04001c16..0000000000000 Binary files a/src/plugins/home/public/assets/osquery_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/osquery_logs/screenshot.webp b/src/plugins/home/public/assets/osquery_logs/screenshot.webp new file mode 100644 index 0000000000000..7aaef4a6dd228 Binary files /dev/null and b/src/plugins/home/public/assets/osquery_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/panw_logs/screenshot.png b/src/plugins/home/public/assets/panw_logs/screenshot.png deleted file mode 100644 index ef9d5f706eca6..0000000000000 Binary files a/src/plugins/home/public/assets/panw_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/panw_logs/screenshot.webp b/src/plugins/home/public/assets/panw_logs/screenshot.webp new file mode 100644 index 0000000000000..cb025f7452142 Binary files /dev/null and b/src/plugins/home/public/assets/panw_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/postgresql_logs/screenshot.png b/src/plugins/home/public/assets/postgresql_logs/screenshot.png deleted file mode 100644 index 8185dcd54c510..0000000000000 Binary files a/src/plugins/home/public/assets/postgresql_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/postgresql_logs/screenshot.webp b/src/plugins/home/public/assets/postgresql_logs/screenshot.webp new file mode 100644 index 0000000000000..b8992f795232b Binary files /dev/null and b/src/plugins/home/public/assets/postgresql_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.png b/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.png deleted file mode 100644 index b4b84cae7267c..0000000000000 Binary files a/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.webp b/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.webp new file mode 100644 index 0000000000000..8877a740d073c Binary files /dev/null and b/src/plugins/home/public/assets/rabbitmq_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/redis_logs/screenshot.png b/src/plugins/home/public/assets/redis_logs/screenshot.png deleted file mode 100644 index 9c79dd42317fc..0000000000000 Binary files a/src/plugins/home/public/assets/redis_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/redis_logs/screenshot.webp b/src/plugins/home/public/assets/redis_logs/screenshot.webp new file mode 100644 index 0000000000000..e272effe20f0d Binary files /dev/null and b/src/plugins/home/public/assets/redis_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/redis_metrics/screenshot.png b/src/plugins/home/public/assets/redis_metrics/screenshot.png deleted file mode 100644 index 0e8f7cb3e645a..0000000000000 Binary files a/src/plugins/home/public/assets/redis_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/redis_metrics/screenshot.webp b/src/plugins/home/public/assets/redis_metrics/screenshot.webp new file mode 100644 index 0000000000000..5504f51a291fe Binary files /dev/null and b/src/plugins/home/public/assets/redis_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.png b/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.png deleted file mode 100644 index cc6ef0ce509eb..0000000000000 Binary files a/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.webp b/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.webp new file mode 100644 index 0000000000000..689e52f86a03b Binary files /dev/null and b/src/plugins/home/public/assets/redisenterprise_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.png b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.png deleted file mode 100644 index 37214a219a377..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.png and /dev/null differ 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 new file mode 100644 index 0000000000000..f6720bf0e3e51 Binary files /dev/null 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.png b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.png deleted file mode 100644 index 0c0e5b6f01205..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.png and /dev/null 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 new file mode 100644 index 0000000000000..b9263441d1f22 Binary files /dev/null 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.png b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.png deleted file mode 100644 index a7b66ceeb288d..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.png and /dev/null 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 new file mode 100644 index 0000000000000..f73ae849befc9 Binary files /dev/null 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.png b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.png deleted file mode 100644 index 18487d5c44bff..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.png and /dev/null 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 new file mode 100644 index 0000000000000..3cea45093ddac Binary files /dev/null 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.png b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.png deleted file mode 100644 index ab33a93e30400..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.png and /dev/null 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 new file mode 100644 index 0000000000000..c5b2250190ae0 Binary files /dev/null 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.png b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.png deleted file mode 100644 index 8586c7741e373..0000000000000 Binary files a/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.png and /dev/null 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 new file mode 100644 index 0000000000000..0c5ff2f6cd9f1 Binary files /dev/null and b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.webp differ diff --git a/src/plugins/home/public/assets/santa_logs/screenshot.png b/src/plugins/home/public/assets/santa_logs/screenshot.png deleted file mode 100644 index 31abdeb270a35..0000000000000 Binary files a/src/plugins/home/public/assets/santa_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/santa_logs/screenshot.webp b/src/plugins/home/public/assets/santa_logs/screenshot.webp new file mode 100644 index 0000000000000..a9f6f01cf1730 Binary files /dev/null and b/src/plugins/home/public/assets/santa_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/stan_metrics/screenshot.png b/src/plugins/home/public/assets/stan_metrics/screenshot.png deleted file mode 100644 index a6ed419fe885b..0000000000000 Binary files a/src/plugins/home/public/assets/stan_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/stan_metrics/screenshot.webp b/src/plugins/home/public/assets/stan_metrics/screenshot.webp new file mode 100644 index 0000000000000..b2c43e3a19d5d Binary files /dev/null and b/src/plugins/home/public/assets/stan_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/suricata_logs/screenshot.png b/src/plugins/home/public/assets/suricata_logs/screenshot.png deleted file mode 100644 index 68193524d8748..0000000000000 Binary files a/src/plugins/home/public/assets/suricata_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/suricata_logs/screenshot.webp b/src/plugins/home/public/assets/suricata_logs/screenshot.webp new file mode 100644 index 0000000000000..41625df5c9aa5 Binary files /dev/null and b/src/plugins/home/public/assets/suricata_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/system_logs/screenshot.png b/src/plugins/home/public/assets/system_logs/screenshot.png deleted file mode 100644 index dfb1d832e3a86..0000000000000 Binary files a/src/plugins/home/public/assets/system_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/system_logs/screenshot.webp b/src/plugins/home/public/assets/system_logs/screenshot.webp new file mode 100644 index 0000000000000..25da6b13eadc1 Binary files /dev/null and b/src/plugins/home/public/assets/system_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/system_metrics/screenshot.png b/src/plugins/home/public/assets/system_metrics/screenshot.png deleted file mode 100644 index 2ee3d14f164b6..0000000000000 Binary files a/src/plugins/home/public/assets/system_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/system_metrics/screenshot.webp b/src/plugins/home/public/assets/system_metrics/screenshot.webp new file mode 100644 index 0000000000000..d8e059513f249 Binary files /dev/null and b/src/plugins/home/public/assets/system_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/traefik_logs/screenshot.png b/src/plugins/home/public/assets/traefik_logs/screenshot.png deleted file mode 100644 index c4149b9cd545e..0000000000000 Binary files a/src/plugins/home/public/assets/traefik_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/traefik_logs/screenshot.webp b/src/plugins/home/public/assets/traefik_logs/screenshot.webp new file mode 100644 index 0000000000000..cc221b8b45e68 Binary files /dev/null and b/src/plugins/home/public/assets/traefik_logs/screenshot.webp differ diff --git a/src/plugins/home/public/assets/uptime_monitors/screenshot.png b/src/plugins/home/public/assets/uptime_monitors/screenshot.png deleted file mode 100644 index 9983667c83ec2..0000000000000 Binary files a/src/plugins/home/public/assets/uptime_monitors/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/uptime_monitors/screenshot.webp b/src/plugins/home/public/assets/uptime_monitors/screenshot.webp new file mode 100644 index 0000000000000..f9dd9605a5f2e Binary files /dev/null and b/src/plugins/home/public/assets/uptime_monitors/screenshot.webp differ diff --git a/src/plugins/home/public/assets/uwsgi_metrics/screenshot.png b/src/plugins/home/public/assets/uwsgi_metrics/screenshot.png deleted file mode 100644 index 9d197bc88568b..0000000000000 Binary files a/src/plugins/home/public/assets/uwsgi_metrics/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/uwsgi_metrics/screenshot.webp b/src/plugins/home/public/assets/uwsgi_metrics/screenshot.webp new file mode 100644 index 0000000000000..33666d43c5402 Binary files /dev/null and b/src/plugins/home/public/assets/uwsgi_metrics/screenshot.webp differ diff --git a/src/plugins/home/public/assets/zeek_logs/screenshot.png b/src/plugins/home/public/assets/zeek_logs/screenshot.png deleted file mode 100644 index 7c8458f19afb0..0000000000000 Binary files a/src/plugins/home/public/assets/zeek_logs/screenshot.png and /dev/null differ diff --git a/src/plugins/home/public/assets/zeek_logs/screenshot.webp b/src/plugins/home/public/assets/zeek_logs/screenshot.webp new file mode 100644 index 0000000000000..b49b6fdbac763 Binary files /dev/null and b/src/plugins/home/public/assets/zeek_logs/screenshot.webp differ diff --git a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts index 08747f08fc923..e995510c99fee 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts @@ -24,8 +24,9 @@ export const ecommerceSpecProvider = function (): SampleDatasetSchema { id: 'ecommerce', name: ecommerceName, description: ecommerceDescription, - previewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard.png', - darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard_dark.png', + previewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard.webp', + darkPreviewImagePath: + '/plugins/home/assets/sample_data_resources/ecommerce/dashboard_dark.webp', overviewDashboard: '722b74f0-b882-11e8-a6d9-e546fe2bba5f', defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', savedObjects: getSavedObjects(), diff --git a/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts b/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts index ac0987559eb79..691ac8334cbc3 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts @@ -24,8 +24,8 @@ export const flightsSpecProvider = function (): SampleDatasetSchema { id: 'flights', name: flightsName, description: flightsDescription, - previewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard.png', - darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard_dark.png', + previewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard.webp', + darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard_dark.webp', overviewDashboard: '7adfa750-4c81-11e8-b3d7-01146121b73d', defaultIndex: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', savedObjects: getSavedObjects(), diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts index 77d5f868ad285..e33d49e3f9a83 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts @@ -25,8 +25,8 @@ export const logsSpecProvider = function (): SampleDatasetSchema { id: 'logs', name: logsName, description: logsDescription, - previewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard.png', - darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard_dark.png', + previewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard.webp', + darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard_dark.webp', overviewDashboard: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b', defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0b247', savedObjects: getSavedObjects(), diff --git a/src/plugins/home/server/tutorials/activemq_logs/index.ts b/src/plugins/home/server/tutorials/activemq_logs/index.ts index cc84f9a536b22..5e1b4c2370451 100644 --- a/src/plugins/home/server/tutorials/activemq_logs/index.ts +++ b/src/plugins/home/server/tutorials/activemq_logs/index.ts @@ -54,7 +54,7 @@ export function activemqLogsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/activemq_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/activemq_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/apache_logs/index.ts b/src/plugins/home/server/tutorials/apache_logs/index.ts index aea8e3c188d94..b9b7c1732f831 100644 --- a/src/plugins/home/server/tutorials/apache_logs/index.ts +++ b/src/plugins/home/server/tutorials/apache_logs/index.ts @@ -55,7 +55,7 @@ export function apacheLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/apache_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/apache_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/apache_metrics/index.ts b/src/plugins/home/server/tutorials/apache_metrics/index.ts index 0af719610c24d..a09cbac364d19 100644 --- a/src/plugins/home/server/tutorials/apache_metrics/index.ts +++ b/src/plugins/home/server/tutorials/apache_metrics/index.ts @@ -54,7 +54,7 @@ export function apacheMetricsSpecProvider(context: TutorialContext): TutorialSch }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/apache_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/apache_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/auditbeat/index.ts b/src/plugins/home/server/tutorials/auditbeat/index.ts index 666fcf15635c3..1e5e44ed77160 100644 --- a/src/plugins/home/server/tutorials/auditbeat/index.ts +++ b/src/plugins/home/server/tutorials/auditbeat/index.ts @@ -54,7 +54,7 @@ processes, users, logins, sockets information, file accesses, and more. \ }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/auditbeat/screenshot.png', + previewImagePath: '/plugins/home/assets/auditbeat/screenshot.webp', onPrem: onPremInstructions(platforms, context), elasticCloud: cloudInstructions(platforms, context), onPremElasticCloud: onPremCloudInstructions(platforms, context), diff --git a/src/plugins/home/server/tutorials/auditd_logs/index.ts b/src/plugins/home/server/tutorials/auditd_logs/index.ts index 24857045ccc28..6d18dcace5845 100644 --- a/src/plugins/home/server/tutorials/auditd_logs/index.ts +++ b/src/plugins/home/server/tutorials/auditd_logs/index.ts @@ -55,7 +55,7 @@ export function auditdLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/auditd_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/auditd_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/aws_logs/index.ts b/src/plugins/home/server/tutorials/aws_logs/index.ts index 60187490318ae..f0c291f3dd344 100644 --- a/src/plugins/home/server/tutorials/aws_logs/index.ts +++ b/src/plugins/home/server/tutorials/aws_logs/index.ts @@ -55,7 +55,7 @@ export function awsLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/aws_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/aws_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/aws_metrics/index.ts b/src/plugins/home/server/tutorials/aws_metrics/index.ts index 6541b4f5f29c8..5e4608e7104ed 100644 --- a/src/plugins/home/server/tutorials/aws_metrics/index.ts +++ b/src/plugins/home/server/tutorials/aws_metrics/index.ts @@ -56,7 +56,7 @@ export function awsMetricsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/aws_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/aws_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/azure_logs/index.ts b/src/plugins/home/server/tutorials/azure_logs/index.ts index 163496813567a..1ddaf49bcaa7d 100644 --- a/src/plugins/home/server/tutorials/azure_logs/index.ts +++ b/src/plugins/home/server/tutorials/azure_logs/index.ts @@ -56,7 +56,7 @@ export function azureLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/azure_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/azure_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/azure_metrics/index.ts b/src/plugins/home/server/tutorials/azure_metrics/index.ts index edf4062812b42..ab8a29dec8c36 100644 --- a/src/plugins/home/server/tutorials/azure_metrics/index.ts +++ b/src/plugins/home/server/tutorials/azure_metrics/index.ts @@ -55,7 +55,7 @@ export function azureMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/azure_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/azure_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/cisco_logs/index.ts b/src/plugins/home/server/tutorials/cisco_logs/index.ts index 3c855996873af..098819c14297f 100644 --- a/src/plugins/home/server/tutorials/cisco_logs/index.ts +++ b/src/plugins/home/server/tutorials/cisco_logs/index.ts @@ -55,7 +55,7 @@ export function ciscoLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/cisco_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/cisco_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts b/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts index d53fd7f1f73aa..65b18c04de879 100644 --- a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts @@ -57,7 +57,7 @@ export function cockroachdbMetricsSpecProvider(context: TutorialContext): Tutori }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/cockroachdb_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/cockroachdb_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/consul_metrics/index.ts b/src/plugins/home/server/tutorials/consul_metrics/index.ts index 26fff9e58f511..9f696671a0659 100644 --- a/src/plugins/home/server/tutorials/consul_metrics/index.ts +++ b/src/plugins/home/server/tutorials/consul_metrics/index.ts @@ -54,7 +54,7 @@ export function consulMetricsSpecProvider(context: TutorialContext): TutorialSch }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/consul_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/consul_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/coredns_logs/index.ts b/src/plugins/home/server/tutorials/coredns_logs/index.ts index 876e6e09d61d6..dbec4483ace5c 100644 --- a/src/plugins/home/server/tutorials/coredns_logs/index.ts +++ b/src/plugins/home/server/tutorials/coredns_logs/index.ts @@ -55,7 +55,7 @@ export function corednsLogsSpecProvider(context: TutorialContext): TutorialSchem }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/coredns_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/coredns_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/coredns_metrics/index.ts b/src/plugins/home/server/tutorials/coredns_metrics/index.ts index b854f4d448361..efadd93b1ddc5 100644 --- a/src/plugins/home/server/tutorials/coredns_metrics/index.ts +++ b/src/plugins/home/server/tutorials/coredns_metrics/index.ts @@ -52,7 +52,7 @@ export function corednsMetricsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/coredns_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/coredns_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts b/src/plugins/home/server/tutorials/couchdb_metrics/index.ts index a379b3b04f4c7..f9c1becf83be4 100644 --- a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/couchdb_metrics/index.ts @@ -57,7 +57,7 @@ export function couchdbMetricsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/couchdb_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/couchdb_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/docker_metrics/index.ts b/src/plugins/home/server/tutorials/docker_metrics/index.ts index e36d590650454..20a184f2fbe78 100644 --- a/src/plugins/home/server/tutorials/docker_metrics/index.ts +++ b/src/plugins/home/server/tutorials/docker_metrics/index.ts @@ -54,7 +54,7 @@ export function dockerMetricsSpecProvider(context: TutorialContext): TutorialSch }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/docker_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/docker_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts b/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts index a1df2d8a4085e..bef2a28e5072f 100644 --- a/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts +++ b/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts @@ -54,7 +54,7 @@ export function elasticsearchLogsSpecProvider(context: TutorialContext): Tutoria }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/elasticsearch_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/elasticsearch_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts index f83ca467b1fde..b0d40c254677e 100644 --- a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts +++ b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts @@ -58,7 +58,7 @@ export function envoyproxyLogsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/envoyproxy_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/envoyproxy_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/f5_logs/index.ts b/src/plugins/home/server/tutorials/f5_logs/index.ts index 381fdd487eb24..0980b14d93f72 100644 --- a/src/plugins/home/server/tutorials/f5_logs/index.ts +++ b/src/plugins/home/server/tutorials/f5_logs/index.ts @@ -53,7 +53,7 @@ export function f5LogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/f5_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/f5_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/gcp_logs/index.ts b/src/plugins/home/server/tutorials/gcp_logs/index.ts index d02c08cd2be9a..b4a5765b87d30 100644 --- a/src/plugins/home/server/tutorials/gcp_logs/index.ts +++ b/src/plugins/home/server/tutorials/gcp_logs/index.ts @@ -57,7 +57,7 @@ export function gcpLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/gcp_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/gcp_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/gcp_metrics/index.ts b/src/plugins/home/server/tutorials/gcp_metrics/index.ts index ea5351d010a42..57d3a1434ec98 100644 --- a/src/plugins/home/server/tutorials/gcp_metrics/index.ts +++ b/src/plugins/home/server/tutorials/gcp_metrics/index.ts @@ -55,7 +55,7 @@ export function gcpMetricsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/gcp_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/gcp_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/haproxy_logs/index.ts b/src/plugins/home/server/tutorials/haproxy_logs/index.ts index 05fc23fa16bcd..1cd429b688e7d 100644 --- a/src/plugins/home/server/tutorials/haproxy_logs/index.ts +++ b/src/plugins/home/server/tutorials/haproxy_logs/index.ts @@ -55,7 +55,7 @@ export function haproxyLogsSpecProvider(context: TutorialContext): TutorialSchem }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/haproxy_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/haproxy_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts b/src/plugins/home/server/tutorials/ibmmq_logs/index.ts index 90b35d0e78842..b6348a5a6ccdd 100644 --- a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts +++ b/src/plugins/home/server/tutorials/ibmmq_logs/index.ts @@ -54,7 +54,7 @@ export function ibmmqLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/ibmmq_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/ibmmq_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts b/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts index 6329df6836b06..f488638b8af24 100644 --- a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts +++ b/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts @@ -53,7 +53,7 @@ export function ibmmqMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/ibmmq_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/ibmmq_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/icinga_logs/index.ts b/src/plugins/home/server/tutorials/icinga_logs/index.ts index c65e92d0fe856..63ecd6db5d548 100644 --- a/src/plugins/home/server/tutorials/icinga_logs/index.ts +++ b/src/plugins/home/server/tutorials/icinga_logs/index.ts @@ -55,7 +55,7 @@ export function icingaLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/icinga_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/icinga_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/iis_logs/index.ts b/src/plugins/home/server/tutorials/iis_logs/index.ts index 423f2f917c84e..3d6d6e009959a 100644 --- a/src/plugins/home/server/tutorials/iis_logs/index.ts +++ b/src/plugins/home/server/tutorials/iis_logs/index.ts @@ -56,7 +56,7 @@ export function iisLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/iis_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/iis_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/iis_metrics/index.ts b/src/plugins/home/server/tutorials/iis_metrics/index.ts index 3c3159c2838d1..d2e193f38b16f 100644 --- a/src/plugins/home/server/tutorials/iis_metrics/index.ts +++ b/src/plugins/home/server/tutorials/iis_metrics/index.ts @@ -55,7 +55,7 @@ export function iisMetricsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/iis_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/iis_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/iptables_logs/index.ts b/src/plugins/home/server/tutorials/iptables_logs/index.ts index f4469de3336cc..082b0a239714a 100644 --- a/src/plugins/home/server/tutorials/iptables_logs/index.ts +++ b/src/plugins/home/server/tutorials/iptables_logs/index.ts @@ -58,7 +58,7 @@ export function iptablesLogsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/iptables_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/iptables_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/kafka_logs/index.ts b/src/plugins/home/server/tutorials/kafka_logs/index.ts index 6e377f3c1f295..6db95424fa967 100644 --- a/src/plugins/home/server/tutorials/kafka_logs/index.ts +++ b/src/plugins/home/server/tutorials/kafka_logs/index.ts @@ -55,7 +55,7 @@ export function kafkaLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/kafka_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/kafka_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts b/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts index acd65e0bdc69d..8eea49daac559 100644 --- a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts +++ b/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts @@ -57,7 +57,7 @@ export function kubernetesMetricsSpecProvider(context: TutorialContext): Tutoria }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/kubernetes_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/kubernetes_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/microsoft_logs/index.ts b/src/plugins/home/server/tutorials/microsoft_logs/index.ts index 39400f4661071..aacac3a3f1299 100644 --- a/src/plugins/home/server/tutorials/microsoft_logs/index.ts +++ b/src/plugins/home/server/tutorials/microsoft_logs/index.ts @@ -55,7 +55,7 @@ export function microsoftLogsSpecProvider(context: TutorialContext): TutorialSch }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/microsoft_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/microsoft_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/misp_logs/index.ts b/src/plugins/home/server/tutorials/misp_logs/index.ts index 4fb70aa1018f7..fcb080459627a 100644 --- a/src/plugins/home/server/tutorials/misp_logs/index.ts +++ b/src/plugins/home/server/tutorials/misp_logs/index.ts @@ -55,7 +55,7 @@ export function mispLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/misp_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/misp_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/mongodb_logs/index.ts b/src/plugins/home/server/tutorials/mongodb_logs/index.ts index 28e323a2b15a9..e9378479b3dbb 100644 --- a/src/plugins/home/server/tutorials/mongodb_logs/index.ts +++ b/src/plugins/home/server/tutorials/mongodb_logs/index.ts @@ -55,7 +55,7 @@ export function mongodbLogsSpecProvider(context: TutorialContext): TutorialSchem }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/mongodb_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/mongodb_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts b/src/plugins/home/server/tutorials/mongodb_metrics/index.ts index db843d09abfd8..18dcaf4fe78e9 100644 --- a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mongodb_metrics/index.ts @@ -57,7 +57,7 @@ export function mongodbMetricsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/mongodb_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/mongodb_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/mssql_metrics/index.ts b/src/plugins/home/server/tutorials/mssql_metrics/index.ts index 3e73714784f0f..321dfce1f8fa7 100644 --- a/src/plugins/home/server/tutorials/mssql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mssql_metrics/index.ts @@ -55,7 +55,7 @@ export function mssqlMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/mssql_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/mssql_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/mysql_logs/index.ts b/src/plugins/home/server/tutorials/mysql_logs/index.ts index 9af0a3d078cab..1bc6b5dae7ddc 100644 --- a/src/plugins/home/server/tutorials/mysql_logs/index.ts +++ b/src/plugins/home/server/tutorials/mysql_logs/index.ts @@ -55,7 +55,7 @@ export function mysqlLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/mysql_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/mysql_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/mysql_metrics/index.ts b/src/plugins/home/server/tutorials/mysql_metrics/index.ts index 8339561d060d6..933f7f0547bac 100644 --- a/src/plugins/home/server/tutorials/mysql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mysql_metrics/index.ts @@ -54,7 +54,7 @@ export function mysqlMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/mysql_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/mysql_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/nats_logs/index.ts b/src/plugins/home/server/tutorials/nats_logs/index.ts index 971f0c2849bda..78af445234f25 100644 --- a/src/plugins/home/server/tutorials/nats_logs/index.ts +++ b/src/plugins/home/server/tutorials/nats_logs/index.ts @@ -56,7 +56,7 @@ export function natsLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/nats_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/nats_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/nats_metrics/index.ts b/src/plugins/home/server/tutorials/nats_metrics/index.ts index cdd633d88140c..56a4a6e83b197 100644 --- a/src/plugins/home/server/tutorials/nats_metrics/index.ts +++ b/src/plugins/home/server/tutorials/nats_metrics/index.ts @@ -54,7 +54,7 @@ export function natsMetricsSpecProvider(context: TutorialContext): TutorialSchem }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/nats_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/nats_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/nginx_logs/index.ts b/src/plugins/home/server/tutorials/nginx_logs/index.ts index 3797f2496ee17..c41597a4c7edb 100644 --- a/src/plugins/home/server/tutorials/nginx_logs/index.ts +++ b/src/plugins/home/server/tutorials/nginx_logs/index.ts @@ -55,7 +55,7 @@ export function nginxLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/nginx_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/nginx_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/nginx_metrics/index.ts b/src/plugins/home/server/tutorials/nginx_metrics/index.ts index f32e9388c1f5b..d8779611e54aa 100644 --- a/src/plugins/home/server/tutorials/nginx_metrics/index.ts +++ b/src/plugins/home/server/tutorials/nginx_metrics/index.ts @@ -59,7 +59,7 @@ which must be enabled in your Nginx installation. \ }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/nginx_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/nginx_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/o365_logs/index.ts b/src/plugins/home/server/tutorials/o365_logs/index.ts index cbdabc7223b32..b1151d1807ed7 100644 --- a/src/plugins/home/server/tutorials/o365_logs/index.ts +++ b/src/plugins/home/server/tutorials/o365_logs/index.ts @@ -58,7 +58,7 @@ export function o365LogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/o365_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/o365_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/okta_logs/index.ts b/src/plugins/home/server/tutorials/okta_logs/index.ts index f45ffbfb800b5..1e73cf63bd9a8 100644 --- a/src/plugins/home/server/tutorials/okta_logs/index.ts +++ b/src/plugins/home/server/tutorials/okta_logs/index.ts @@ -56,7 +56,7 @@ export function oktaLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/okta_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/okta_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/panw_logs/index.ts b/src/plugins/home/server/tutorials/panw_logs/index.ts index 4b44038c07ade..5bc2537821764 100644 --- a/src/plugins/home/server/tutorials/panw_logs/index.ts +++ b/src/plugins/home/server/tutorials/panw_logs/index.ts @@ -58,7 +58,7 @@ export function panwLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/panw_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/panw_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/postgresql_logs/index.ts b/src/plugins/home/server/tutorials/postgresql_logs/index.ts index a628f422dfb72..bfcf64c809e54 100644 --- a/src/plugins/home/server/tutorials/postgresql_logs/index.ts +++ b/src/plugins/home/server/tutorials/postgresql_logs/index.ts @@ -58,7 +58,7 @@ export function postgresqlLogsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/postgresql_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/postgresql_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts b/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts index 4487a187fa373..7aa31fd6ff277 100644 --- a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts +++ b/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts @@ -58,7 +58,7 @@ export function rabbitmqMetricsSpecProvider(context: TutorialContext): TutorialS }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/rabbitmq_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/rabbitmq_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/redis_logs/index.ts b/src/plugins/home/server/tutorials/redis_logs/index.ts index bb5d902d089e2..a80513f57d15c 100644 --- a/src/plugins/home/server/tutorials/redis_logs/index.ts +++ b/src/plugins/home/server/tutorials/redis_logs/index.ts @@ -61,7 +61,7 @@ Note that the `slowlog` fileset is experimental. \ }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/redis_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/redis_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/redis_metrics/index.ts b/src/plugins/home/server/tutorials/redis_metrics/index.ts index d2e8ed1efb779..d9614639e7033 100644 --- a/src/plugins/home/server/tutorials/redis_metrics/index.ts +++ b/src/plugins/home/server/tutorials/redis_metrics/index.ts @@ -54,7 +54,7 @@ export function redisMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/redis_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/redis_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts b/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts index 85d6dce9adc52..52394a5274dc1 100644 --- a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts +++ b/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts @@ -53,7 +53,7 @@ export function redisenterpriseMetricsSpecProvider(context: TutorialContext): Tu }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/redisenterprise_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/redisenterprise_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/santa_logs/index.ts b/src/plugins/home/server/tutorials/santa_logs/index.ts index 65a7bb0bd26cb..1db34e2245535 100644 --- a/src/plugins/home/server/tutorials/santa_logs/index.ts +++ b/src/plugins/home/server/tutorials/santa_logs/index.ts @@ -56,7 +56,7 @@ export function santaLogsSpecProvider(context: TutorialContext): TutorialSchema }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/santa_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/santa_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/stan_metrics/index.ts b/src/plugins/home/server/tutorials/stan_metrics/index.ts index 50f2b9dbd2e87..cb39bd5f91e34 100644 --- a/src/plugins/home/server/tutorials/stan_metrics/index.ts +++ b/src/plugins/home/server/tutorials/stan_metrics/index.ts @@ -54,7 +54,7 @@ export function stanMetricsSpecProvider(context: TutorialContext): TutorialSchem }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/stan_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/stan_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/suricata_logs/index.ts b/src/plugins/home/server/tutorials/suricata_logs/index.ts index a511be4a7a968..b60521b71452e 100644 --- a/src/plugins/home/server/tutorials/suricata_logs/index.ts +++ b/src/plugins/home/server/tutorials/suricata_logs/index.ts @@ -56,7 +56,7 @@ export function suricataLogsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/suricata_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/suricata_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/home/server/tutorials/system_metrics/index.ts b/src/plugins/home/server/tutorials/system_metrics/index.ts index 10a6c741721b8..7bbc341c47746 100644 --- a/src/plugins/home/server/tutorials/system_metrics/index.ts +++ b/src/plugins/home/server/tutorials/system_metrics/index.ts @@ -56,7 +56,7 @@ It collects system wide statistics and statistics per process and filesystem. \ }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/system_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/system_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/uptime_monitors/index.ts b/src/plugins/home/server/tutorials/uptime_monitors/index.ts index 9015cb4783163..29610f603aed5 100644 --- a/src/plugins/home/server/tutorials/uptime_monitors/index.ts +++ b/src/plugins/home/server/tutorials/uptime_monitors/index.ts @@ -53,7 +53,7 @@ export function uptimeMonitorsSpecProvider(context: TutorialContext): TutorialSc }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/uptime_monitors/screenshot.png', + previewImagePath: '/plugins/home/assets/uptime_monitors/screenshot.webp', onPrem: onPremInstructions([], context), elasticCloud: cloudInstructions(context), onPremElasticCloud: onPremCloudInstructions(context), diff --git a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts index bb288ba72ab02..ff47974287c57 100644 --- a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts +++ b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts @@ -55,7 +55,7 @@ export function uwsgiMetricsSpecProvider(context: TutorialContext): TutorialSche }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/uwsgi_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/uwsgi_metrics/screenshot.webp', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName, context), onPremElasticCloud: onPremCloudInstructions(moduleName, context), diff --git a/src/plugins/home/server/tutorials/zeek_logs/index.ts b/src/plugins/home/server/tutorials/zeek_logs/index.ts index 3eded8336df74..d61a265d7e79f 100644 --- a/src/plugins/home/server/tutorials/zeek_logs/index.ts +++ b/src/plugins/home/server/tutorials/zeek_logs/index.ts @@ -56,7 +56,7 @@ export function zeekLogsSpecProvider(context: TutorialContext): TutorialSchema { }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/zeek_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/zeek_logs/screenshot.webp', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms, context), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms, context), diff --git a/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts index a6479bbbefeb0..8c8bda93ce13e 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts @@ -7,7 +7,8 @@ */ import { findObjectByTitle } from './find_object_by_title'; -import { SimpleSavedObjectImpl, SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { simpleSavedObjectMock } from '@kbn/core/public/mocks'; describe('findObjectByTitle', () => { const savedObjectsClient: SavedObjectsClientContract = {} as SavedObjectsClientContract; @@ -22,7 +23,7 @@ describe('findObjectByTitle', () => { }); it('matches any case', async () => { - const indexPattern = new SimpleSavedObjectImpl(savedObjectsClient, { + const indexPattern = simpleSavedObjectMock.create(savedObjectsClient, { attributes: { title: 'foo' }, } as SavedObject); savedObjectsClient.find = jest.fn().mockImplementation(() => diff --git a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts index 62b8c01c72880..9dbeb270a9d72 100644 --- a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts +++ b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts @@ -49,12 +49,13 @@ handlebars.registerHelper( handlebars.registerHelper('date', (...args) => { const values = args.slice(0, -1) as [string | Date, string | undefined]; + const { hash } = args.slice(-1)[0] as Handlebars.HelperOptions; // eslint-disable-next-line prefer-const let [date, format] = values; if (typeof date === 'undefined') throw new Error(`[date]: unknown variable`); let momentDate: Moment | undefined; if (typeof date === 'string') { - momentDate = dateMath.parse(date); + momentDate = dateMath.parse(date, { roundUp: hash.roundUp === true }); if (!momentDate || !momentDate.isValid()) { const ts = Number(date); if (!Number.isNaN(ts)) { diff --git a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts index fefbe03327956..1868f48d033c2 100644 --- a/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts +++ b/src/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts @@ -108,6 +108,13 @@ describe('date helper', () => { ); }); + test('can configure roundUp for dateMath', async () => { + const url = 'https://elastic.co/from={{date from}}&to={{date to roundUp=true}}'; + expect(await compile(url, { from: 'now/d', to: 'now/d' })).toMatchInlineSnapshot( + `"https://elastic.co/from=2020-08-18T00:00:00.000Z&to=2020-08-18T23:59:59.999Z"` + ); + }); + test('can use format', async () => { const url = 'https://elastic.co/{{date time "dddd, MMMM Do YYYY, h:mm:ss a"}}'; expect(await compile(url, { time: 'now' })).toMatchInlineSnapshot( diff --git a/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx index 551915a34b11f..bff5becf3c743 100644 --- a/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx +++ b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx @@ -430,6 +430,24 @@ storiesOf('SearchBar', module) showSubmitButton: false, } as SearchBarProps) ) + .add('show only datepicker without submit', () => + wrapSearchBarInContext({ + showDatePicker: true, + showFilterBar: false, + showAutoRefreshOnly: false, + showQueryInput: false, + showSubmitButton: false, + } as SearchBarProps) + ) + .add('show only query bar and timepicker without submit', () => + wrapSearchBarInContext({ + showDatePicker: true, + showFilterBar: false, + showAutoRefreshOnly: false, + showQueryInput: true, + showSubmitButton: false, + } as SearchBarProps) + ) .add('with filter bar on but pinning option is hidden from menus', () => wrapSearchBarInContext({ showDatePicker: false, 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 3513900c68dd1..9a5f5bf565959 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 @@ -14,7 +14,6 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { ChangeDataView } from './change_dataview'; -import { EuiTourStep } from '@elastic/eui'; import { DataViewPickerPropsExtended, TextBasedLanguages } from '.'; describe('DataView component', () => { @@ -83,25 +82,6 @@ describe('DataView component', () => { onTextLangQuerySubmit: jest.fn(), }; }); - it('should not render the tour component by default', async () => { - await act(async () => { - const component = mount(wrapDataViewComponentInContext(props, true)); - expect(component.find(EuiTourStep).prop('isStepOpen')).toBe(false); - }); - }); - it('should render the tour component if the showNewMenuTour is true', async () => { - const component = mount( - wrapDataViewComponentInContext({ ...props, showNewMenuTour: true }, false) - ); - expect(component.find(EuiTourStep).prop('isStepOpen')).toBe(true); - }); - - it('should not render the tour component if the showNewMenuTour is true but the hideAnnouncements setting is on', async () => { - const component = mount( - wrapDataViewComponentInContext({ ...props, showNewMenuTour: true }, false, true) - ); - expect(component.find(EuiTourStep).prop('isStepOpen')).toBe(false); - }); it('should not render the add runtime field menu if addField is not given', async () => { await act(async () => { @@ -114,10 +94,7 @@ describe('DataView component', () => { it('should render the add runtime field menu if addField is given', async () => { const addFieldSpy = jest.fn(); const component = mount( - wrapDataViewComponentInContext( - { ...props, onAddField: addFieldSpy, showNewMenuTour: true }, - false - ) + wrapDataViewComponentInContext({ ...props, onAddField: addFieldSpy }, false) ); findTestSubject(component, 'dataview-trigger').simulate('click'); expect(component.find('[data-test-subj="indexPattern-add-field"]').at(0).text()).toContain( @@ -138,10 +115,7 @@ describe('DataView component', () => { it('should render the add datavuew menu if onDataViewCreated is given', async () => { const addDataViewSpy = jest.fn(); const component = mount( - wrapDataViewComponentInContext( - { ...props, onDataViewCreated: addDataViewSpy, showNewMenuTour: true }, - false - ) + wrapDataViewComponentInContext({ ...props, onDataViewCreated: addDataViewSpy }, false) ); findTestSubject(component, 'dataview-trigger').simulate('click'); expect(component.find('[data-test-subj="dataview-create-new"]').at(0).text()).toContain( @@ -156,7 +130,6 @@ describe('DataView component', () => { wrapDataViewComponentInContext( { ...props, - showNewMenuTour: true, textBasedLanguages: [TextBasedLanguages.ESQL, TextBasedLanguages.SQL], textBasedLanguage: TextBasedLanguages.SQL, }, 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 d2762f0d27b2f..39ccfb44e7def 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -18,9 +18,7 @@ import { useEuiTheme, useGeneratedHtmlId, EuiIcon, - EuiLink, EuiText, - EuiTourStep, EuiContextMenuPanelProps, EuiFlexGroup, EuiFlexItem, @@ -36,31 +34,9 @@ import type { TextBasedLanguagesListProps } from './text_languages_list'; import type { TextBasedLanguagesTransitionModalProps } from './text_languages_transition_modal'; import { changeDataViewStyles } from './change_dataview.styles'; -const hideAnnouncementsUISetting = 'hideAnnouncements'; -// local storage key for the tour component -const NEW_DATA_VIEW_MENU_STORAGE_KEY = 'data.newDataViewMenu'; // local storage key for the text based languages transition modal const TEXT_LANG_TRANSITION_MODAL_KEY = 'data.textLangTransitionModal'; -const newMenuTourTitle = i18n.translate('unifiedSearch.query.dataViewMenu.newMenuTour.title', { - defaultMessage: 'A better data view menu', -}); - -const newMenuTourDescription = i18n.translate( - 'unifiedSearch.query.dataViewMenu.newMenuTour.description', - { - defaultMessage: - 'This menu now offers all the tools you need to create, find, and edit your data views.', - } -); - -const newMenuTourDismissLabel = i18n.translate( - 'unifiedSearch.query.dataViewMenu.newMenuTour.dismissLabel', - { - defaultMessage: 'Got it', - } -); - const Fallback = () =>
; const LazyTextBasedLanguagesTransitionModal = React.lazy( @@ -89,7 +65,6 @@ export function ChangeDataView({ onDataViewCreated, trigger, selectableProps, - showNewMenuTour = false, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, @@ -106,40 +81,11 @@ export function ChangeDataView({ const [selectedDataViewId, setSelectedDataViewId] = useState(currentDataViewId); const kibana = useKibana(); - const { application, data, storage, uiSettings } = kibana.services; + const { application, data, storage } = kibana.services; const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() => Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY)) ); - const isHideAnnouncementSettingsOn = Boolean(uiSettings.get(hideAnnouncementsUISetting)); - - const [isTourDismissed, setIsTourDismissed] = useState(() => - Boolean(storage.get(NEW_DATA_VIEW_MENU_STORAGE_KEY)) - ); - const [isTourOpen, setIsTourOpen] = useState(false); - - useEffect(() => { - if ( - showNewMenuTour && - !isTourDismissed && - !isHideAnnouncementSettingsOn && - !isTextBasedLangSelected - ) { - setIsTourOpen(true); - } - }, [ - isHideAnnouncementSettingsOn, - isTextBasedLangSelected, - isTourDismissed, - setIsTourOpen, - showNewMenuTour, - ]); - - const onTourDismiss = () => { - storage.set(NEW_DATA_VIEW_MENU_STORAGE_KEY, true); - setIsTourDismissed(true); - setIsTourOpen(false); - }; // Create a reusable id to ensure search input is the first focused item in the popover even though it's not the first item const searchListInputId = useGeneratedHtmlId({ prefix: 'dataviewPickerListSearchInput' }); @@ -176,8 +122,6 @@ export function ChangeDataView({ data-test-subj={dataTestSubj} onClick={() => { setPopoverIsOpen(!isPopoverOpen); - setIsTourOpen(false); - // onTourDismiss(); TODO: Decide if opening the menu should also dismiss the tour }} color={isMissingCurrent ? 'danger' : 'primary'} iconSide="right" @@ -427,47 +371,23 @@ export function ChangeDataView({ return ( <> - -   {newMenuTourTitle} - - } - content={ - -

{newMenuTourDescription}

-
- } - isStepOpen={isTourOpen} - onFinish={onTourDismiss} - step={1} - stepsTotal={1} - footerAction={ - - {newMenuTourDismissLabel} - - } - repositionOnScroll + setPopoverIsOpen(false)} + panelPaddingSize="none" + initialFocus={!isTextBasedLangSelected ? `#${searchListInputId}` : false} display="block" + buffer={8} > - setPopoverIsOpen(false)} - panelPaddingSize="none" - initialFocus={!isTextBasedLangSelected ? `#${searchListInputId}` : false} - display="block" - buffer={8} - > -
- -
-
-
+
+ +
+ {modal} ); diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 40ced3b6f4d47..82909714cffad 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -58,10 +58,6 @@ export interface DataViewPickerProps { * Also works as a flag to show the create dataview button. */ onDataViewCreated?: () => void; - /** - * Flag to show the tour component for the first time. - */ - showNewMenuTour?: boolean; /** * List of the supported text based languages (SQL, ESQL) etc. * Defined per application, if not provided, no text based languages @@ -93,7 +89,6 @@ export const DataViewPicker = ({ onDataViewCreated, trigger, selectableProps, - showNewMenuTour, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, @@ -108,7 +103,6 @@ export const DataViewPicker = ({ onDataViewCreated={onDataViewCreated} trigger={trigger} selectableProps={selectableProps} - showNewMenuTour={showNewMenuTour} textBasedLanguages={textBasedLanguages} onSaveTextLanguageQuery={onSaveTextLanguageQuery} onTextLangQuerySubmit={onTextLangQuerySubmit} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx index fd1ea2e9bce78..1f879ebcae9a8 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx @@ -113,6 +113,7 @@ function wrapQueryBarTopRowInContext(testProps: any) { describe('QueryBarTopRowTopRow', () => { const QUERY_INPUT_SELECTOR = 'QueryStringInputUI'; const TIMEPICKER_SELECTOR = 'Memo(EuiSuperDatePicker)'; + const REFRESH_BUTTON_SELECTOR = 'EuiSuperUpdateButton'; const TIMEPICKER_DURATION = '[data-shared-timefilter-duration]'; beforeEach(() => { @@ -195,6 +196,23 @@ describe('QueryBarTopRowTopRow', () => { expect(component.find(TIMEPICKER_SELECTOR).length).toBe(1); }); + it('Should render timepicker without the submit button if showSubmitButton is false', () => { + const component = mount( + wrapQueryBarTopRowInContext({ + isDirty: false, + screenTitle: 'Another Screen', + showDatePicker: true, + showSubmitButton: false, + dateRangeFrom: 'now-7d', + dateRangeTo: 'now', + timeHistory: mockTimeHistory, + }) + ); + + expect(component.find(REFRESH_BUTTON_SELECTOR).length).toBe(0); + expect(component.find(TIMEPICKER_SELECTOR).length).toBe(1); + }); + it('Should render the timefilter duration container for sharing', () => { const component = mount( wrapQueryBarTopRowInContext({ diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 22c6765368c42..acad2a39d4c4f 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -375,10 +375,9 @@ export const QueryBarTopRow = React.memo( } function renderUpdateButton() { - if (!shouldRenderUpdatebutton()) { + if (!shouldRenderUpdatebutton() && !shouldRenderDatePicker()) { return null; } - const buttonLabelUpdate = i18n.translate('unifiedSearch.queryBarTopRow.submitButton.update', { defaultMessage: 'Needs updating', }); @@ -421,7 +420,8 @@ export const QueryBarTopRow = React.memo( ); - if (!shouldRenderDatePicker()) { + // allows to render the button without the datepicker + if (!shouldRenderDatePicker() && shouldRenderUpdatebutton()) { return button; } @@ -429,8 +429,8 @@ export const QueryBarTopRow = React.memo( - {renderDatePicker()} - {button} + {shouldRenderDatePicker() ? renderDatePicker() : null} + {shouldRenderUpdatebutton() ? button : null} @@ -447,7 +447,6 @@ export const QueryBarTopRow = React.memo( return ( = { test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView, @@ -60,13 +60,13 @@ const model = { ], } as Panel; -describe('triggerTSVBtoLensConfiguration', () => { +describe('convertTSVBtoLensConfiguration', () => { test('should return null for a non timeseries chart', async () => { const metricModel = { ...model, type: 'metric', } as Panel; - const triggerOptions = await triggerTSVBtoLensConfiguration(metricModel); + const triggerOptions = await convertTSVBtoLensConfiguration(metricModel); expect(triggerOptions).toBeNull(); }); @@ -75,7 +75,7 @@ describe('triggerTSVBtoLensConfiguration', () => { ...model, use_kibana_indexes: false, }; - const triggerOptions = await triggerTSVBtoLensConfiguration(stringIndexPatternModel); + const triggerOptions = await convertTSVBtoLensConfiguration(stringIndexPatternModel); expect(triggerOptions).toBeNull(); }); @@ -93,12 +93,12 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(nonSupportedAggModel); + const triggerOptions = await convertTSVBtoLensConfiguration(nonSupportedAggModel); expect(triggerOptions).toBeNull(); }); test('should return options for a supported aggregation', async () => { - const triggerOptions = await triggerTSVBtoLensConfiguration(model); + const triggerOptions = await convertTSVBtoLensConfiguration(model); expect(triggerOptions).toStrictEqual({ configuration: { extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, @@ -152,7 +152,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithFill); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); expect(triggerOptions?.layers[0].chartType).toBe('area'); }); @@ -166,7 +166,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithFill); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.shift).toBe('1h'); }); @@ -183,7 +183,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithFill); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.kql).toBe('test'); }); @@ -207,7 +207,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithSplitFilters); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithSplitFilters); expect(triggerOptions?.layers[0]?.splitFilters).toStrictEqual([ { color: 'rgba(188,0,85,1)', @@ -240,7 +240,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ] as unknown as Series[], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithTerms); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); expect(triggerOptions?.layers[0]?.collapseFn).toStrictEqual('sum'); expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ size: 6, @@ -269,7 +269,7 @@ describe('triggerTSVBtoLensConfiguration', () => { }, ] as unknown as Series[], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithTerms); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ size: 6, otherBucket: false, @@ -289,7 +289,7 @@ describe('triggerTSVBtoLensConfiguration', () => { ...model, interval: '1h', }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithTerms); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); expect(triggerOptions?.layers[0]?.timeInterval).toBe('1h'); }); @@ -298,7 +298,7 @@ describe('triggerTSVBtoLensConfiguration', () => { ...model, drop_last_bucket: 1, }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithDropBuckets); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithDropBuckets); expect(triggerOptions?.layers[0]?.dropPartialBuckets).toBe(true); }); @@ -311,7 +311,7 @@ describe('triggerTSVBtoLensConfiguration', () => { show_grid: 1, series: [{ ...model.series[0], fill: '0.3', separate_axis: 1, axis_position: 'right' }], }; - const triggerOptions = await triggerTSVBtoLensConfiguration(modelWithConfig); + const triggerOptions = await convertTSVBtoLensConfiguration(modelWithConfig); expect(triggerOptions).toStrictEqual({ configuration: { extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, 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 new file mode 100644 index 0000000000000..071001381f0f1 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Panel } from '../../common/types'; +import { PANEL_TYPES } from '../../common/enums'; +import { ConvertTsvbToLensVisualization } from './types'; + +const getConvertFnByType = ( + type: PANEL_TYPES +): Promise | undefined => { + const convertionFns: { [key in PANEL_TYPES]?: () => Promise } = { + [PANEL_TYPES.TIMESERIES]: async () => { + const { convertToLens } = await import('./timeseries'); + return convertToLens; + }, + }; + + return convertionFns[type]?.(); +}; + +/* + * This function is used to convert the TSVB model to compatible Lens model. + * Returns the Lens model, only if it is supported. If not, it returns null. + * In case of null, the menu item is disabled and the user can't navigate to Lens. + */ +export const convertTSVBtoLensConfiguration = async (model: Panel) => { + // Disables the option for not timeseries charts, for the string mode and for series with annotations + if (!model.use_kibana_indexes || (model.annotations && model.annotations.length > 0)) { + return null; + } + const convertFn = await getConvertFnByType(model.type); + + return (await convertFn?.(model)) ?? null; +}; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.test.ts similarity index 75% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.test.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.test.ts index 8405a685122ed..33052a25190d1 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.test.ts @@ -5,8 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DataView } from '@kbn/data-plugin/common'; import { getDataSourceInfo } from './get_datasource_info'; + const dataViewsMap: Record = { test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView, test2: { @@ -17,27 +19,26 @@ const dataViewsMap: Record = { test3: { id: 'test3', title: 'test3', timeFieldName: 'timeField3' } as DataView, }; -const getDataview = (id: string): DataView | undefined => dataViewsMap[id]; -jest.mock('../services', () => { - return { - getDataViewsStart: jest.fn(() => { - return { - getDefault: jest.fn(() => { - return { id: '12345', title: 'default', timeFieldName: '@timestamp' }; - }), - get: getDataview, - }; - }), - }; -}); +const getDataview = async (id: string): Promise => dataViewsMap[id]; describe('getDataSourceInfo', () => { + let dataViews: DataViewsPublicPluginStart; + beforeAll(() => { + dataViews = { + getDefault: jest.fn(async () => { + return { id: '12345', title: 'default', timeFieldName: '@timestamp' }; + }), + get: getDataview, + } as unknown as DataViewsPublicPluginStart; + }); + test('should return the default dataview if model_indexpattern is string', async () => { const { indexPatternId, timeField } = await getDataSourceInfo( 'test', undefined, false, - undefined + undefined, + dataViews ); expect(indexPatternId).toBe('12345'); expect(timeField).toBe('@timestamp'); @@ -48,7 +49,8 @@ describe('getDataSourceInfo', () => { { id: 'dataview-1-id' }, 'timeField-1', false, - undefined + undefined, + dataViews ); expect(indexPatternId).toBe('dataview-1-id'); expect(timeField).toBe('timeField-1'); @@ -59,7 +61,8 @@ describe('getDataSourceInfo', () => { { id: 'dataview-1-id' }, 'timeField-1', true, - { id: 'test2' } + { id: 'test2' }, + dataViews ); expect(indexPatternId).toBe('test2'); expect(timeField).toBe('timeField2'); diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.ts similarity index 78% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.ts index b5c9addd81435..8377ddafe1926 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_datasource_info.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_datasource_info.ts @@ -5,17 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { fetchIndexPattern, isStringTypeIndexPattern } from '../../common/index_patterns_utils'; -import type { IndexPatternValue } from '../../common/types'; -import { getDataViewsStart } from '../services'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { + fetchIndexPattern, + isStringTypeIndexPattern, +} from '../../../../common/index_patterns_utils'; +import type { IndexPatternValue } from '../../../../common/types'; export const getDataSourceInfo = async ( modelIndexPattern: IndexPatternValue, modelTimeField: string | undefined, isOverwritten: boolean, - overwrittenIndexPattern: IndexPatternValue | undefined + overwrittenIndexPattern: IndexPatternValue | undefined, + dataViews: DataViewsPublicPluginStart ) => { - const dataViews = getDataViewsStart(); let indexPatternId = modelIndexPattern && !isStringTypeIndexPattern(modelIndexPattern) ? modelIndexPattern.id : ''; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_field_type.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_field_type.ts similarity index 62% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_field_type.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_field_type.ts index 9e508f895e914..07ab80fd33ec1 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_field_type.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/get_field_type.ts @@ -5,11 +5,14 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { getDataViewsStart } from '../services'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -export const getFieldType = async (indexPatternId: string, fieldName: string) => { - const dataViews = getDataViewsStart(); +export const getFieldType = async ( + indexPatternId: string, + fieldName: string, + dataViews: DataViewsPublicPluginStart +) => { const dataView = await dataViews.get(indexPatternId); - const field = await dataView.getFieldByName(fieldName); + const field = dataView.getFieldByName(fieldName); return field?.type; }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/index.ts new file mode 100644 index 0000000000000..befb8af63a745 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/datasource/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './get_datasource_info'; +export * from './get_field_type'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/index.ts new file mode 100644 index 0000000000000..04d55dd0ba52e --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { SUPPORTED_FORMATTERS } from './supported_formatters'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/supported_formatters.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/supported_formatters.ts new file mode 100644 index 0000000000000..8c92686933de7 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/formatters/supported_formatters.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const SUPPORTED_FORMATTERS = ['bytes', 'percent', 'number']; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts new file mode 100644 index 0000000000000..8483c6f2c3191 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { PaletteOutput } from '@kbn/coloring'; +import { SUPPORTED_FORMATTERS } from '../formatters'; +import { convertSplitFilters } from '../split_chart'; +import { convertMetrics, convertFilter } from '../metrics'; +import type { Panel, Series } from '../../../../common/types'; +import { VisSeries } from '../series'; + +function getTermParams(layer: Series) { + return { + size: layer.terms_size ?? 10, + ...(layer.terms_include && { include: [layer.terms_include] }), + includeIsRegex: Boolean(layer.terms_include), + ...(layer.terms_exclude && { exclude: [layer.terms_exclude] }), + excludeIsRegex: Boolean(layer.terms_exclude), + otherBucket: false, + orderDirection: layer.terms_direction ?? 'desc', + orderBy: layer.terms_order_by === '_key' ? { type: 'alphabetical' } : { type: 'column' }, + parentFormat: { id: 'terms' }, + }; +} + +function getPalette(palette: PaletteOutput): PaletteOutput { + return !palette || palette.name === 'gradient' || palette.name === 'rainbow' + ? { name: 'default', type: 'palette' } + : palette; +} + +export const getLayerConfiguration = ( + indexPatternId: string, + layerIdx: number, + chartType: string, + model: Panel, + series: VisSeries, + splitFields: string[], + timeField?: string, + splitWithDateHistogram?: boolean +): VisualizeEditorLayersContext => { + const layer = model.series[layerIdx]; + const palette = layer.palette as PaletteOutput; + const splitFilters = convertSplitFilters(layer); + const { metrics: metricsArray, seriesAgg } = series; + const filter = convertFilter(layer); + const metrics = convertMetrics(layer, metricsArray, filter); + return { + indexPatternId, + timeFieldName: timeField, + chartType, + axisPosition: layer.separate_axis ? layer.axis_position : model.axis_position, + ...(layer.terms_field && { splitFields }), + splitWithDateHistogram, + ...(layer.split_mode !== 'everything' && { splitMode: layer.split_mode }), + ...(splitFilters.length > 0 && { splitFilters }), + // for non supported palettes, we will use the default palette + palette: getPalette(palette), + ...(layer.split_mode === 'terms' && { + termsParams: getTermParams(layer), + }), + collapseFn: seriesAgg, + metrics, + timeInterval: model.interval && !model.interval?.includes('=') ? model.interval : 'auto', + ...(SUPPORTED_FORMATTERS.includes(layer.formatter) && { format: layer.formatter }), + ...(layer.label && { label: layer.label }), + dropPartialBuckets: layer.override_index_pattern + ? layer.series_drop_last_bucket > 0 + : model.drop_last_bucket > 0, + }; +}; diff --git a/packages/kbn-dev-utils/src/vscode_config/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/index.ts similarity index 88% rename from packages/kbn-dev-utils/src/vscode_config/index.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/index.ts index 1b08881273edc..eaef2f964f94a 100644 --- a/packages/kbn-dev-utils/src/vscode_config/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export * from './update_vscode_config_cli'; +export * from './get_layer'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.test.ts new file mode 100644 index 0000000000000..1d5ae66ce958b --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.test.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 type { Metric } from '../../../../common/types'; +import { getFilterRatioFormula } from './filter_ratio_formula'; + +describe('getFilterRatioFormula', () => { + test('should return correct formula for filter ratio', () => { + const metric = { + id: '12345', + type: 'filter_ratio', + field: 'day_of_week_i', + numerator: { + query: 'category.keyword : "Men\'s Clothing" ', + language: 'kuery', + }, + denominator: { + query: 'customer_gender : "FEMALE" ', + language: 'kuery', + }, + } as Metric; + const formula = getFilterRatioFormula(metric); + expect(formula).toStrictEqual( + "count(kql='category.keyword : \"Men\\'s Clothing\" ') / count(kql='customer_gender : \"FEMALE\" ')" + ); + }); + + test('should return correct formula for positive rate', () => { + const metric = { + id: '12345', + type: 'filter_ratio', + field: 'day_of_week_i', + numerator: { + query: 'category.keyword : "Men\'s Clothing" ', + language: 'kuery', + }, + denominator: { + query: 'customer_gender : "FEMALE" ', + language: 'kuery', + }, + metric_agg: 'positive_rate', + } as Metric; + const formula = getFilterRatioFormula(metric); + expect(formula).toStrictEqual( + "counter_rate(max('day_of_week_i',kql='category.keyword : \"Men\\'s Clothing\" ')) / counter_rate(max('day_of_week_i',kql='customer_gender : \"FEMALE\" '))" + ); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts new file mode 100644 index 0000000000000..a132b861889fa --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.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 type { Query } from '@kbn/es-query'; +import type { Metric } from '../../../../common/types'; +import { SUPPORTED_METRICS } from './supported_metrics'; + +const escapeQuotes = (str: string) => { + return str?.replace(/'/g, "\\'"); +}; + +const constructFilterRationFormula = (operation: string, metric?: Query) => { + return `${operation}${metric?.language === 'lucene' ? 'lucene' : 'kql'}='${ + metric?.query && typeof metric?.query === 'string' + ? escapeQuotes(metric?.query) + : metric?.query ?? '*' + }')`; +}; + +export const getFilterRatioFormula = (currentMetric: Metric) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { numerator, denominator, metric_agg, field } = currentMetric; + let aggregation = SUPPORTED_METRICS.count; + if (metric_agg) { + aggregation = SUPPORTED_METRICS[metric_agg]; + if (!aggregation) { + return null; + } + } + const operation = + metric_agg && metric_agg !== 'count' ? `${aggregation.name}('${field}',` : 'count('; + + if (aggregation.name === 'counter_rate') { + const numeratorFormula = constructFilterRationFormula( + `${aggregation.name}(max('${field}',`, + numerator + ); + const denominatorFormula = constructFilterRationFormula( + `${aggregation.name}(max('${field}',`, + denominator + ); + return `${numeratorFormula}) / ${denominatorFormula})`; + } else { + const numeratorFormula = constructFilterRationFormula(operation, numerator); + const denominatorFormula = constructFilterRationFormula(operation, denominator); + return `${numeratorFormula} / ${denominatorFormula}`; + } +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts new file mode 100644 index 0000000000000..9a79fb804e3ba --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/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 * from './supported_metrics'; +export * from './metrics_helpers'; +export * from './metrics_converter'; +export * from './parent_pipeline_formula'; +export * from './sibling_pipeline_formula'; +export * from './filter_ratio_formula'; +export * from './parent_pipeline_series'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts new file mode 100644 index 0000000000000..65427bfa2a268 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { Series } from '../../../../common/types'; +import { Filter } from '../../types'; + +export const convertFilter = (series: Series): Filter | void => { + if (!series.filter) { + return; + } + + if (series.filter.language === 'kuery') { + return { kql: series.filter.query }; + } + + if (series.filter.language === 'lucene') { + return { lucene: series.filter.query }; + } +}; + +const convertMetric = ( + series: Series, + metric: VisualizeEditorLayersContext['metrics'][number], + filter: Filter | void +) => ({ + ...metric, + color: metric.color ?? series.color, + params: { + ...metric.params, + ...(series.offset_time && { shift: series.offset_time }), + ...(filter && filter), + }, +}); + +export const convertMetrics = ( + series: Series, + metrics: VisualizeEditorLayersContext['metrics'], + filter: Filter | void +) => metrics.map((metric) => convertMetric(series, metric, filter)); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts new file mode 100644 index 0000000000000..83922a54aa111 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { Metric } from '../../../../common/types'; +import { getPercentilesSeries, getPercentileRankSeries } from './metrics_helpers'; + +describe('getPercentilesSeries', () => { + test('should return correct config for multiple percentiles', () => { + const percentiles = [ + { + color: '#68BC00', + id: 'aef159f0-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + shade: 0.2, + value: 50, + }, + { + color: 'rgba(0,63,188,1)', + id: 'b0e0a6d0-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + percentile: '', + shade: 0.2, + value: '70', + }, + { + color: 'rgba(188,38,0,1)', + id: 'b2e04760-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + percentile: '', + shade: 0.2, + value: '80', + }, + { + color: 'rgba(188,0,3,1)', + id: 'b503eab0-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + percentile: '', + shade: 0.2, + value: '90', + }, + ] as Metric['percentiles']; + const config = getPercentilesSeries(percentiles, 'bytes'); + expect(config).toStrictEqual([ + { + agg: 'percentile', + color: '#68BC00', + fieldName: 'bytes', + isFullReference: false, + params: { percentile: 50 }, + }, + { + agg: 'percentile', + color: 'rgba(0,63,188,1)', + fieldName: 'bytes', + isFullReference: false, + params: { percentile: '70' }, + }, + { + agg: 'percentile', + color: 'rgba(188,38,0,1)', + fieldName: 'bytes', + isFullReference: false, + params: { percentile: '80' }, + }, + { + agg: 'percentile', + color: 'rgba(188,0,3,1)', + fieldName: 'bytes', + isFullReference: false, + params: { percentile: '90' }, + }, + ]); + }); +}); + +describe('getPercentileRankSeries', () => { + test('should return correct config for multiple percentile ranks', () => { + const values = ['1', '5', '7'] as Metric['values']; + const colors = ['#68BC00', 'rgba(0,63,188,1)', 'rgba(188,38,0,1)'] as Metric['colors']; + const config = getPercentileRankSeries(values, colors, 'day_of_week_i'); + expect(config).toStrictEqual([ + { + agg: 'percentile_rank', + color: '#68BC00', + fieldName: 'day_of_week_i', + isFullReference: false, + params: { value: '1' }, + }, + { + agg: 'percentile_rank', + color: 'rgba(0,63,188,1)', + fieldName: 'day_of_week_i', + isFullReference: false, + params: { value: '5' }, + }, + { + agg: 'percentile_rank', + color: 'rgba(188,38,0,1)', + fieldName: 'day_of_week_i', + isFullReference: false, + params: { value: '7' }, + }, + ]); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts new file mode 100644 index 0000000000000..fdc7f4ca2f6d0 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Metric } from '../../../../common/types'; +import { SUPPORTED_METRICS } from './supported_metrics'; +import { getFilterRatioFormula } from './filter_ratio_formula'; +import { getParentPipelineSeriesFormula } from './parent_pipeline_formula'; +import { getSiblingPipelineSeriesFormula } from './sibling_pipeline_formula'; + +export const getPercentilesSeries = (percentiles: Metric['percentiles'], fieldName?: string) => { + return percentiles?.map((percentile) => { + return { + agg: 'percentile', + isFullReference: false, + color: percentile.color, + fieldName: fieldName ?? 'document', + params: { percentile: percentile.value }, + }; + }); +}; + +export const getPercentileRankSeries = ( + values: Metric['values'], + colors: Metric['colors'], + fieldName?: string +) => { + return values?.map((value, index) => { + return { + agg: 'percentile_rank', + isFullReference: false, + color: colors?.[index], + fieldName: fieldName ?? 'document', + params: { value }, + }; + }); +}; + +export const getTimeScale = (metric: Metric) => { + const supportedTimeScales = ['1s', '1m', '1h', '1d']; + let timeScale; + if (metric.unit && supportedTimeScales.includes(metric.unit)) { + timeScale = metric.unit.replace('1', ''); + } + return timeScale; +}; + +export const getFormulaSeries = (script: string) => { + return [ + { + agg: 'formula', + isFullReference: true, + fieldName: 'document', + params: { formula: script }, + }, + ]; +}; + +export const getPipelineAgg = (subFunctionMetric: Metric) => { + const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; + if (!pipelineAggMap) { + return null; + } + return pipelineAggMap.name; +}; + +export const getFormulaEquivalent = ( + currentMetric: Metric, + metrics: Metric[], + metaValue?: number +) => { + const aggregation = SUPPORTED_METRICS[currentMetric.type]?.name; + switch (currentMetric.type) { + case 'avg_bucket': + case 'max_bucket': + case 'min_bucket': + case 'sum_bucket': + case 'positive_only': { + return getSiblingPipelineSeriesFormula(currentMetric.type, currentMetric, metrics); + } + case 'count': { + return `${aggregation}()`; + } + case 'percentile': { + return `${aggregation}(${currentMetric.field}${ + metaValue ? `, percentile=${metaValue}` : '' + })`; + } + case 'percentile_rank': { + return `${aggregation}(${currentMetric.field}${metaValue ? `, value=${metaValue}` : ''})`; + } + case 'cumulative_sum': + case 'derivative': + case 'moving_average': { + const [fieldId, _] = currentMetric?.field?.split('[') ?? []; + const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); + if (!subFunctionMetric) { + return null; + } + const pipelineAgg = getPipelineAgg(subFunctionMetric); + if (!pipelineAgg) { + return null; + } + return getParentPipelineSeriesFormula( + metrics, + subFunctionMetric, + pipelineAgg, + currentMetric.type, + metaValue + ); + } + case 'positive_rate': { + return `${aggregation}(max(${currentMetric.field}))`; + } + case 'filter_ratio': { + return getFilterRatioFormula(currentMetric); + } + case 'static': { + return `${currentMetric.value}`; + } + default: { + return `${aggregation}(${currentMetric.field})`; + } + } +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts new file mode 100644 index 0000000000000..e00fe505df1fd --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { Metric, MetricType } from '../../../../common/types'; +import { SUPPORTED_METRICS } from './supported_metrics'; +import { getFilterRatioFormula } from './filter_ratio_formula'; + +export const getParentPipelineSeriesFormula = ( + metrics: Metric[], + subFunctionMetric: Metric, + pipelineAgg: string, + aggregation: MetricType, + percentileValue?: number +) => { + let formula = ''; + const aggregationMap = SUPPORTED_METRICS[aggregation]; + const subMetricField = subFunctionMetric.field; + const [nestedFieldId, nestedMeta] = subMetricField?.split('[') ?? []; + // support nested aggs + const additionalSubFunction = metrics.find((metric) => metric.id === nestedFieldId); + if (additionalSubFunction) { + // support nested aggs with formula + const additionalPipelineAggMap = SUPPORTED_METRICS[additionalSubFunction.type]; + if (!additionalPipelineAggMap) { + return null; + } + const nestedMetaValue = Number(nestedMeta?.replace(']', '')); + const aggMap = SUPPORTED_METRICS[aggregation]; + let additionalFunctionArgs; + if (additionalPipelineAggMap.name === 'percentile' && nestedMetaValue) { + additionalFunctionArgs = `, percentile=${nestedMetaValue}`; + } + if (additionalPipelineAggMap.name === 'percentile_rank' && nestedMetaValue) { + additionalFunctionArgs = `, value=${nestedMetaValue}`; + } + formula = `${aggMap.name}(${pipelineAgg}(${additionalPipelineAggMap.name}(${ + additionalSubFunction.field ?? '' + }${additionalFunctionArgs ?? ''})))`; + } else { + let additionalFunctionArgs; + if (pipelineAgg === 'percentile' && percentileValue) { + additionalFunctionArgs = `, percentile=${percentileValue}`; + } + if (pipelineAgg === 'percentile_rank' && percentileValue) { + additionalFunctionArgs = `, value=${percentileValue}`; + } + if (pipelineAgg === 'filter_ratio') { + const script = getFilterRatioFormula(subFunctionMetric); + if (!script) { + return null; + } + formula = `${aggregationMap.name}(${script}${additionalFunctionArgs ?? ''})`; + } else if (pipelineAgg === 'counter_rate') { + formula = `${aggregationMap.name}(${pipelineAgg}(max(${subFunctionMetric.field}${ + additionalFunctionArgs ? `${additionalFunctionArgs}` : '' + })))`; + } else { + formula = `${aggregationMap.name}(${pipelineAgg}(${subFunctionMetric.field}${ + additionalFunctionArgs ? `${additionalFunctionArgs}` : '' + }))`; + } + } + return formula; +}; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts similarity index 61% rename from src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.test.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts index 292515adf21e6..40264b89491b7 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts @@ -5,113 +5,10 @@ * 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 type { Metric, MetricType } from '../../common/types'; -import { - getPercentilesSeries, - getPercentileRankSeries, - getParentPipelineSeries, -} from './metrics_helpers'; -describe('getPercentilesSeries', () => { - test('should return correct config for multiple percentiles', () => { - const percentiles = [ - { - color: '#68BC00', - id: 'aef159f0-7db8-11ec-9d0c-e57521cec076', - mode: 'line', - shade: 0.2, - value: 50, - }, - { - color: 'rgba(0,63,188,1)', - id: 'b0e0a6d0-7db8-11ec-9d0c-e57521cec076', - mode: 'line', - percentile: '', - shade: 0.2, - value: '70', - }, - { - color: 'rgba(188,38,0,1)', - id: 'b2e04760-7db8-11ec-9d0c-e57521cec076', - mode: 'line', - percentile: '', - shade: 0.2, - value: '80', - }, - { - color: 'rgba(188,0,3,1)', - id: 'b503eab0-7db8-11ec-9d0c-e57521cec076', - mode: 'line', - percentile: '', - shade: 0.2, - value: '90', - }, - ] as Metric['percentiles']; - const config = getPercentilesSeries(percentiles, 'bytes'); - expect(config).toStrictEqual([ - { - agg: 'percentile', - color: '#68BC00', - fieldName: 'bytes', - isFullReference: false, - params: { percentile: 50 }, - }, - { - agg: 'percentile', - color: 'rgba(0,63,188,1)', - fieldName: 'bytes', - isFullReference: false, - params: { percentile: '70' }, - }, - { - agg: 'percentile', - color: 'rgba(188,38,0,1)', - fieldName: 'bytes', - isFullReference: false, - params: { percentile: '80' }, - }, - { - agg: 'percentile', - color: 'rgba(188,0,3,1)', - fieldName: 'bytes', - isFullReference: false, - params: { percentile: '90' }, - }, - ]); - }); -}); - -describe('getPercentileRankSeries', () => { - test('should return correct config for multiple percentile ranks', () => { - const values = ['1', '5', '7'] as Metric['values']; - const colors = ['#68BC00', 'rgba(0,63,188,1)', 'rgba(188,38,0,1)'] as Metric['colors']; - const config = getPercentileRankSeries(values, colors, 'day_of_week_i'); - expect(config).toStrictEqual([ - { - agg: 'percentile_rank', - color: '#68BC00', - fieldName: 'day_of_week_i', - isFullReference: false, - params: { value: '1' }, - }, - { - agg: 'percentile_rank', - color: 'rgba(0,63,188,1)', - fieldName: 'day_of_week_i', - isFullReference: false, - params: { value: '5' }, - }, - { - agg: 'percentile_rank', - color: 'rgba(188,38,0,1)', - fieldName: 'day_of_week_i', - isFullReference: false, - params: { value: '7' }, - }, - ]); - }); -}); +import { METRIC_TYPES } from '@kbn/data-plugin/public'; +import type { Metric, MetricType } from '../../../../common/types'; +import { getParentPipelineSeries } from './parent_pipeline_series'; describe('getParentPipelineSeries', () => { test('should return correct config for pipeline agg on percentiles', () => { diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts new file mode 100644 index 0000000000000..6a263ca8bb44d --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { Metric, MetricType } from '../../../../common/types'; +import { SUPPORTED_METRICS } from './supported_metrics'; +import { getParentPipelineSeriesFormula } from './parent_pipeline_formula'; +import { getFilterRatioFormula } from './filter_ratio_formula'; +import { getFormulaSeries, getTimeScale, getPipelineAgg } from './metrics_helpers'; + +export const computeParentSeries = ( + aggregation: MetricType, + currentMetric: Metric, + subFunctionMetric: Metric, + pipelineAgg: string, + meta?: number +) => { + const aggregationMap = SUPPORTED_METRICS[aggregation]; + if (subFunctionMetric.type === 'filter_ratio') { + const script = getFilterRatioFormula(subFunctionMetric); + if (!script) { + return null; + } + const formula = `${aggregationMap.name}(${script})`; + return getFormulaSeries(formula); + } + const timeScale = getTimeScale(currentMetric); + return [ + { + agg: aggregationMap.name, + isFullReference: aggregationMap.isFullReference, + pipelineAggType: pipelineAgg, + fieldName: + subFunctionMetric?.field && pipelineAgg !== 'count' ? subFunctionMetric?.field : 'document', + params: { + ...(currentMetric.window && { window: currentMetric.window }), + ...(timeScale && { timeScale }), + ...(pipelineAgg === 'percentile' && meta && { percentile: meta }), + ...(pipelineAgg === 'percentile_rank' && meta && { value: meta }), + }, + }, + ]; +}; + +export const getParentPipelineSeries = ( + aggregation: MetricType, + currentMetricIdx: number, + metrics: Metric[] +) => { + const currentMetric = metrics[currentMetricIdx]; + // percentile value is derived from the field Id. It has the format xxx-xxx-xxx-xxx[percentile] + const [fieldId, meta] = currentMetric?.field?.split('[') ?? []; + const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); + if (!subFunctionMetric || subFunctionMetric.type === 'static') { + return null; + } + const pipelineAgg = getPipelineAgg(subFunctionMetric); + if (!pipelineAgg) { + return null; + } + const metaValue = Number(meta?.replace(']', '')); + const subMetricField = subFunctionMetric.field; + const [nestedFieldId, _] = subMetricField?.split('[') ?? []; + // support nested aggs with formula + const additionalSubFunction = metrics.find((metric) => metric.id === nestedFieldId); + if (additionalSubFunction) { + const formula = getParentPipelineSeriesFormula( + metrics, + subFunctionMetric, + pipelineAgg, + aggregation, + metaValue + ); + if (!formula) { + return null; + } + return getFormulaSeries(formula); + } else { + return computeParentSeries( + aggregation, + currentMetric, + subFunctionMetric, + pipelineAgg, + metaValue + ); + } +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.test.ts new file mode 100644 index 0000000000000..c251f72c84a9f --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.test.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/public'; +import type { Metric } from '../../../../common/types'; +import { TSVB_METRIC_TYPES } from '../../../../common/enums'; +import { getSiblingPipelineSeriesFormula } from './sibling_pipeline_formula'; + +describe('getSiblingPipelineSeriesFormula', () => { + test('should return correct formula for sibling pipeline agg on positive only', () => { + const metrics = [ + { + field: 'day_of_week_i', + id: '123456', + type: 'max', + }, + { + id: '891011', + type: 'positive_only', + field: '123456', + }, + ] as Metric[]; + const formula = getSiblingPipelineSeriesFormula( + TSVB_METRIC_TYPES.POSITIVE_ONLY, + metrics[1], + metrics + ); + expect(formula).toStrictEqual('pick_max(max(day_of_week_i), 0)'); + }); + + test('should return correct config for sibling pipeline agg on percentile ranks', () => { + const metrics = [ + { + field: 'AvgTicketPrice', + id: '04558549-f19f-4a87-9923-27df8b81af3e', + values: ['400', '500', '700'], + colors: ['rgba(211,96,134,1)', 'rgba(155,33,230,1)', '#68BC00'], + type: 'percentile_rank', + }, + { + field: '04558549-f19f-4a87-9923-27df8b81af3e[400.0]', + id: '764f4110-7db9-11ec-9fdf-91a8881dd06b', + type: 'avg_bucket', + }, + ] as Metric[]; + const formula = getSiblingPipelineSeriesFormula(METRIC_TYPES.AVG_BUCKET, metrics[1], metrics); + expect(formula).toStrictEqual('overall_average(percentile_rank(AvgTicketPrice, value=400))'); + }); + + test('should return correct config for sibling pipeline agg on percentile', () => { + const metrics = [ + { + field: 'AvgTicketPrice', + id: '04558549-f19f-4a87-9923-27df8b81af3e', + percentiles: [ + { + color: '#68BC00', + id: 'aef159f0-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + shade: 0.2, + value: 50, + }, + { + color: 'rgba(0,63,188,1)', + id: 'b0e0a6d0-7db8-11ec-9d0c-e57521cec076', + mode: 'line', + percentile: '', + shade: 0.2, + value: '70', + }, + ], + type: 'percentile', + }, + { + field: '04558549-f19f-4a87-9923-27df8b81af3e[70.0]', + id: '764f4110-7db9-11ec-9fdf-91a8881dd06b', + type: 'avg_bucket', + }, + ] as Metric[]; + const formula = getSiblingPipelineSeriesFormula(METRIC_TYPES.AVG_BUCKET, metrics[1], metrics); + expect(formula).toStrictEqual('overall_average(percentile(AvgTicketPrice, percentile=70))'); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts new file mode 100644 index 0000000000000..b3e141b11e598 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 { Metric, MetricType } from '../../../../common/types'; +import { SUPPORTED_METRICS } from './supported_metrics'; + +export const getSiblingPipelineSeriesFormula = ( + aggregation: MetricType, + currentMetric: Metric, + metrics: Metric[] +) => { + const [nestedFieldId, nestedMeta] = currentMetric.field?.split('[') ?? []; + const subFunctionMetric = metrics.find((metric) => metric.id === nestedFieldId); + if (!subFunctionMetric || subFunctionMetric.type === 'static') { + return null; + } + const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; + if (!pipelineAggMap) { + return null; + } + const aggregationMap = SUPPORTED_METRICS[aggregation]; + const subMetricField = subFunctionMetric.type !== 'count' ? subFunctionMetric.field : ''; + // support nested aggs with formula + const additionalSubFunction = metrics.find((metric) => metric.id === subMetricField); + let formula = `${aggregationMap.name}(`; + let minimumValue = ''; + if (currentMetric.type === 'positive_only') { + minimumValue = `, 0`; + } + if (additionalSubFunction) { + const additionalPipelineAggMap = SUPPORTED_METRICS[additionalSubFunction.type]; + if (!additionalPipelineAggMap) { + return null; + } + const additionalSubFunctionField = + additionalSubFunction.type !== 'count' ? additionalSubFunction.field : ''; + formula += `${pipelineAggMap.name}(${additionalPipelineAggMap.name}(${ + additionalSubFunctionField ?? '' + }))${minimumValue})`; + } else { + let additionalFunctionArgs; + // handle percentile and percentile_rank + const nestedMetaValue = Number(nestedMeta?.replace(']', '')); + if (pipelineAggMap.name === 'percentile' && nestedMetaValue) { + additionalFunctionArgs = `, percentile=${nestedMetaValue}`; + } + if (pipelineAggMap.name === 'percentile_rank' && nestedMetaValue) { + additionalFunctionArgs = `, value=${nestedMetaValue}`; + } + formula += `${pipelineAggMap.name}(${subMetricField ?? ''}${ + additionalFunctionArgs ? `${additionalFunctionArgs}` : '' + })${minimumValue})`; + } + return formula; +}; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts similarity index 100% rename from src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts similarity index 99% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts index fe51f034f70c8..aeb401e86dd01 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { Metric } from '../../common/types'; +import type { Metric } from '../../../../common/types'; import { getSeries } from './get_series'; describe('getSeries', () => { diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts similarity index 96% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts index 74cbe3337af48..0db270b465719 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ import type { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; -import type { Metric } from '../../common/types'; -import { SUPPORTED_METRICS } from './supported_metrics'; +import type { Metric } from '../../../../common/types'; import { getSeriesAgg } from './get_series_agg'; import { + SUPPORTED_METRICS, getPercentilesSeries, getPercentileRankSeries, getFormulaSeries, @@ -21,12 +21,14 @@ import { getParentPipelineSeriesFormula, getFilterRatioFormula, getTimeScale, -} from './metrics_helpers'; +} from '../metrics'; -export const getSeries = ( - initialMetrics: Metric[], - totalSeriesNum: number -): { metrics: VisualizeEditorLayersContext['metrics']; seriesAgg?: string } | null => { +export interface VisSeries { + metrics: VisualizeEditorLayersContext['metrics']; + seriesAgg?: string; +} + +export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): VisSeries | null => { const { metrics, seriesAgg } = getSeriesAgg(initialMetrics); const metricIdx = metrics.length - 1; const aggregation = metrics[metricIdx].type; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_series_agg.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series_agg.ts similarity index 93% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_series_agg.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series_agg.ts index 07bfd071acbaa..030bcd887f9cb 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_series_agg.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series_agg.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { Metric } from '../../common/types'; +import type { Metric } from '../../../../common/types'; const functionMap: Partial> = { mean: 'avg', diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/index.ts new file mode 100644 index 0000000000000..6c111d50493e6 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './get_series'; +export * from './get_series_agg'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/date_histogram.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/date_histogram.ts new file mode 100644 index 0000000000000..bad2e476ecc85 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/date_histogram.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { Series } from '../../../../common/types'; +import { getFieldType } from '../datasource'; + +export const isSplitWithDateHistogram = async ( + series: Series, + splitFields: string[], + indexPatternId: string, + dataViews: DataViewsPublicPluginStart +) => { + let splitWithDateHistogram = false; + if (series.terms_field && series.split_mode === 'terms' && splitFields) { + for (const f of splitFields) { + const fieldType = await getFieldType(indexPatternId, f, dataViews); + + if (fieldType === 'date') { + if (splitFields.length === 1) { + splitWithDateHistogram = true; + } else { + return null; + } + } + } + } + return splitWithDateHistogram; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/filters.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/filters.ts new file mode 100644 index 0000000000000..3a22b3b70294f --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/filters.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { Series } from '../../../../common/types'; + +export const convertSplitFilters = ( + series: Series +): Exclude => { + const splitFilters = []; + if (series.split_mode === 'filter' && series.filter) { + splitFilters.push({ filter: series.filter }); + } + if (series.split_filters) { + splitFilters.push(...series.split_filters); + } + return splitFilters; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/index.ts new file mode 100644 index 0000000000000..109178cf4d959 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/split_chart/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './date_histogram'; +export * from './filters'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/chart_type.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/chart_type.ts new file mode 100644 index 0000000000000..f389568eed668 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/chart_type.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Series } from '../../../../common/types'; + +export const convertChartType = (series: Series) => { + const layerChartType = + series.chart_type === 'line' && Number(series.fill) > 0 ? 'area' : series.chart_type; + + if (series.stacked !== 'none' && series.stacked !== 'percent') { + return layerChartType !== 'line' ? `${layerChartType}_stacked` : 'line'; + } + if (series.stacked === 'percent') { + return layerChartType !== 'line' ? `${layerChartType}_percentage_stacked` : 'line'; + } + + return layerChartType; +}; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_extents.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.test.ts similarity index 97% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_extents.test.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.test.ts index 67ee8a1fb290c..e07aff3d3427f 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_extents.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.test.ts @@ -5,8 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { Panel } from '../../common/types'; -import { getYExtents } from './get_extents'; +import type { Panel } from '../../../../common/types'; +import { getYExtents } from './extents'; const model = { axis_position: 'left', diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_extents.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.ts similarity index 98% rename from src/plugins/vis_types/timeseries/public/trigger_action/get_extents.ts rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.ts index a0587671c7686..5e1b0ac4cac8e 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_extents.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/extents.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Panel, Series } from '../../common/types'; +import type { Panel, Series } from '../../../../common/types'; const lowerBoundShouldBeZero = ( lowerBound: number | null, diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/index.ts new file mode 100644 index 0000000000000..08adee9f984a0 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/xy/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './extents'; +export * from './chart_type'; 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 new file mode 100644 index 0000000000000..2996ad19c42d9 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.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 { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { getDataViewsStart } from '../../services'; +import { getDataSourceInfo } from '../lib/datasource'; +import { getSeries } from '../lib/series'; +import { getFieldsForTerms } from '../../../common/fields_utils'; +import { ConvertTsvbToLensVisualization } from '../types'; +import { convertChartType, getYExtents } from '../lib/xy'; +import { getLayerConfiguration } from '../lib/layers'; +import { isSplitWithDateHistogram } from '../lib/split_chart'; + +export const convertToLens: ConvertTsvbToLensVisualization = async (model) => { + const layersConfiguration: { [key: string]: VisualizeEditorLayersContext } = {}; + + // get the active series number + const seriesNum = model.series.filter((series) => !series.hidden).length; + const dataViews = getDataViewsStart(); + + // handle multiple layers/series + for (let layerIdx = 0; layerIdx < model.series.length; layerIdx++) { + const layer = model.series[layerIdx]; + if (layer.hidden) { + continue; + } + + const { indexPatternId, timeField } = await getDataSourceInfo( + model.index_pattern, + model.time_field, + Boolean(layer.override_index_pattern), + layer.series_index_pattern, + dataViews + ); + + // handle multiple metrics + const series = getSeries(layer.metrics, seriesNum); + if (!series || !series.metrics) { + return null; + } + + const splitFields = getFieldsForTerms(layer.terms_field); + + // in case of terms in a date field, we want to apply the date_histogram + const splitWithDateHistogram = await isSplitWithDateHistogram( + layer, + splitFields, + indexPatternId, + dataViews + ); + + if (splitWithDateHistogram === null) { + return null; + } + + const chartType = convertChartType(layer); + + layersConfiguration[layerIdx] = getLayerConfiguration( + indexPatternId, + layerIdx, + chartType, + model, + series, + splitFields, + timeField, + splitWithDateHistogram + ); + } + + const extents = getYExtents(model); + + return { + layers: layersConfiguration, + type: 'lnsXY', + configuration: { + fill: model.series[0].fill ?? 0.3, + legend: { + isVisible: Boolean(model.show_legend), + showSingleSeries: Boolean(model.show_legend), + position: model.legend_position ?? 'right', + shouldTruncate: Boolean(model.truncate_legend), + maxLines: model.max_lines_legend ?? 1, + }, + gridLinesVisibility: { + x: Boolean(model.show_grid), + yLeft: Boolean(model.show_grid), + yRight: Boolean(model.show_grid), + }, + extents, + }, + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts new file mode 100644 index 0000000000000..8c17b9ec6ce57 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { NavigateToLensContext } from '@kbn/visualizations-plugin/public'; +import type { Panel } from '../../common/types'; + +export type ConvertTsvbToLensVisualization = ( + model: Panel +) => Promise; + +export interface Filter { + kql?: string | { [key: string]: any } | undefined; + lucene?: string | { [key: string]: any } | undefined; +} diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index c013553e44197..10658b264c4a3 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -28,6 +28,7 @@ import { toExpressionAst } from './to_ast'; import { getDataViewsStart } from './services'; import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types'; import type { IndexPatternValue, Panel } from '../common/types'; +import { convertTSVBtoLensConfiguration } from './convert_to_lens'; export const withReplacedIds = ( vis: Vis @@ -167,14 +168,9 @@ export const metricsVisDefinition: VisTypeDefinition< } return []; }, - navigateToLens: async (params?: VisParams) => { - const { triggerTSVBtoLensConfiguration } = await import('./trigger_action'); + navigateToLens: async (params?: VisParams) => + params ? await convertTSVBtoLensConfiguration(params as Panel) : null, - const triggerConfiguration = params - ? await triggerTSVBtoLensConfiguration(params as Panel) - : null; - return triggerConfiguration; - }, inspectorAdapters: () => ({ requests: new RequestAdapter(), }), diff --git a/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx index c6a255164fb60..60b651f2df21b 100644 --- a/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx @@ -65,8 +65,8 @@ export const getTimeseriesVisRenderer: (deps: { const { visParams: model, visData, syncColors, syncTooltips } = config; const showNoResult = !checkIfDataExists(visData, model); - const { triggerTSVBtoLensConfiguration } = await import('./trigger_action'); - const canNavigateToLens = await triggerTSVBtoLensConfiguration(model); + const { convertTSVBtoLensConfiguration } = await import('./convert_to_lens'); + const canNavigateToLens = await convertTSVBtoLensConfiguration(model); const renderComplete = () => { const usageCollection = getUsageCollectionStart(); diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts b/src/plugins/vis_types/timeseries/public/trigger_action/index.ts deleted file mode 100644 index f384bc6d6562c..0000000000000 --- a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 { PaletteOutput } from '@kbn/coloring'; -import type { - NavigateToLensContext, - VisualizeEditorLayersContext, -} from '@kbn/visualizations-plugin/public'; -import type { Panel } from '../../common/types'; -import { PANEL_TYPES } from '../../common/enums'; -import { getDataSourceInfo } from './get_datasource_info'; -import { getFieldType } from './get_field_type'; -import { getSeries } from './get_series'; -import { getYExtents } from './get_extents'; -import { getFieldsForTerms } from '../../common/fields_utils'; - -const SUPPORTED_FORMATTERS = ['bytes', 'percent', 'number']; - -/* - * This function is used to convert the TSVB model to compatible Lens model. - * Returns the Lens model, only if it is supported. If not, it returns null. - * In case of null, the menu item is disabled and the user can't navigate to Lens. - */ -export const triggerTSVBtoLensConfiguration = async ( - model: Panel -): Promise => { - // Disables the option for not timeseries charts, for the string mode and for series with annotations - if ( - model.type !== PANEL_TYPES.TIMESERIES || - !model.use_kibana_indexes || - (model.annotations && model.annotations.length > 0) - ) { - return null; - } - const layersConfiguration: { [key: string]: VisualizeEditorLayersContext } = {}; - // get the active series number - let seriesNum = 0; - model.series.forEach((series) => { - if (!series.hidden) seriesNum++; - }); - - // handle multiple layers/series - for (let layerIdx = 0; layerIdx < model.series.length; layerIdx++) { - const layer = model.series[layerIdx]; - if (layer.hidden) continue; - - const { indexPatternId, timeField } = await getDataSourceInfo( - model.index_pattern, - model.time_field, - Boolean(layer.override_index_pattern), - layer.series_index_pattern - ); - - const timeShift = layer.offset_time; - // translate to Lens seriesType - const layerChartType = - layer.chart_type === 'line' && Number(layer.fill) > 0 ? 'area' : layer.chart_type; - let chartType = layerChartType; - - if (layer.stacked !== 'none' && layer.stacked !== 'percent') { - chartType = layerChartType !== 'line' ? `${layerChartType}_stacked` : 'line'; - } - if (layer.stacked === 'percent') { - chartType = layerChartType !== 'line' ? `${layerChartType}_percentage_stacked` : 'line'; - } - - // handle multiple metrics - const series = getSeries(layer.metrics, seriesNum); - if (!series) { - return null; - } - const { metrics: metricsArray, seriesAgg } = series; - let filter: { - kql?: string | { [key: string]: any } | undefined; - lucene?: string | { [key: string]: any } | undefined; - }; - if (layer.filter) { - if (layer.filter.language === 'kuery') { - filter = { kql: layer.filter.query }; - } else if (layer.filter.language === 'lucene') { - filter = { lucene: layer.filter.query }; - } - } - - const metricsWithParams = metricsArray.map((metric) => { - return { - ...metric, - color: metric.color ?? layer.color, - params: { - ...metric.params, - ...(timeShift && { shift: timeShift }), - ...(filter && filter), - }, - }; - }); - const splitFilters: VisualizeEditorLayersContext['splitFilters'] = []; - if (layer.split_mode === 'filter' && layer.filter) { - splitFilters.push({ filter: layer.filter }); - } - if (layer.split_filters) { - splitFilters.push(...layer.split_filters); - } - - const palette = layer.palette as PaletteOutput; - const splitFields = getFieldsForTerms(layer.terms_field); - - // in case of terms in a date field, we want to apply the date_histogram - let splitWithDateHistogram = false; - if (layer.terms_field && layer.split_mode === 'terms' && splitFields) { - for (const f of splitFields) { - const fieldType = await getFieldType(indexPatternId, f); - - if (fieldType === 'date') { - if (splitFields.length === 1) { - splitWithDateHistogram = true; - } else { - return null; - } - } - } - } - - const layerConfiguration: VisualizeEditorLayersContext = { - indexPatternId, - timeFieldName: timeField, - chartType, - axisPosition: layer.separate_axis ? layer.axis_position : model.axis_position, - ...(layer.terms_field && { splitFields }), - splitWithDateHistogram, - ...(layer.split_mode !== 'everything' && { splitMode: layer.split_mode }), - ...(splitFilters.length > 0 && { splitFilters }), - // for non supported palettes, we will use the default palette - palette: - !palette || palette.name === 'gradient' || palette.name === 'rainbow' - ? { name: 'default', type: 'palette' } - : palette, - ...(layer.split_mode === 'terms' && { - termsParams: { - size: layer.terms_size ?? 10, - ...(layer.terms_include && { include: [layer.terms_include] }), - includeIsRegex: Boolean(layer.terms_include), - ...(layer.terms_exclude && { exclude: [layer.terms_exclude] }), - excludeIsRegex: Boolean(layer.terms_exclude), - otherBucket: false, - orderDirection: layer.terms_direction ?? 'desc', - orderBy: layer.terms_order_by === '_key' ? { type: 'alphabetical' } : { type: 'column' }, - parentFormat: { id: 'terms' }, - }, - }), - collapseFn: seriesAgg, - metrics: [...metricsWithParams], - timeInterval: model.interval && !model.interval?.includes('=') ? model.interval : 'auto', - ...(SUPPORTED_FORMATTERS.includes(layer.formatter) && { format: layer.formatter }), - ...(layer.label && { label: layer.label }), - dropPartialBuckets: layer.override_index_pattern - ? layer.series_drop_last_bucket > 0 - : model.drop_last_bucket > 0, - }; - layersConfiguration[layerIdx] = layerConfiguration; - } - - const extents = getYExtents(model); - - return { - layers: layersConfiguration, - type: 'lnsXY', - configuration: { - fill: model.series[0].fill ?? 0.3, - legend: { - isVisible: Boolean(model.show_legend), - showSingleSeries: Boolean(model.show_legend), - position: model.legend_position ?? 'right', - shouldTruncate: Boolean(model.truncate_legend), - maxLines: model.max_lines_legend ?? 1, - }, - gridLinesVisibility: { - x: Boolean(model.show_grid), - yLeft: Boolean(model.show_grid), - yRight: Boolean(model.show_grid), - }, - extents, - }, - }; -}; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts b/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts deleted file mode 100644 index 7eebd9a5c440b..0000000000000 --- a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 { Query } from '@kbn/es-query'; -import type { Metric, MetricType } from '../../common/types'; -import { SUPPORTED_METRICS } from './supported_metrics'; - -export const getPercentilesSeries = (percentiles: Metric['percentiles'], fieldName?: string) => { - return percentiles?.map((percentile) => { - return { - agg: 'percentile', - isFullReference: false, - color: percentile.color, - fieldName: fieldName ?? 'document', - params: { percentile: percentile.value }, - }; - }); -}; - -export const getPercentileRankSeries = ( - values: Metric['values'], - colors: Metric['colors'], - fieldName?: string -) => { - return values?.map((value, index) => { - return { - agg: 'percentile_rank', - isFullReference: false, - color: colors?.[index], - fieldName: fieldName ?? 'document', - params: { value }, - }; - }); -}; - -export const getFormulaSeries = (script: string) => { - return [ - { - agg: 'formula', - isFullReference: true, - fieldName: 'document', - params: { formula: script }, - }, - ]; -}; - -export const getPipelineAgg = (subFunctionMetric: Metric) => { - const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; - if (!pipelineAggMap) { - return null; - } - return pipelineAggMap.name; -}; - -export const getTimeScale = (metric: Metric) => { - const supportedTimeScales = ['1s', '1m', '1h', '1d']; - let timeScale; - if (metric.unit && supportedTimeScales.includes(metric.unit)) { - timeScale = metric.unit.replace('1', ''); - } - return timeScale; -}; - -export const computeParentSeries = ( - aggregation: MetricType, - currentMetric: Metric, - subFunctionMetric: Metric, - pipelineAgg: string, - meta?: number -) => { - const aggregationMap = SUPPORTED_METRICS[aggregation]; - if (subFunctionMetric.type === 'filter_ratio') { - const script = getFilterRatioFormula(subFunctionMetric); - if (!script) { - return null; - } - const formula = `${aggregationMap.name}(${script})`; - return getFormulaSeries(formula); - } - const timeScale = getTimeScale(currentMetric); - return [ - { - agg: aggregationMap.name, - isFullReference: aggregationMap.isFullReference, - pipelineAggType: pipelineAgg, - fieldName: - subFunctionMetric?.field && pipelineAgg !== 'count' ? subFunctionMetric?.field : 'document', - params: { - ...(currentMetric.window && { window: currentMetric.window }), - ...(timeScale && { timeScale }), - ...(pipelineAgg === 'percentile' && meta && { percentile: meta }), - ...(pipelineAgg === 'percentile_rank' && meta && { value: meta }), - }, - }, - ]; -}; - -export const getParentPipelineSeries = ( - aggregation: MetricType, - currentMetricIdx: number, - metrics: Metric[] -) => { - const currentMetric = metrics[currentMetricIdx]; - // percentile value is derived from the field Id. It has the format xxx-xxx-xxx-xxx[percentile] - const [fieldId, meta] = currentMetric?.field?.split('[') ?? []; - const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); - if (!subFunctionMetric || subFunctionMetric.type === 'static') { - return null; - } - const pipelineAgg = getPipelineAgg(subFunctionMetric); - if (!pipelineAgg) { - return null; - } - const metaValue = Number(meta?.replace(']', '')); - const subMetricField = subFunctionMetric.field; - const [nestedFieldId, _] = subMetricField?.split('[') ?? []; - // support nested aggs with formula - const additionalSubFunction = metrics.find((metric) => metric.id === nestedFieldId); - if (additionalSubFunction) { - const formula = getParentPipelineSeriesFormula( - metrics, - subFunctionMetric, - pipelineAgg, - aggregation, - metaValue - ); - if (!formula) { - return null; - } - return getFormulaSeries(formula); - } else { - return computeParentSeries( - aggregation, - currentMetric, - subFunctionMetric, - pipelineAgg, - metaValue - ); - } -}; - -export const getParentPipelineSeriesFormula = ( - metrics: Metric[], - subFunctionMetric: Metric, - pipelineAgg: string, - aggregation: MetricType, - percentileValue?: number -) => { - let formula = ''; - const aggregationMap = SUPPORTED_METRICS[aggregation]; - const subMetricField = subFunctionMetric.field; - const [nestedFieldId, nestedMeta] = subMetricField?.split('[') ?? []; - // support nested aggs - const additionalSubFunction = metrics.find((metric) => metric.id === nestedFieldId); - if (additionalSubFunction) { - // support nested aggs with formula - const additionalPipelineAggMap = SUPPORTED_METRICS[additionalSubFunction.type]; - if (!additionalPipelineAggMap) { - return null; - } - const nestedMetaValue = Number(nestedMeta?.replace(']', '')); - const aggMap = SUPPORTED_METRICS[aggregation]; - let additionalFunctionArgs; - if (additionalPipelineAggMap.name === 'percentile' && nestedMetaValue) { - additionalFunctionArgs = `, percentile=${nestedMetaValue}`; - } - if (additionalPipelineAggMap.name === 'percentile_rank' && nestedMetaValue) { - additionalFunctionArgs = `, value=${nestedMetaValue}`; - } - formula = `${aggMap.name}(${pipelineAgg}(${additionalPipelineAggMap.name}(${ - additionalSubFunction.field ?? '' - }${additionalFunctionArgs ?? ''})))`; - } else { - let additionalFunctionArgs; - if (pipelineAgg === 'percentile' && percentileValue) { - additionalFunctionArgs = `, percentile=${percentileValue}`; - } - if (pipelineAgg === 'percentile_rank' && percentileValue) { - additionalFunctionArgs = `, value=${percentileValue}`; - } - if (pipelineAgg === 'filter_ratio') { - const script = getFilterRatioFormula(subFunctionMetric); - if (!script) { - return null; - } - formula = `${aggregationMap.name}(${script}${additionalFunctionArgs ?? ''})`; - } else if (pipelineAgg === 'counter_rate') { - formula = `${aggregationMap.name}(${pipelineAgg}(max(${subFunctionMetric.field}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - })))`; - } else { - formula = `${aggregationMap.name}(${pipelineAgg}(${subFunctionMetric.field}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - }))`; - } - } - return formula; -}; - -export const getSiblingPipelineSeriesFormula = ( - aggregation: MetricType, - currentMetric: Metric, - metrics: Metric[] -) => { - const [nestedFieldId, nestedMeta] = currentMetric.field?.split('[') ?? []; - const subFunctionMetric = metrics.find((metric) => metric.id === nestedFieldId); - if (!subFunctionMetric || subFunctionMetric.type === 'static') { - return null; - } - const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; - if (!pipelineAggMap) { - return null; - } - const aggregationMap = SUPPORTED_METRICS[aggregation]; - const subMetricField = subFunctionMetric.type !== 'count' ? subFunctionMetric.field : ''; - // support nested aggs with formula - const additionalSubFunction = metrics.find((metric) => metric.id === subMetricField); - let formula = `${aggregationMap.name}(`; - let minimumValue = ''; - if (currentMetric.type === 'positive_only') { - minimumValue = `, 0`; - } - if (additionalSubFunction) { - const additionalPipelineAggMap = SUPPORTED_METRICS[additionalSubFunction.type]; - if (!additionalPipelineAggMap) { - return null; - } - const additionalSubFunctionField = - additionalSubFunction.type !== 'count' ? additionalSubFunction.field : ''; - formula += `${pipelineAggMap.name}(${additionalPipelineAggMap.name}(${ - additionalSubFunctionField ?? '' - }))${minimumValue})`; - } else { - let additionalFunctionArgs; - // handle percentile and percentile_rank - const nestedMetaValue = Number(nestedMeta?.replace(']', '')); - if (pipelineAggMap.name === 'percentile' && nestedMetaValue) { - additionalFunctionArgs = `, percentile=${nestedMetaValue}`; - } - if (pipelineAggMap.name === 'percentile_rank' && nestedMetaValue) { - additionalFunctionArgs = `, value=${nestedMetaValue}`; - } - formula += `${pipelineAggMap.name}(${subMetricField ?? ''}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - })${minimumValue})`; - } - return formula; -}; - -const escapeQuotes = (str: string) => { - return str?.replace(/'/g, "\\'"); -}; - -const constructFilterRationFormula = (operation: string, metric?: Query) => { - return `${operation}${metric?.language === 'lucene' ? 'lucene' : 'kql'}='${ - metric?.query && typeof metric?.query === 'string' - ? escapeQuotes(metric?.query) - : metric?.query ?? '*' - }')`; -}; - -export const getFilterRatioFormula = (currentMetric: Metric) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { numerator, denominator, metric_agg, field } = currentMetric; - let aggregation = SUPPORTED_METRICS.count; - if (metric_agg) { - aggregation = SUPPORTED_METRICS[metric_agg]; - if (!aggregation) { - return null; - } - } - const operation = - metric_agg && metric_agg !== 'count' ? `${aggregation.name}('${field}',` : 'count('; - - if (aggregation.name === 'counter_rate') { - const numeratorFormula = constructFilterRationFormula( - `${aggregation.name}(max('${field}',`, - numerator - ); - const denominatorFormula = constructFilterRationFormula( - `${aggregation.name}(max('${field}',`, - denominator - ); - return `${numeratorFormula}) / ${denominatorFormula})`; - } else { - const numeratorFormula = constructFilterRationFormula(operation, numerator); - const denominatorFormula = constructFilterRationFormula(operation, denominator); - return `${numeratorFormula} / ${denominatorFormula}`; - } -}; - -export const getFormulaEquivalent = ( - currentMetric: Metric, - metrics: Metric[], - metaValue?: number -) => { - const aggregation = SUPPORTED_METRICS[currentMetric.type]?.name; - switch (currentMetric.type) { - case 'avg_bucket': - case 'max_bucket': - case 'min_bucket': - case 'sum_bucket': - case 'positive_only': { - return getSiblingPipelineSeriesFormula(currentMetric.type, currentMetric, metrics); - } - case 'count': { - return `${aggregation}()`; - } - case 'percentile': { - return `${aggregation}(${currentMetric.field}${ - metaValue ? `, percentile=${metaValue}` : '' - })`; - } - case 'percentile_rank': { - return `${aggregation}(${currentMetric.field}${metaValue ? `, value=${metaValue}` : ''})`; - } - case 'cumulative_sum': - case 'derivative': - case 'moving_average': { - const [fieldId, _] = currentMetric?.field?.split('[') ?? []; - const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); - if (!subFunctionMetric) { - return null; - } - const pipelineAgg = getPipelineAgg(subFunctionMetric); - if (!pipelineAgg) { - return null; - } - return getParentPipelineSeriesFormula( - metrics, - subFunctionMetric, - pipelineAgg, - currentMetric.type, - metaValue - ); - } - case 'positive_rate': { - return `${aggregation}(max(${currentMetric.field}))`; - } - case 'filter_ratio': { - return getFilterRatioFormula(currentMetric); - } - case 'static': { - return `${currentMetric.value}`; - } - default: { - return `${aggregation}(${currentMetric.field})`; - } - } -}; diff --git a/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts b/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts index a6479bbbefeb0..8c8bda93ce13e 100644 --- a/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts +++ b/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts @@ -7,7 +7,8 @@ */ import { findObjectByTitle } from './find_object_by_title'; -import { SimpleSavedObjectImpl, SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { simpleSavedObjectMock } from '@kbn/core/public/mocks'; describe('findObjectByTitle', () => { const savedObjectsClient: SavedObjectsClientContract = {} as SavedObjectsClientContract; @@ -22,7 +23,7 @@ describe('findObjectByTitle', () => { }); it('matches any case', async () => { - const indexPattern = new SimpleSavedObjectImpl(savedObjectsClient, { + const indexPattern = simpleSavedObjectMock.create(savedObjectsClient, { attributes: { title: 'foo' }, } as SavedObject); savedObjectsClient.find = jest.fn().mockImplementation(() => diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx index 8f19856bcdd40..ca373b31b6020 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx @@ -328,7 +328,6 @@ const TopNav = ({ }, isMissingCurrent: isMissingCurrentDataView, onChangeDataView, - showNewMenuTour: false, } : undefined } diff --git a/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx b/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx index a2b75d5868b64..03cb64920879e 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx +++ b/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx @@ -140,7 +140,7 @@ export const getNoItemsMessage = (createItem: () => void) => ( +

void) => (

} actions={ - + { - const { eventTypes = [], withTimeoutMs, fromTimestamp } = options; + const { eventTypes = [], withTimeoutMs, fromTimestamp, filters } = options; const filteredEvents$ = events$.pipe( filter((event) => { @@ -39,6 +40,28 @@ export async function fetchEvents( return new Date(event.timestamp).getTime() - new Date(fromTimestamp).getTime() > 0; } return true; + }), + filter((event) => { + if (filters) { + return Object.entries(filters).every(([key, comparison]) => { + const value = get(event, key); + return Object.entries(comparison).every(([operation, valueToCompare]) => { + switch (operation) { + case 'eq': + return value === valueToCompare; + case 'gte': + return value >= (valueToCompare as typeof value); + case 'gt': + return value > (valueToCompare as typeof value); + case 'lte': + return value <= (valueToCompare as typeof value); + case 'lt': + return value < (valueToCompare as typeof value); + } + }); + }); + } + return true; }) ); diff --git a/test/analytics/fixtures/plugins/analytics_ftr_helpers/common/types.ts b/test/analytics/fixtures/plugins/analytics_ftr_helpers/common/types.ts index 3f66cde1fa3ae..97e1faff823a4 100644 --- a/test/analytics/fixtures/plugins/analytics_ftr_helpers/common/types.ts +++ b/test/analytics/fixtures/plugins/analytics_ftr_helpers/common/types.ts @@ -8,6 +8,10 @@ import type { Event, EventType } from '@kbn/analytics-client'; +export type FiltersOptions = { + [key in 'eq' | 'gte' | 'gt' | 'lte' | 'lt']?: unknown; +}; + export interface GetEventsOptions { /** * eventTypes (optional) array of event types to return @@ -24,6 +28,18 @@ export interface GetEventsOptions { * @remarks Useful when we need to retrieve the events after a specific action, and we don't care about anything prior to that. */ fromTimestamp?: string; + /** + * List of internal keys to validate in the event with the validation comparison. + * @example + * { + * filters: { + * 'properties.my_key': { + * eq: 'my expected value', + * }, + * }, + * } + */ + filters?: Record; } export interface EBTHelpersContract { @@ -37,7 +53,10 @@ export interface EBTHelpersContract { * @param takeNumberOfEvents - number of events to return * @param options (optional) list of options to filter events or for advanced usage of the API {@link GetEventsOptions}. */ - getEvents: (takeNumberOfEvents: number, options?: GetEventsOptions) => Promise; + getEvents: ( + takeNumberOfEvents: number, + options?: GetEventsOptions + ) => Promise>>>; /** * Count the number of events that match the filters. * @param options list of options to filter the events {@link GetEventsOptions}. `withTimeoutMs` is required in this API. diff --git a/test/analytics/fixtures/plugins/analytics_ftr_helpers/public/plugin.test.ts b/test/analytics/fixtures/plugins/analytics_ftr_helpers/public/plugin.test.ts index c5cb6d4df1dd3..b3f86172b751b 100644 --- a/test/analytics/fixtures/plugins/analytics_ftr_helpers/public/plugin.test.ts +++ b/test/analytics/fixtures/plugins/analytics_ftr_helpers/public/plugin.test.ts @@ -127,5 +127,32 @@ describe('AnalyticsFTRHelpers', () => { }) ).resolves.toEqual([{ ...event, timestamp: '2022-06-10T00:00:00.000Z' }]); }); + + test('should filter by `filters` when provided', async () => { + // 3 enqueued events + const events = [ + { ...event, timestamp: '2022-01-10T00:00:00.000Z' }, + { ...event, timestamp: '2022-03-10T00:00:00.000Z', properties: { my_property: 20 } }, + { ...event, timestamp: '2022-06-10T00:00:00.000Z' }, + ]; + events.forEach((ev) => events$.next(ev)); + + await expect( + window.__analytics_ftr_helpers__.getEvents(1, { + eventTypes: [event.event_type], + filters: { + 'properties.my_property': { + eq: 20, + gte: 20, + lte: 20, + gt: 10, + lt: 30, + }, + }, + }) + ).resolves.toEqual([ + { ...event, timestamp: '2022-03-10T00:00:00.000Z', properties: { my_property: 20 } }, + ]); + }); }); }); diff --git a/test/analytics/fixtures/plugins/analytics_ftr_helpers/server/plugin.ts b/test/analytics/fixtures/plugins/analytics_ftr_helpers/server/plugin.ts index 88cacd2bfd36d..2e5d93345b07f 100644 --- a/test/analytics/fixtures/plugins/analytics_ftr_helpers/server/plugin.ts +++ b/test/analytics/fixtures/plugins/analytics_ftr_helpers/server/plugin.ts @@ -48,6 +48,21 @@ export class AnalyticsFTRHelpers implements Plugin { eventTypes: schema.arrayOf(schema.string()), withTimeoutMs: schema.maybe(schema.number()), fromTimestamp: schema.maybe(schema.string()), + filters: schema.maybe( + schema.recordOf( + schema.string(), + schema.recordOf( + schema.oneOf([ + schema.literal('eq'), + schema.literal('gte'), + schema.literal('gt'), + schema.literal('lte'), + schema.literal('lt'), + ]), + schema.any() + ) + ) + ), }), }, }, diff --git a/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts b/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts index 6543b3c7955ef..3d0b018156ce7 100644 --- a/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts +++ b/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts @@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const { common } = getPageObjects(['common']); describe('Core Context Providers', () => { - let event: Event; + let event: Event>; before(async () => { await common.navigateToApp('home'); [event] = await ebtUIHelper.getEvents(1, { eventTypes: ['Loaded Kibana'] }); // Get the loaded Kibana event 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 178bed7cbbbe5..0522ab5b41c2c 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 @@ -10,6 +10,8 @@ import { GetEventsOptions } from '@kbn/analytics-ftr-helpers-plugin/common/types import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../services'; +const DASHBOARD_LOADED_EVENT = 'dashboard_loaded'; + export default function ({ getService, getPageObjects }: FtrProviderContext) { const ebtUIHelper = getService('kibana_ebt_ui'); const PageObjects = getPageObjects([ @@ -29,23 +31,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const getEvents = async (count: number, options?: GetEventsOptions) => ebtUIHelper.getEvents(count, { - eventTypes: ['dashboard-data-loaded'], + eventTypes: ['performance_metric'], fromTimestamp, withTimeoutMs: 1000, + filters: { 'properties.eventName': { eq: DASHBOARD_LOADED_EVENT } }, ...options, }); const checkEmitsOnce = async (options?: GetEventsOptions) => { const events = await getEvents(Number.MAX_SAFE_INTEGER, options); - expect(events.length).to.be(1); const event = events[0]; - expect(event.event_type).to.eql('dashboard-data-loaded'); + expect(event.event_type).to.eql('performance_metric'); + expect(event.properties.eventName).to.eql(DASHBOARD_LOADED_EVENT); expect(event.context.applicationId).to.be('dashboards'); expect(event.context.page).to.be('app'); expect(event.context.pageName).to.be('application:dashboards:app'); - expect(event.properties.status).to.be('done'); - expect(event.properties.timeToData).to.be.a('number'); - expect(event.properties.timeToDone).to.be.a('number'); + expect(event.properties.duration).to.be.a('number'); + expect(event.properties.key1).to.eql('time_to_data'); + expect(event.properties.value1).to.be.a('number'); + expect(event.properties.key2).to.eql('num_of_panels'); + expect(event.properties.value2).to.be.a('number'); // update fromTimestamp fromTimestamp = event.timestamp; @@ -81,7 +86,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboards'); }); - it('doesnt emit on empty dashboard', async () => { + it("doesn't emit on empty dashboard", async () => { await PageObjects.dashboard.clickNewDashboard(); await checkDoesNotEmit(); }); @@ -100,7 +105,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const event = await checkEmitsOnce(); expect(event.context.entityId).to.be('new'); - expect(event.properties.numOfPanels).to.be(1); + expect(event.properties.key2).to.be('num_of_panels'); + expect(event.properties.value2).to.be(1); }); it('emits on saved search refreshed', async () => { @@ -108,7 +114,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await checkEmitsOnce(); }); - it('doesnt emit when removing saved search panel', async () => { + it("doesn't emit when removing saved search panel", async () => { await dashboardPanelActions.removePanelByTitle(SAVED_SEARCH_PANEL_TITLE); await checkDoesNotEmit(); }); @@ -126,7 +132,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await checkEmitsOnce(); }); - it('doesnt emit when removing vis panel', async () => { + it("doesn't emit when removing vis panel", async () => { await dashboardPanelActions.removePanelByTitle(VIS_PANEL_TITLE); await checkDoesNotEmit(); }); @@ -150,7 +156,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await checkEmitsOnce(); }); - it('doesnt emit when removing markup panel', async () => { + it("doesn't emit when removing markup panel", async () => { await dashboardPanelActions.removePanelByTitle(MARKDOWN_PANEL_TITLE); await checkDoesNotEmit(); }); @@ -170,7 +176,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await checkEmitsOnce(); }); - it('doesnt emit when removing map panel', async () => { + it("doesn't emit when removing map panel", async () => { await dashboardPanelActions.removePanelByTitle(MAP_PANEL_TITLE); await checkDoesNotEmit(); }); @@ -187,10 +193,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const event = await checkEmitsOnce(); expect(event.context.entityId).to.be('7adfa750-4c81-11e8-b3d7-01146121b73d'); - expect(event.properties.numOfPanels).to.be(17); - expect(event.properties.timeToDone as number).to.be.greaterThan( - event.properties.timeToData as number + + expect(event.properties.key1).to.be('time_to_data'); + expect(event.properties.duration as number).to.be.greaterThan( + event.properties.value1 as number ); + + expect(event.properties.key2).to.be('num_of_panels'); + expect(event.properties.value2).to.be(17); }); /** diff --git a/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts b/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts index 7ed47db35cbeb..e4ac2346b5893 100644 --- a/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts +++ b/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts @@ -19,36 +19,57 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await common.navigateToApp('home'); }); - it('should emit the "Loaded Kibana" event', async () => { + it('should emit the legacy "Loaded Kibana"', async () => { const [event] = await ebtUIHelper.getEvents(1, { eventTypes: ['Loaded Kibana'] }); + expect(event.event_type).to.eql('Loaded Kibana'); expect(event.properties).to.have.property('kibana_version'); expect(event.properties.kibana_version).to.be.a('string'); expect(event.properties).to.have.property('protocol'); expect(event.properties.protocol).to.be.a('string'); + }); + + it('should emit the kibana_loaded event', async () => { + const [event] = await ebtUIHelper.getEvents(1, { + eventTypes: ['performance_metric'], + filters: { 'properties.eventName': { eq: 'kibana_loaded' } }, + }); + + // New event + expect(event.event_type).to.eql('performance_metric'); + expect(event.properties.eventName).to.eql('kibana_loaded'); + + // meta + expect(event.properties).to.have.property('meta'); + + const meta = event.properties.meta as Record; + expect(meta.kibana_version).to.be.a('string'); + expect(meta.protocol).to.be.a('string'); // Kibana Loaded timings - expect(event.properties).to.have.property('load_started'); - expect(event.properties.load_started).to.be.a('number'); - expect(event.properties).to.have.property('bootstrap_started'); - expect(event.properties.bootstrap_started).to.be.a('number'); - expect(event.properties).to.have.property('core_created'); - expect(event.properties.core_created).to.be.a('number'); - expect(event.properties).to.have.property('setup_done'); - expect(event.properties.setup_done).to.be.a('number'); - expect(event.properties).to.have.property('start_done'); - expect(event.properties.start_done).to.be.a('number'); - expect(event.properties).to.have.property('first_app_nav'); - expect(event.properties.start_done).to.be.a('number'); + expect(event.properties).to.have.property('duration'); + expect(event.properties.duration).to.be.a('number'); + + expect(event.properties).to.have.property('key1', 'load_started'); + expect(event.properties).to.have.property('key2', 'bootstrap_started'); + expect(event.properties).to.have.property('key3', 'core_created'); + expect(event.properties).to.have.property('key4', 'setup_done'); + expect(event.properties).to.have.property('key5', 'start_done'); + + expect(event.properties.value1).to.be.a('number'); + expect(event.properties.value2).to.be.a('number'); + expect(event.properties.value3).to.be.a('number'); + expect(event.properties.value4).to.be.a('number'); + expect(event.properties.value5).to.be.a('number'); if (browser.isChromium) { // Kibana Loaded memory - expect(event.properties).to.have.property('memory_js_heap_size_limit'); - expect(event.properties.memory_js_heap_size_limit).to.be.a('number'); - expect(event.properties).to.have.property('memory_js_heap_size_total'); - expect(event.properties.memory_js_heap_size_total).to.be.a('number'); - expect(event.properties).to.have.property('memory_js_heap_size_used'); - expect(event.properties.memory_js_heap_size_used).to.be.a('number'); + expect(meta).to.have.property('jsHeapSizeLimit'); + expect(meta.jsHeapSizeLimit).to.be.a('number'); + expect(meta).to.have.property('totalJSHeapSize'); + expect(meta.totalJSHeapSize).to.be.a('number'); + expect(meta).to.have.property('usedJSHeapSize'); + expect(meta.usedJSHeapSize).to.be.a('number'); } }); }); diff --git a/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts b/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts index 9a694c38b8b40..cf781bed5ad0e 100644 --- a/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts +++ b/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts @@ -15,7 +15,7 @@ export default function ({ getService }: FtrProviderContext) { const ebtServerHelper = getService('kibana_ebt_server'); describe('Core Context Providers', () => { - let event: Event; + let event: Event>; before(async () => { let i = 2; do { diff --git a/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts b/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts index 07c03465f6c27..8f904c4c8844c 100644 --- a/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts +++ b/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts @@ -14,8 +14,8 @@ export default function ({ getService }: FtrProviderContext) { const ebtServerHelper = getService('kibana_ebt_server'); describe('core-overall_status_changed', () => { - let initialEvent: Event; - let secondEvent: Event; + let initialEvent: Event>; + let secondEvent: Event>; before(async () => { [initialEvent, secondEvent] = await ebtServerHelper.getEvents(2, { diff --git a/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts b/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts index 5380d4e392e52..d1b59ce9d4e9c 100644 --- a/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts +++ b/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts @@ -13,7 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const ebtServerHelper = getService('kibana_ebt_server'); describe('kibana_started', () => { - it('should emit the "kibana_started" event', async () => { + it('should emit the legacy "kibana_started" event', async () => { const [event] = await ebtServerHelper.getEvents(1, { eventTypes: ['kibana_started'] }); expect(event.event_type).to.eql('kibana_started'); const uptimePerStep = event.properties.uptime_per_step as Record< @@ -29,5 +29,25 @@ export default function ({ getService }: FtrProviderContext) { expect(uptimePerStep.start.start).to.be.a('number'); expect(uptimePerStep.start.end).to.be.a('number'); }); + + it('should emit the "kibana_started" metric event', async () => { + const [event] = await ebtServerHelper.getEvents(1, { + eventTypes: ['performance_metric'], + filters: { 'properties.eventName': { eq: 'kibana_started' } }, + }); + expect(event.event_type).to.eql('performance_metric'); + expect(event.properties.eventName).to.eql('kibana_started'); + expect(event.properties.duration).to.be.a('number'); + expect(event.properties.key1).to.eql('time_to_constructor'); + expect(event.properties.value1).to.be.a('number'); + expect(event.properties.key2).to.eql('constructor_time'); + expect(event.properties.value2).to.be.a('number'); + expect(event.properties.key3).to.eql('preboot_time'); + expect(event.properties.value3).to.be.a('number'); + expect(event.properties.key4).to.eql('setup_time'); + expect(event.properties.value4).to.be.a('number'); + expect(event.properties.key5).to.eql('start_time'); + expect(event.properties.value5).to.be.a('number'); + }); }); } diff --git a/test/functional/apps/dashboard/group3/dashboard_state.ts b/test/functional/apps/dashboard/group3/dashboard_state.ts index 79179b7c9d08b..33d5acceb00d8 100644 --- a/test/functional/apps/dashboard/group3/dashboard_state.ts +++ b/test/functional/apps/dashboard/group3/dashboard_state.ts @@ -48,7 +48,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { isNewChartsLibraryEnabled = await PageObjects.visChart.isNewChartsLibraryEnabled(); await PageObjects.dashboard.initTests(); await PageObjects.dashboard.preserveCrossAppState(); - await browser.setLocalStorageItem('data.newDataViewMenu', 'true'); if (!isNewChartsLibraryEnabled) { await kibanaServer.uiSettings.update({ diff --git a/test/functional/apps/getting_started/_shakespeare.ts b/test/functional/apps/getting_started/_shakespeare.ts index 8cdf4a5f2ff76..900121f1bcf0f 100644 --- a/test/functional/apps/getting_started/_shakespeare.ts +++ b/test/functional/apps/getting_started/_shakespeare.ts @@ -14,7 +14,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); - const esArchiver = getService('esArchiver'); const retry = getService('retry'); const security = getService('security'); const browser = getService('browser'); @@ -42,19 +41,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let isNewChartsLibraryEnabled = true; before(async function () { - log.debug( - 'Load empty_kibana and Shakespeare Getting Started data\n' + - 'https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html' - ); + log.debug('https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html'); isNewChartsLibraryEnabled = await PageObjects.visChart.isNewChartsLibraryEnabled(); await security.testUser.setRoles(['kibana_admin', 'test_shakespeare_reader']); - await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana', { - skipExisting: true, - }); + await kibanaServer.savedObjects.cleanStandardList(); log.debug('Load shakespeare data'); - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/getting_started/shakespeare' - ); if (!isNewChartsLibraryEnabled) { await kibanaServer.uiSettings.update({ @@ -66,7 +57,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); - await esArchiver.unload('test/functional/fixtures/es_archiver/getting_started/shakespeare'); + kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.uiSettings.replace({}); }); diff --git a/test/functional/apps/getting_started/index.ts b/test/functional/apps/getting_started/index.ts index dfda371c3eedf..9d4e3702dcf60 100644 --- a/test/functional/apps/getting_started/index.ts +++ b/test/functional/apps/getting_started/index.ts @@ -11,10 +11,18 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); describe('Getting Started ', function () { before(async function () { await browser.setWindowSize(1200, 800); + await esArchiver.loadIfNeeded( + 'test/functional/fixtures/es_archiver/getting_started/shakespeare' + ); + }); + + after(async function () { + await esArchiver.unload('test/functional/fixtures/es_archiver/getting_started/shakespeare'); }); // TODO: Remove when vislib is removed diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index 1d9d02d5e94b5..016cead53f0c4 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); - const PageObjects = getPageObjects(['common', 'header', 'home', 'timePicker', 'unifiedSearch']); + const PageObjects = getPageObjects(['common', 'header', 'home', 'timePicker']); const appsMenu = getService('appsMenu'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); @@ -37,7 +37,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Navigate to discover app await appsMenu.clickLink('Discover'); - await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); const discoverUrl = await browser.getCurrentUrl(); await PageObjects.timePicker.setDefaultAbsoluteRange(); const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index df6f04f7b4f5b..b3c0f7deda982 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -293,7 +293,6 @@ export class CommonPageObject extends FtrService { } if (appName === 'discover') { await this.browser.setLocalStorageItem('data.autocompleteFtuePopover', 'true'); - await this.browser.setLocalStorageItem('data.newDataViewMenu', 'true'); } return currentUrl; }); diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 7ba25a89ce84a..6734690c4b3bd 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -333,7 +333,7 @@ export class DashboardPageObject extends FtrService { await this.common.clickConfirmOnModal(); } } - await this.listingTable.clickNewButton('createDashboardPromptButton'); + await this.listingTable.clickNewButton(); if (await this.testSubjects.exists('dashboardCreateConfirm')) { if (continueEditing) { await this.testSubjects.click('dashboardCreateConfirmContinue'); @@ -355,7 +355,7 @@ export class DashboardPageObject extends FtrService { await this.common.clickConfirmOnModal(); } } - await this.listingTable.clickNewButton('createDashboardPromptButton'); + await this.listingTable.clickNewButton(); await this.testSubjects.existOrFail('dashboardCreateConfirm'); if (continueEditing) { await this.testSubjects.click('dashboardCreateConfirmContinue'); @@ -367,11 +367,11 @@ export class DashboardPageObject extends FtrService { } public async clickCreateDashboardPrompt() { - await this.testSubjects.click('createDashboardPromptButton'); + await this.testSubjects.click('newItemButton'); } public async getCreateDashboardPromptExists() { - return await this.testSubjects.exists('createDashboardPromptButton'); + return this.testSubjects.exists('emptyListPrompt'); } public async isOptionsOpen() { diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 3f81384310eea..8700cc734d809 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -24,8 +24,6 @@ export class DiscoverPageObject extends FtrService { private readonly kibanaServer = this.ctx.getService('kibanaServer'); private readonly queryBar = this.ctx.getService('queryBar'); - private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); - private readonly defaultFindTimeout = this.config.get('timeouts.find'); public async getChartTimespan() { @@ -570,7 +568,6 @@ export class DiscoverPageObject extends FtrService { await this.retry.waitFor('Discover app on screen', async () => { return await this.isDiscoverAppOnScreen(); }); - await this.unifiedSearch.closeTourPopoverByLocalStorage(); } public async showAllFilterActions() { diff --git a/test/functional/page_objects/unified_search_page.ts b/test/functional/page_objects/unified_search_page.ts index af3cbd05c53e4..ab85d250a61e3 100644 --- a/test/functional/page_objects/unified_search_page.ts +++ b/test/functional/page_objects/unified_search_page.ts @@ -9,22 +9,9 @@ import { FtrService } from '../ftr_provider_context'; export class UnifiedSearchPageObject extends FtrService { - private readonly browser = this.ctx.getService('browser'); private readonly retry = this.ctx.getService('retry'); private readonly testSubjects = this.ctx.getService('testSubjects'); - public async closeTour() { - const tourPopoverIsOpen = await this.testSubjects.exists('dataViewPickerTourLink'); - if (tourPopoverIsOpen) { - await this.testSubjects.click('dataViewPickerTourLink'); - } - } - - public async closeTourPopoverByLocalStorage() { - await this.browser.setLocalStorageItem('data.newDataViewMenu', 'true'); - await this.browser.refresh(); - } - public async switchDataView(switchButtonSelector: string, dataViewTitle: string) { await this.testSubjects.click(switchButtonSelector); diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index e087d50f21003..95adb7e64b1ec 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -39,7 +39,6 @@ export class VisualizePageObject extends FtrService { private readonly elasticChart = this.ctx.getService('elasticChart'); private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); - private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); private readonly visEditor = this.ctx.getPageObject('visEditor'); private readonly visChart = this.ctx.getPageObject('visChart'); @@ -70,7 +69,7 @@ export class VisualizePageObject extends FtrService { } public async clickNewVisualization() { - await this.listingTable.clickNewButton('createVisualizationPromptButton'); + await this.listingTable.clickNewButton(); } public async clickAggBasedVisualizations() { @@ -82,7 +81,7 @@ export class VisualizePageObject extends FtrService { } public async createVisualizationPromptButton() { - await this.testSubjects.click('createVisualizationPromptButton'); + await this.testSubjects.click('newItemButton'); } public async getChartTypes() { @@ -155,10 +154,6 @@ export class VisualizePageObject extends FtrService { public async clickVisType(type: string) { await this.testSubjects.click(`visType-${type}`); await this.header.waitUntilLoadingHasFinished(); - - if (type === 'lens') { - await this.unifiedSearch.closeTour(); - } } public async clickAreaChart() { @@ -260,7 +255,7 @@ export class VisualizePageObject extends FtrService { await this.listingTable.checkListingSelectAllCheckbox(); await this.listingTable.clickDeleteSelected(); await this.common.clickConfirmOnModal(); - await this.testSubjects.find('createVisualizationPromptButton'); + await this.testSubjects.find('newItemButton'); }); } diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 48828798a4efa..8688d375f7a7b 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -17,7 +17,6 @@ export class DashboardVisualizationsService extends FtrService { private readonly visualize = this.ctx.getPageObject('visualize'); private readonly visEditor = this.ctx.getPageObject('visEditor'); private readonly header = this.ctx.getPageObject('header'); - private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); private readonly discover = this.ctx.getPageObject('discover'); private readonly timePicker = this.ctx.getPageObject('timePicker'); @@ -44,7 +43,6 @@ export class DashboardVisualizationsService extends FtrService { }) { this.log.debug(`createSavedSearch(${name})`); await this.header.clickDiscover(true); - await this.unifiedSearch.closeTourPopoverByLocalStorage(); await this.timePicker.setHistoricalDataRange(); if (query) { diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts index 1cd4249df5050..8db68883715e2 100644 --- a/test/functional/services/listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -180,21 +180,9 @@ export class ListingTableService extends FtrService { /** * Clicks NewItem button on Landing page - * @param promptBtnTestSubj testSubj locator for Prompt button */ - public async clickNewButton(promptBtnTestSubj: string): Promise { - await this.retry.tryForTime(20000, async () => { - // newItemButton button is only visible when there are items in the listing table is displayed. - const isnNewItemButtonPresent = await this.testSubjects.exists('newItemButton', { - timeout: 10000, - }); - if (isnNewItemButtonPresent) { - await this.testSubjects.click('newItemButton'); - } else { - // no items exist, click createPromptButton to create new dashboard/visualization - await this.testSubjects.click(promptBtnTestSubj); - } - }); + public async clickNewButton(): Promise { + await this.testSubjects.click('newItemButton'); } public async onListingPage(appName: AppName) { diff --git a/tsconfig.base.json b/tsconfig.base.json index 297cb4101b6f6..c3a37ced8434e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -47,6 +47,8 @@ "@kbn/ui-actions-examples-plugin/*": ["examples/ui_action_examples/*"], "@kbn/ui-actions-explorer-plugin": ["examples/ui_actions_explorer"], "@kbn/ui-actions-explorer-plugin/*": ["examples/ui_actions_explorer/*"], + "@kbn/user-profile-examples-plugin": ["examples/user_profile_examples"], + "@kbn/user-profile-examples-plugin/*": ["examples/user_profile_examples/*"], "@kbn/advanced-settings-plugin": ["src/plugins/advanced_settings"], "@kbn/advanced-settings-plugin/*": ["src/plugins/advanced_settings/*"], "@kbn/bfetch-plugin": ["src/plugins/bfetch"], diff --git a/x-pack/gulpfile.js b/x-pack/gulpfile.js index 3b9263fdcea2e..f460d99af28f0 100644 --- a/x-pack/gulpfile.js +++ b/x-pack/gulpfile.js @@ -7,11 +7,9 @@ require('../src/setup_node_env'); -const { buildTask } = require('./tasks/build'); const { downloadChromium } = require('./tasks/download_chromium'); // export the tasks that are runnable from the CLI module.exports = { - build: buildTask, downloadChromium, }; diff --git a/x-pack/package.json b/x-pack/package.json index dbec2f68b8dfa..2433a70c26123 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -8,7 +8,6 @@ "github-checks-reporter": "../node_modules/.bin/github-checks-reporter", "kbn": "node ../scripts/kbn", "start": "node ../scripts/kibana --dev", - "build": "node --preserve-symlinks ../node_modules/.bin/gulp build", "test:jest": "node ../scripts/jest" }, "kibana": { diff --git a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx index 1c03b909ded1c..b4eb2e0809f4e 100644 --- a/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx +++ b/x-pack/packages/ml/aiops_components/src/dual_brush/dual_brush.tsx @@ -12,6 +12,7 @@ import * as d3Scale from 'd3-scale'; import * as d3Selection from 'd3-selection'; import * as d3Transition from 'd3-transition'; +import { getSnappedWindowParameters } from '@kbn/aiops-utils'; import type { WindowParameters } from '@kbn/aiops-utils'; import './dual_brush.scss'; @@ -58,6 +59,7 @@ interface DualBrushProps { max: number; onChange?: (windowParameters: WindowParameters, windowPxParameters: WindowParameters) => void; marginLeft: number; + snapTimestamps?: number[]; width: number; } @@ -67,6 +69,7 @@ export function DualBrush({ max, onChange, marginLeft, + snapTimestamps, width, }: DualBrushProps) { const d3BrushContainer = useRef(null); @@ -97,10 +100,10 @@ export function DualBrush({ const xMax = x(max) ?? 0; const minExtentPx = Math.round((xMax - xMin) / 100); - const baselineBrush = d3.select('#brush-baseline'); + const baselineBrush = d3.select('#aiops-brush-baseline'); const baselineSelection = d3.brushSelection(baselineBrush.node() as SVGGElement); - const deviationBrush = d3.select('#brush-deviation'); + const deviationBrush = d3.select('#aiops-brush-deviation'); const deviationSelection = d3.brushSelection(deviationBrush.node() as SVGGElement); if (!isBrushXSelection(deviationSelection) || !isBrushXSelection(baselineSelection)) { @@ -129,12 +132,6 @@ export function DualBrush({ deviationMin: px2ts(deviationSelection[0]), deviationMax: px2ts(deviationSelection[1]), }; - const newBrushPx = { - baselineMin: baselineSelection[0], - baselineMax: baselineSelection[1], - deviationMin: deviationSelection[0], - deviationMax: deviationSelection[1], - }; if ( id === 'deviation' && @@ -147,14 +144,6 @@ export function DualBrush({ newWindowParameters.deviationMin = px2ts(newDeviationMin); newWindowParameters.deviationMax = px2ts(newDeviationMax); - newBrushPx.deviationMin = newDeviationMin; - newBrushPx.deviationMax = newDeviationMax; - - d3.select(this) - .transition() - .duration(200) - // @ts-expect-error call doesn't allow the brush move function - .call(brushes.current[1].brush.move, [newDeviationMin, newDeviationMax]); } else if ( id === 'baseline' && deviationSelection && @@ -166,23 +155,56 @@ export function DualBrush({ newWindowParameters.baselineMin = px2ts(newBaselineMin); newWindowParameters.baselineMax = px2ts(newBaselineMax); - newBrushPx.baselineMin = newBaselineMin; - newBrushPx.baselineMax = newBaselineMax; + } + + const snappedWindowParameters = snapTimestamps + ? getSnappedWindowParameters(newWindowParameters, snapTimestamps) + : newWindowParameters; + + const newBrushPx = { + baselineMin: x(snappedWindowParameters.baselineMin) ?? 0, + baselineMax: x(snappedWindowParameters.baselineMax) ?? 0, + deviationMin: x(snappedWindowParameters.deviationMin) ?? 0, + deviationMax: x(snappedWindowParameters.deviationMax) ?? 0, + }; + if ( + id === 'baseline' && + (baselineSelection[0] !== newBrushPx.baselineMin || + baselineSelection[1] !== newBrushPx.baselineMax) + ) { + d3.select(this) + .transition() + .duration(200) + // @ts-expect-error call doesn't allow the brush move function + .call(brushes.current[0].brush.move, [ + newBrushPx.baselineMin, + newBrushPx.baselineMax, + ]); + } + + if ( + id === 'deviation' && + (deviationSelection[0] !== newBrushPx.deviationMin || + deviationSelection[1] !== newBrushPx.deviationMax) + ) { d3.select(this) .transition() .duration(200) // @ts-expect-error call doesn't allow the brush move function - .call(brushes.current[0].brush.move, [newBaselineMin, newBaselineMax]); + .call(brushes.current[1].brush.move, [ + newBrushPx.deviationMin, + newBrushPx.deviationMax, + ]); } - brushes.current[0].start = newWindowParameters.baselineMin; - brushes.current[0].end = newWindowParameters.baselineMax; - brushes.current[1].start = newWindowParameters.deviationMin; - brushes.current[1].end = newWindowParameters.deviationMax; + brushes.current[0].start = snappedWindowParameters.baselineMin; + brushes.current[0].end = snappedWindowParameters.baselineMax; + brushes.current[1].start = snappedWindowParameters.deviationMin; + brushes.current[1].end = snappedWindowParameters.deviationMax; if (onChange) { - onChange(newWindowParameters, newBrushPx); + onChange(snappedWindowParameters, newBrushPx); } drawBrushes(); } @@ -199,7 +221,7 @@ export function DualBrush({ .insert('g', '.brush') .attr('class', 'brush') .attr('id', (b: DualBrush) => { - return 'brush-' + b.id; + return 'aiops-brush-' + b.id; }) .each((brushObject: DualBrush, i, n) => { const x = d3.scaleLinear().domain([min, max]).rangeRound([0, widthRef.current]); @@ -255,7 +277,17 @@ export function DualBrush({ drawBrushes(); } - }, [min, max, width, baselineMin, baselineMax, deviationMin, deviationMax, onChange]); + }, [ + min, + max, + width, + baselineMin, + baselineMax, + deviationMin, + deviationMax, + snapTimestamps, + onChange, + ]); return ( <> diff --git a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx index 5130a511a92c3..be82f11f778b2 100644 --- a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx +++ b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx @@ -5,7 +5,14 @@ * 2.0. */ -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiProgress, EuiText } from '@elastic/eui'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiProgress, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; @@ -19,6 +26,7 @@ interface ProgressControlProps { onRefresh: () => void; onCancel: () => void; isRunning: boolean; + shouldRerunAnalysis: boolean; } export function ProgressControls({ @@ -27,6 +35,7 @@ export function ProgressControls({ onRefresh, onCancel, isRunning, + shouldRerunAnalysis, }: ProgressControlProps) { return ( @@ -56,11 +65,34 @@ export function ProgressControls({ {!isRunning && ( - - + + + + + + {shouldRerunAnalysis && ( + <> + + + + + )} + )} {isRunning && ( diff --git a/x-pack/packages/ml/aiops_utils/src/fetch_stream.ts b/x-pack/packages/ml/aiops_utils/src/fetch_stream.ts index 4a1ed282c3ee7..d8d71adc81bc3 100644 --- a/x-pack/packages/ml/aiops_utils/src/fetch_stream.ts +++ b/x-pack/packages/ml/aiops_utils/src/fetch_stream.ts @@ -44,17 +44,24 @@ export async function* fetchStream | Array> | undefined] > { - const stream = await fetch(endpoint, { - signal: abortCtrl.current.signal, - method: 'POST', - headers: { - // This refers to the format of the request body, - // not the response, which will be a uint8array Buffer. - 'Content-Type': 'application/json', - 'kbn-xsrf': 'stream', - }, - ...(Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}), - }); + let stream: Response; + + try { + stream = await fetch(endpoint, { + signal: abortCtrl.current.signal, + method: 'POST', + headers: { + // This refers to the format of the request body, + // not the response, which will be a uint8array Buffer. + 'Content-Type': 'application/json', + 'kbn-xsrf': 'stream', + }, + ...(Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}), + }); + } catch (error) { + yield [error.toString(), undefined]; + return; + } if (!stream.ok) { yield [`Error ${stream.status}: ${stream.statusText}`, undefined]; diff --git a/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts b/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts index 0cdcf891b053f..9408e360ba5eb 100644 --- a/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts +++ b/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts @@ -65,3 +65,63 @@ export const getWindowParameters = ( deviationMax: Math.round(deviationMax), }; }; + +/** + * + * Converts window paramaters from the brushes to “snap” the brushes to the chart histogram bar width and ensure timestamps + * correspond to bucket timestamps + * + * @param windowParameters time range definition for baseline and deviation to be used by spike log analysis + * @param snapTimestamps time range definition that always corresponds to histogram bucket timestamps + * @returns WindowParameters + */ +export const getSnappedWindowParameters = ( + windowParameters: WindowParameters, + snapTimestamps: number[] +): WindowParameters => { + const snappedBaselineMin = snapTimestamps.reduce((pts, cts) => { + if ( + Math.abs(cts - windowParameters.baselineMin) < Math.abs(pts - windowParameters.baselineMin) + ) { + return cts; + } + return pts; + }, snapTimestamps[0]); + const baselineMaxTimestamps = snapTimestamps.filter((ts) => ts > snappedBaselineMin); + + const snappedBaselineMax = baselineMaxTimestamps.reduce((pts, cts) => { + if ( + Math.abs(cts - windowParameters.baselineMax) < Math.abs(pts - windowParameters.baselineMax) + ) { + return cts; + } + return pts; + }, baselineMaxTimestamps[0]); + const deviationMinTss = baselineMaxTimestamps.filter((ts) => ts > snappedBaselineMax); + + const snappedDeviationMin = deviationMinTss.reduce((pts, cts) => { + if ( + Math.abs(cts - windowParameters.deviationMin) < Math.abs(pts - windowParameters.deviationMin) + ) { + return cts; + } + return pts; + }, deviationMinTss[0]); + const deviationMaxTss = deviationMinTss.filter((ts) => ts > snappedDeviationMin); + + const snappedDeviationMax = deviationMaxTss.reduce((pts, cts) => { + if ( + Math.abs(cts - windowParameters.deviationMax) < Math.abs(pts - windowParameters.deviationMax) + ) { + return cts; + } + return pts; + }, deviationMaxTss[0]); + + return { + baselineMin: snappedBaselineMin, + baselineMax: snappedBaselineMax, + deviationMin: snappedDeviationMin, + deviationMax: snappedDeviationMax, + }; +}; diff --git a/x-pack/packages/ml/aiops_utils/src/index.ts b/x-pack/packages/ml/aiops_utils/src/index.ts index a02ecc2d41958..554d7cf23b0c2 100644 --- a/x-pack/packages/ml/aiops_utils/src/index.ts +++ b/x-pack/packages/ml/aiops_utils/src/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { getWindowParameters } from './get_window_parameters'; +export { getSnappedWindowParameters, getWindowParameters } from './get_window_parameters'; export type { WindowParameters } from './get_window_parameters'; export { streamFactory } from './stream_factory'; export { useFetchStream } from './use_fetch_stream'; diff --git a/x-pack/packages/ml/aiops_utils/src/use_fetch_stream.ts b/x-pack/packages/ml/aiops_utils/src/use_fetch_stream.ts index 13436efc6d471..516a63e165ffb 100644 --- a/x-pack/packages/ml/aiops_utils/src/use_fetch_stream.ts +++ b/x-pack/packages/ml/aiops_utils/src/use_fetch_stream.ts @@ -41,7 +41,7 @@ interface UseFetchStreamReturnType { cancel: () => void; data: Data; dispatch: Dispatch; - error: string | undefined; + errors: string[]; isCancelled: boolean; isRunning: boolean; start: () => Promise; @@ -76,7 +76,7 @@ export function useFetchStream } ): UseFetchStreamReturnType, ReducerAction> { - const [error, setError] = useState(); + const [errors, setErrors] = useState([]); const [isCancelled, setIsCancelled] = useState(false); const [isRunning, setIsRunning] = useState(false); @@ -87,13 +87,17 @@ export function useFetchStream { + setErrors((prevErrors) => [...prevErrors, error]); + }; + const start = async () => { if (isRunning) { - setError('Restart not supported yet.'); + addError('Restart not supported yet.'); return; } - setError(undefined); + setErrors([]); setIsRunning(true); setIsCancelled(false); @@ -104,7 +108,7 @@ export function useFetchStream(endpoint, abortCtrl, body, options !== undefined)) { if (fetchStreamError !== null) { - setError(fetchStreamError); + addError(fetchStreamError); } else if (actions.length > 0) { dispatch(actions as ReducerAction); } @@ -128,7 +132,7 @@ export function useFetchStream { let service: ExternalService; @@ -66,8 +65,13 @@ describe('Cases webhook service', () => { logger, configurationUtilities ); + jest.useFakeTimers('modern'); + jest.setSystemTime(mockTime); }); + afterAll(() => { + jest.useRealTimers(); + }); beforeEach(() => { jest.clearAllMocks(); }); @@ -130,8 +134,6 @@ describe('Cases webhook service', () => { fields: { title: 'title', description: 'description', - created: '2021-10-20T19:41:02.754+0300', - updated: '2021-10-20T19:41:02.754+0300', }, }, }; @@ -142,8 +144,6 @@ describe('Cases webhook service', () => { expect(res).toEqual({ id: '1', title: 'CK-1', - createdAt: '2021-10-20T19:41:02.754+0300', - updatedAt: '2021-10-20T19:41:02.754+0300', }); }); @@ -186,7 +186,7 @@ describe('Cases webhook service', () => { ); await expect(service.getIncident('1')).rejects.toThrow( - '[Action][Webhook - Case Management]: Unable to get case with id 1. Error: Response is missing the expected fields: fields.created, key, fields.updated' + '[Action][Webhook - Case Management]: Unable to get case with id 1. Error: Response is missing the expected field: key' ); }); }); @@ -215,7 +215,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - fields: { created: '2020-04-27T10:59:46.202Z', updated: '2020-04-27T10:59:46.202Z' }, }, }) ); @@ -229,7 +228,7 @@ describe('Cases webhook service', () => { expect(res).toEqual({ title: 'CK-1', id: '1', - pushedDate: '2020-04-27T10:59:46.202Z', + pushedDate: mockTime.toISOString(), url: 'https://coolsite.net/browse/CK-1', }); }); @@ -240,7 +239,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - fields: { created: '2020-04-27T10:59:46.202Z' }, }, }) ); @@ -250,7 +248,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - fields: { created: '2020-04-27T10:59:46.202Z', updated: '2020-04-27T10:59:46.202Z' }, }, }) ); @@ -314,7 +311,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - fields: { created: '2020-04-27T10:59:46.202Z', updated: '2020-04-27T10:59:46.202Z' }, }, }) ); @@ -324,7 +320,7 @@ describe('Cases webhook service', () => { expect(res).toEqual({ title: 'CK-1', id: '1', - pushedDate: '2020-04-27T10:59:46.202Z', + pushedDate: mockTime.toISOString(), url: 'https://coolsite.net/browse/CK-1', }); }); @@ -335,7 +331,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - fields: { created: '2020-04-27T10:59:46.202Z', updated: '2020-04-27T10:59:46.202Z' }, }, }) ); @@ -397,7 +392,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - created: '2020-04-27T10:59:46.202Z', }, }) ); @@ -413,7 +407,6 @@ describe('Cases webhook service', () => { data: { id: '1', key: 'CK-1', - created: '2020-04-27T10:59:46.202Z', }, }) ); @@ -648,10 +641,6 @@ describe('Cases webhook service', () => { data: { id: '../../malicious-app/malicious-endpoint/', key: '../../malicious-app/malicious-endpoint/', - fields: { - updated: '2020-04-27T10:59:46.202Z', - created: '2020-04-27T10:59:46.202Z', - }, }, }) ); diff --git a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/service.ts b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/service.ts index 26551200a3b69..ef8879b8e574b 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/service.ts @@ -14,7 +14,6 @@ import { renderMustacheStringNoEscape } from '../../lib/mustache_renderer'; import { createServiceError, getObjectValueByKeyAsString, - getPushedDate, stringifyObjValues, removeSlash, throwDescriptiveErrorIfResponseIsNotValid, @@ -49,9 +48,7 @@ export const createExternalService = ( createIncidentMethod, createIncidentResponseKey, createIncidentUrl: createIncidentUrlConfig, - getIncidentResponseCreatedDateKey, getIncidentResponseExternalTitleKey, - getIncidentResponseUpdatedDateKey, getIncidentUrl, hasAuth, headers, @@ -107,17 +104,11 @@ export const createExternalService = ( throwDescriptiveErrorIfResponseIsNotValid({ res, - requiredAttributesToBeInTheResponse: [ - getIncidentResponseCreatedDateKey, - getIncidentResponseExternalTitleKey, - getIncidentResponseUpdatedDateKey, - ], + requiredAttributesToBeInTheResponse: [getIncidentResponseExternalTitleKey], }); const title = getObjectValueByKeyAsString(res.data, getIncidentResponseExternalTitleKey)!; - const createdAt = getObjectValueByKeyAsString(res.data, getIncidentResponseCreatedDateKey)!; - const updatedAt = getObjectValueByKeyAsString(res.data, getIncidentResponseUpdatedDateKey)!; - return { id, title, createdAt, updatedAt }; + return { id, title }; } catch (error) { throw createServiceError(error, `Unable to get case with id ${id}`); } @@ -180,7 +171,7 @@ export const createExternalService = ( id: externalId, title: insertedIncident.title, url: normalizedViewUrl, - pushedDate: getPushedDate(insertedIncident.createdAt), + pushedDate: new Date().toISOString(), }; } catch (error) { throw createServiceError(error, 'Unable to create case'); @@ -250,7 +241,7 @@ export const createExternalService = ( id: incidentId, title: updatedIncident.title, url: normalizedViewUrl, - pushedDate: getPushedDate(updatedIncident.updatedAt), + pushedDate: new Date().toISOString(), }; } catch (error) { throw createServiceError(error, `Unable to update case with id ${incidentId}`); diff --git a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/types.ts b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/types.ts index 1ea2b515e3ecd..095b07fd4c9f7 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/types.ts @@ -108,8 +108,6 @@ export interface ExternalServiceCommentResponse { export interface GetIncidentResponse { id: string; title: string; - createdAt: string; - updatedAt: string; } export interface ExternalServiceApi { diff --git a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/utils.ts index 5833db7b6358e..12a5ab0f64021 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/utils.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/cases_webhook/utils.ts @@ -7,7 +7,7 @@ import { AxiosResponse, AxiosError } from 'axios'; import { isEmpty, isObjectLike, get } from 'lodash'; -import { addTimeZoneToDate, getErrorMessage } from '../lib/axios_utils'; +import { getErrorMessage } from '../lib/axios_utils'; import * as i18n from './translations'; export const createServiceError = (error: AxiosError, message: string) => { @@ -24,17 +24,6 @@ export const createServiceError = (error: AxiosError, message: string) => { ); }; -export const getPushedDate = (timestamp?: string) => { - if (timestamp != null && new Date(timestamp).getTime() > 0) { - try { - return new Date(timestamp).toISOString(); - } catch (e) { - return new Date(addTimeZoneToDate(timestamp)).toISOString(); - } - } - return new Date().toISOString(); -}; - export const getObjectValueByKeyAsString = ( obj: Record | unknown>, key: string 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 78ec9c7c33432..74b004a0c7436 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 @@ -10,7 +10,7 @@ import type { ChangePoint, ChangePointHistogram } from '@kbn/ml-agg-utils'; export const API_ACTION_NAME = { ADD_CHANGE_POINTS: 'add_change_points', ADD_CHANGE_POINTS_HISTOGRAM: 'add_change_points_histogram', - ERROR: 'error', + ADD_ERROR: 'add_error', RESET: 'reset', UPDATE_LOADING_STATE: 'update_loading_state', } as const; @@ -44,14 +44,14 @@ export function addChangePointsHistogramAction( }; } -interface ApiActionError { - type: typeof API_ACTION_NAME.ERROR; +interface ApiActionAddError { + type: typeof API_ACTION_NAME.ADD_ERROR; payload: string; } -export function errorAction(payload: ApiActionError['payload']): ApiActionError { +export function addErrorAction(payload: ApiActionAddError['payload']): ApiActionAddError { return { - type: API_ACTION_NAME.ERROR, + type: API_ACTION_NAME.ADD_ERROR, payload, }; } @@ -85,6 +85,6 @@ export function updateLoadingStateAction( export type AiopsExplainLogRateSpikesApiAction = | ApiActionAddChangePoints | ApiActionAddChangePointsHistogram - | ApiActionError + | ApiActionAddError | 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 e03a527eb576f..91b5e3acf2282 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 @@ -8,7 +8,7 @@ export { addChangePointsAction, addChangePointsHistogramAction, - errorAction, + addErrorAction, resetAction, updateLoadingStateAction, API_ACTION_NAME, diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts index 483de40e685d6..260e3b12afaeb 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts @@ -24,6 +24,7 @@ describe('streamReducer', () => { loaded: 50, loadingState: 'Loaded 50%', changePoints: [], + errors: [], }); }); diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.ts b/x-pack/plugins/aiops/common/api/stream_reducer.ts index 597527e80daef..4a19ecfdd035e 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.ts @@ -12,6 +12,7 @@ import { API_ACTION_NAME, AiopsExplainLogRateSpikesApiAction } from './explain_l interface StreamState { ccsWarning: boolean; changePoints: ChangePoint[]; + errors: string[]; loaded: number; loadingState: string; } @@ -19,6 +20,7 @@ interface StreamState { export const initialState: StreamState = { ccsWarning: false, changePoints: [], + errors: [], loaded: 0, loadingState: '', }; @@ -45,6 +47,8 @@ export function streamReducer( return cp; }); return { ...state, changePoints }; + case API_ACTION_NAME.ADD_ERROR: + return { ...state, errors: [...state.errors, action.payload] }; case API_ACTION_NAME.RESET: return initialState; case API_ACTION_NAME.UPDATE_LOADING_STATE: diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/brush_badge.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/brush_badge.tsx new file mode 100644 index 0000000000000..3f1039a0365f3 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/brush_badge.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; + +import { EuiBadge, EuiText, EuiToolTip } from '@elastic/eui'; +// @ts-ignore +import { formatDate } from '@elastic/eui/lib/services/format'; + +const DATE_FORMAT = 'YYYY-MM-DD'; +const TIME_FORMAT = 'HH:mm:ss'; + +interface BrushBadgeProps { + label: string; + marginLeft: number; + timestampFrom: number; + timestampTo: number; + width: number; +} + +export const BrushBadge: FC = ({ + label, + marginLeft, + timestampFrom, + timestampTo, + width, +}) => { + // If "from" and "to" are on the same day, we skip displaying the date twice. + const dateFrom = formatDate(timestampFrom, DATE_FORMAT); + const dateTo = formatDate(timestampTo, DATE_FORMAT); + const timeFrom = formatDate(timestampFrom, TIME_FORMAT); + const timeTo = formatDate(timestampTo, TIME_FORMAT); + + return ( +
+ + {dateFrom} {timeFrom} -{' '} + {dateFrom !== dateTo && ( + <> +
+ {dateTo}{' '} + + )} + {timeTo} + + } + position="top" + > + {label} +
+
+ ); +}; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx index 4f2c723791e1c..79f5494b419ca 100644 --- a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -20,19 +20,19 @@ import { XYChartElementEvent, XYBrushEvent, } from '@elastic/charts'; -import { EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { IUiSettingsClient } from '@kbn/core/public'; import { DualBrush, DualBrushAnnotation } from '@kbn/aiops-components'; -import { getWindowParameters } from '@kbn/aiops-utils'; +import { getSnappedWindowParameters, getWindowParameters } from '@kbn/aiops-utils'; import type { WindowParameters } from '@kbn/aiops-utils'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; import type { ChangePoint } from '@kbn/ml-agg-utils'; import { useAiOpsKibana } from '../../../kibana_context'; +import { BrushBadge } from './brush_badge'; + export interface DocumentCountChartPoint { time: number | string; value: number; @@ -52,6 +52,9 @@ interface DocumentCountChartProps { const SPEC_ID = 'document_count'; +const BADGE_HEIGHT = 20; +const BADGE_WIDTH = 75; + enum VIEW_MODE { ZOOM = 'zoom', BRUSH = 'brush', @@ -67,6 +70,19 @@ function getTimezone(uiSettings: IUiSettingsClient) { } } +function getBaselineBadgeOverflow( + windowParametersAsPixels: WindowParameters, + baselineBadgeWidth: number +) { + const { baselineMin, baselineMax, deviationMin } = windowParametersAsPixels; + + const baselineBrushWidth = baselineMax - baselineMin; + const baselineBadgeActualMax = baselineMin + baselineBadgeWidth; + return deviationMin < baselineBadgeActualMax + ? Math.max(0, baselineBadgeWidth - baselineBrushWidth) + : 0; +} + export const DocumentCountChart: FC = ({ brushSelectionUpdateHandler, width, @@ -148,6 +164,14 @@ export const DocumentCountChart: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartPointsSplit, timeRangeEarliest, timeRangeLatest, interval]); + const snapTimestamps = useMemo(() => { + return adjustedChartPoints + .map((d) => d.time) + .filter(function (arg: unknown): arg is number { + return typeof arg === 'number'; + }); + }, [adjustedChartPoints]); + const timefilterUpdateHandler = useCallback( (ranges: { from: number; to: number }) => { data.query.timefilter.timefilter.setTime({ @@ -189,9 +213,10 @@ export const DocumentCountChart: FC = ({ xDomain.min, xDomain.max + interval ); - setOriginalWindowParameters(wp); - setWindowParameters(wp); - brushSelectionUpdateHandler(wp, true); + const wpSnap = getSnappedWindowParameters(wp, snapTimestamps); + setOriginalWindowParameters(wpSnap); + setWindowParameters(wpSnap); + brushSelectionUpdateHandler(wpSnap, true); } } }; @@ -230,47 +255,45 @@ export const DocumentCountChart: FC = ({ }, [viewMode]); const isBrushVisible = - originalWindowParameters && mlBrushMarginLeft && mlBrushWidth && mlBrushWidth > 0; + originalWindowParameters && + windowParameters && + mlBrushMarginLeft && + mlBrushWidth && + mlBrushWidth > 0; + + // Avoid overlap of brush badges when the brushes are quite narrow. + const baselineBadgeOverflow = windowParametersAsPixels + ? getBaselineBadgeOverflow(windowParametersAsPixels, BADGE_WIDTH) + : 0; + const baselineBadgeMarginLeft = + (mlBrushMarginLeft ?? 0) + (windowParametersAsPixels?.baselineMin ?? 0); return ( <> {isBrushVisible && (
-
- - - -
-
- - - +
+ +
@@ -280,6 +303,7 @@ export const DocumentCountChart: FC = ({ max={timeRangeLatest + interval} onChange={onWindowParametersChange} marginLeft={mlBrushMarginLeft} + snapTimestamps={snapTimestamps} width={mlBrushWidth} />
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 6bd060577a425..c9b74a634001e 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 @@ -5,12 +5,17 @@ * 2.0. */ -import React, { useEffect, FC } from 'react'; +import React, { useEffect, useMemo, useState, FC } from 'react'; +import { isEqual } from 'lodash'; + +import { EuiCallOut, EuiEmptyPrompt, EuiSpacer, EuiText } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import { ProgressControls } from '@kbn/aiops-components'; import { useFetchStream } from '@kbn/aiops-utils'; import type { WindowParameters } from '@kbn/aiops-utils'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { ChangePoint } from '@kbn/ml-agg-utils'; import type { Query } from '@kbn/es-query'; @@ -51,10 +56,17 @@ export const ExplainLogRateSpikesAnalysis: FC const { services } = useAiOpsKibana(); const basePath = services.http?.basePath.get() ?? ''; - const { cancel, start, data, isRunning, error } = useFetchStream< - ApiExplainLogRateSpikes, - typeof basePath - >( + const [currentAnalysisWindowParameters, setCurrentAnalysisWindowParameters] = useState< + WindowParameters | undefined + >(); + + const { + cancel, + start, + data, + isRunning, + errors: streamErrors, + } = useFetchStream( `${basePath}/internal/aiops/explain_log_rate_spikes`, { start: earliest, @@ -68,10 +80,7 @@ export const ExplainLogRateSpikesAnalysis: FC { reducer: streamReducer, initialState } ); - useEffect(() => { - start(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + const errors = useMemo(() => [...streamErrors, ...data.errors], [streamErrors, data.errors]); // Start handler clears possibly hovered or pinned // change points on analysis refresh. @@ -82,9 +91,26 @@ export const ExplainLogRateSpikesAnalysis: FC if (onSelectedChangePoint) { onSelectedChangePoint(null); } + + setCurrentAnalysisWindowParameters(windowParameters); start(); } + useEffect(() => { + setCurrentAnalysisWindowParameters(windowParameters); + start(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const shouldRerunAnalysis = useMemo( + () => + currentAnalysisWindowParameters !== undefined && + !isEqual(currentAnalysisWindowParameters, windowParameters), + [currentAnalysisWindowParameters, windowParameters] + ); + + const showSpikeAnalysisTable = data?.changePoints.length > 0; + return ( <> isRunning={isRunning} onRefresh={startHandler} onCancel={cancel} + shouldRerunAnalysis={shouldRerunAnalysis} /> - {data?.changePoints ? ( + + {!isRunning && !showSpikeAnalysisTable && ( + + +

+ } + titleSize="xs" + body={ +

+ +

+ } + /> + )} + {errors.length > 0 && ( + <> + + + {errors.length === 1 ? ( +

{errors[0]}

+ ) : ( +
    + {errors.map((e, i) => ( +
  • {e}
  • + ))} +
+ )} +
+
+ + + )} + {showSpikeAnalysisTable && ( - ) : null} + )} ); }; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index 7bb8049fe4dd1..4b7d2bb1d0597 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -29,15 +29,11 @@ import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transa const NARROW_COLUMN_WIDTH = '120px'; const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; -const noDataText = i18n.translate('xpack.aiops.correlations.correlationsTable.noDataText', { - defaultMessage: 'No data', -}); const DEFAULT_SORT_FIELD = 'pValue'; const DEFAULT_SORT_DIRECTION = 'asc'; interface SpikeAnalysisTableProps { changePoints: ChangePoint[]; - error?: string; loading: boolean; onPinnedChangePoint?: (changePoint: ChangePoint | null) => void; onSelectedChangePoint?: (changePoint: ChangePoint | null) => void; @@ -46,7 +42,6 @@ interface SpikeAnalysisTableProps { export const SpikeAnalysisTable: FC = ({ changePoints, - error, loading, onPinnedChangePoint, onSelectedChangePoint, @@ -206,16 +201,21 @@ export const SpikeAnalysisTable: FC = ({ }; }, [pageIndex, pageSize, sortField, sortDirection, changePoints]); + // Don't pass on the `loading` state to the table itself because + // it disables hovering events. Because the mini histograms take a while + // to load, hovering would not update the main chart. Instead, + // the loading state is shown by the progress bar on the outer component level. + // The outer component also will display a prompt when no data was returned + // running the analysis and will hide this table. + return ( } rowProps={(changePoint) => { return { 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 cb0911e1c53be..9fba2b9e100a5 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 @@ -21,7 +21,7 @@ import { addChangePointsAction, addChangePointsHistogramAction, aiopsExplainLogRateSpikesSchema, - errorAction, + addErrorAction, resetAction, updateLoadingStateAction, AiopsExplainLogRateSpikesApiAction, @@ -75,6 +75,23 @@ export const defineExplainLogRateSpikesRoute = ( logger ); + function endWithUpdatedLoadingState() { + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded: 1, + loadingState: i18n.translate( + 'xpack.aiops.explainLogRateSpikes.loadingState.doneMessage', + { + defaultMessage: 'Done.', + } + ), + }) + ); + + end(); + } + // Async IIFE to run the analysis while not blocking returning `responseWithHeaders`. (async () => { push(resetAction()); @@ -95,16 +112,12 @@ export const defineExplainLogRateSpikesRoute = ( try { fieldCandidates = await fetchFieldCandidates(client, request.body); } catch (e) { - push(errorAction(e.toString())); + push(addErrorAction(e.toString())); end(); return; } - if (fieldCandidates.length > 0) { - loaded += LOADED_FIELD_CANDIDATES; - } else { - loaded = 1; - } + loaded += LOADED_FIELD_CANDIDATES; push( updateLoadingStateAction({ @@ -123,7 +136,9 @@ export const defineExplainLogRateSpikesRoute = ( }) ); - if (shouldStop || fieldCandidates.length === 0) { + if (fieldCandidates.length === 0) { + endWithUpdatedLoadingState(); + } else if (shouldStop) { end(); return; } @@ -139,7 +154,7 @@ export const defineExplainLogRateSpikesRoute = ( try { pValues = await fetchChangePointPValues(client, request.body, fieldCandidatesChunk); } catch (e) { - push(errorAction(e.toString())); + push(addErrorAction(e.toString())); end(); return; } @@ -179,7 +194,7 @@ export const defineExplainLogRateSpikesRoute = ( } if (changePoints?.length === 0) { - end(); + endWithUpdatedLoadingState(); return; } @@ -274,20 +289,7 @@ export const defineExplainLogRateSpikesRoute = ( }); } - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded: 1, - loadingState: i18n.translate( - 'xpack.aiops.explainLogRateSpikes.loadingState.doneMessage', - { - defaultMessage: 'Done.', - } - ), - }) - ); - - end(); + endWithUpdatedLoadingState(); })(); return response.ok(responseWithHeaders); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index f5be4f0fcd34e..c9f127a63d8c5 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -142,7 +142,35 @@ describe('getExecutionLogAggregation', () => { }, }, aggs: { - executionUuidCardinality: { cardinality: { field: 'kibana.alert.rule.execution.uuid' } }, + executionUuidCardinality: { + aggs: { + executionUuidCardinality: { + cardinality: { field: 'kibana.alert.rule.execution.uuid' }, + }, + }, + filter: { + bool: { + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + }, executionUuid: { terms: { field: 'kibana.alert.rule.execution.uuid', @@ -175,12 +203,28 @@ describe('getExecutionLogAggregation', () => { }, aggs: { actionOutcomes: { terms: { field: 'event.outcome', size: 2 } } }, }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, ruleExecution: { filter: { bool: { must: [ - { match: { 'event.action': 'execute' } }, - { match: { 'event.provider': 'alerting' } }, + { + bool: { + must: [ + { match: { 'event.action': 'execute' } }, + { match: { 'event.provider': 'alerting' } }, + ], + }, + }, ], }, }, @@ -448,7 +492,9 @@ describe('formatExecutionLogResult', () => { ], }, executionUuidCardinality: { - value: 374, + executionUuidCardinality: { + value: 374, + }, }, }, }, @@ -683,7 +729,9 @@ describe('formatExecutionLogResult', () => { ], }, executionUuidCardinality: { - value: 374, + executionUuidCardinality: { + value: 374, + }, }, }, }, @@ -910,7 +958,9 @@ describe('formatExecutionLogResult', () => { ], }, executionUuidCardinality: { - value: 374, + executionUuidCardinality: { + value: 374, + }, }, }, }, @@ -1142,7 +1192,9 @@ describe('formatExecutionLogResult', () => { ], }, executionUuidCardinality: { - value: 417, + executionUuidCardinality: { + value: 417, + }, }, }, }, diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index aa8a7f6de88cf..7365c9d125d00 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -9,6 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import Boom from '@hapi/boom'; import { flatMap, get } from 'lodash'; import { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { parseDuration } from '.'; import { IExecutionLog, IExecutionLogResult } from '../../common'; @@ -74,9 +75,12 @@ export interface ExecutionUuidAggResult interface ExcludeExecuteStartAggResult extends estypes.AggregationsAggregateBase { executionUuid: ExecutionUuidAggResult; - executionUuidCardinality: estypes.AggregationsCardinalityAggregate; + executionUuidCardinality: { + executionUuidCardinality: estypes.AggregationsCardinalityAggregate; + }; } export interface IExecutionLogAggOptions { + filter?: string; page: number; perPage: number; sort: estypes.Sort; @@ -95,7 +99,12 @@ const ExecutionLogSortFields: Record = { num_new_alerts: 'ruleExecution>numNewAlerts', }; -export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLogAggOptions) { +export function getExecutionLogAggregation({ + filter, + page, + perPage, + sort, +}: IExecutionLogAggOptions) { // Check if valid sort fields const sortFields = flatMap(sort as estypes.SortCombinations[], (s) => Object.keys(s)); for (const field of sortFields) { @@ -118,6 +127,13 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo throw Boom.badRequest(`Invalid perPage field "${perPage}" - must be greater than 0`); } + let dslFilterQuery: estypes.QueryDslBoolQuery['filter']; + try { + dslFilterQuery = filter ? toElasticsearchQuery(fromKueryExpression(filter)) : undefined; + } catch (err) { + throw Boom.badRequest(`Invalid kuery syntax for filter ${filter}`); + } + return { excludeExecuteStart: { filter: { @@ -134,8 +150,18 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo aggs: { // Get total number of executions executionUuidCardinality: { - cardinality: { - field: EXECUTION_UUID_FIELD, + filter: { + bool: { + ...(dslFilterQuery ? { filter: dslFilterQuery } : {}), + must: [getProviderAndActionFilter('alerting', 'execute')], + }, + }, + aggs: { + executionUuidCardinality: { + cardinality: { + field: EXECUTION_UUID_FIELD, + }, + }, }, }, executionUuid: { @@ -169,7 +195,12 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo }, // Filter by rule execute doc and get information from this event ruleExecution: { - filter: getProviderAndActionFilter('alerting', 'execute'), + filter: { + bool: { + ...(dslFilterQuery ? { filter: dslFilterQuery } : {}), + must: [getProviderAndActionFilter('alerting', 'execute')], + }, + }, aggs: { executeStartTime: { min: { @@ -235,6 +266,17 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo timeoutMessage: { filter: getProviderAndActionFilter('alerting', 'execute-timeout'), }, + // Filter out execution UUID buckets where ruleExecution doc count is 0 + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, }, }, }, @@ -315,7 +357,7 @@ export function formatExecutionLogResult( const aggs = aggregations.excludeExecuteStart as ExcludeExecuteStartAggResult; - const total = aggs.executionUuidCardinality.value; + const total = aggs.executionUuidCardinality.executionUuidCardinality.value; const buckets = aggs.executionUuid.buckets; return { 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 f87f6a886eaf9..35462f9cc4e75 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -856,8 +856,8 @@ export class RulesClient { { start: parsedDateStart.toISOString(), end: parsedDateEnd.toISOString(), - filter, aggs: getExecutionLogAggregation({ + filter, page, perPage, sort, @@ -1796,7 +1796,7 @@ export class RulesClient { let result; try { - result = await this.unsecuredSavedObjectsClient.bulkUpdate(rules); + result = await this.unsecuredSavedObjectsClient.bulkCreate(rules, { overwrite: true }); } catch (e) { // avoid unused newly generated API keys if (apiKeysMap.size > 0) { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index 78e56c7df3263..f0ac3781bce1e 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -124,7 +124,7 @@ describe('bulkEdit()', () => { mockCreatePointInTimeFinderAsInternalUser(); - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [existingRule], }); @@ -145,7 +145,7 @@ describe('bulkEdit()', () => { }); describe('tags operations', () => { test('should add new tag', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -184,21 +184,23 @@ describe('bulkEdit()', () => { expect(result.rules).toHaveLength(1); expect(result.rules[0]).toHaveProperty('tags', ['foo', 'test-1']); - expect(unsecuredSavedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0]).toHaveLength(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toEqual([ - expect.objectContaining({ - id: '1', - type: 'alert', - attributes: expect.objectContaining({ - tags: ['foo', 'test-1'], + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + tags: ['foo', 'test-1'], + }), }), - }), - ]); + ], + { overwrite: true } + ); }); test('should delete tag', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -234,21 +236,23 @@ describe('bulkEdit()', () => { expect(result.rules[0]).toHaveProperty('tags', []); - expect(unsecuredSavedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0]).toHaveLength(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toEqual([ - expect.objectContaining({ - id: '1', - type: 'alert', - attributes: expect.objectContaining({ - tags: [], + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + tags: [], + }), }), - }), - ]); + ], + { overwrite: true } + ); }); test('should set tags', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -284,17 +288,19 @@ describe('bulkEdit()', () => { expect(result.rules[0]).toHaveProperty('tags', ['test-1', 'test-2']); - expect(unsecuredSavedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0]).toHaveLength(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toEqual([ - expect.objectContaining({ - id: '1', - type: 'alert', - attributes: expect.objectContaining({ - tags: ['test-1', 'test-2'], + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + tags: ['test-1', 'test-2'], + }), }), - }), - ]); + ], + { overwrite: true } + ); }); }); @@ -574,7 +580,7 @@ describe('bulkEdit()', () => { ); }); - test('should call bulkMarkApiKeysForInvalidation to invalidate unused keys if bulkUpdate failed', async () => { + test('should call bulkMarkApiKeysForInvalidation to invalidate unused keys if bulkCreate failed', async () => { createAPIKeyMock.mockReturnValue({ apiKeysEnabled: true, result: { api_key: '111' } }); mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [ @@ -585,7 +591,7 @@ describe('bulkEdit()', () => { ], }); - unsecuredSavedObjectsClient.bulkUpdate.mockImplementation(() => { + unsecuredSavedObjectsClient.bulkCreate.mockImplementation(() => { throw new Error('Fail'); }); @@ -621,7 +627,7 @@ describe('bulkEdit()', () => { ], }); - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -795,7 +801,7 @@ describe('bulkEdit()', () => { minimumScheduleInterval: { value: '3m', enforce: true }, }); - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [], }); @@ -819,7 +825,7 @@ describe('bulkEdit()', () => { describe('paramsModifier', () => { test('should update index pattern params', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -856,19 +862,21 @@ describe('bulkEdit()', () => { expect(result.rules).toHaveLength(1); expect(result.rules[0]).toHaveProperty('params.index', ['test-index-*']); - expect(unsecuredSavedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0]).toHaveLength(1); - expect(unsecuredSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toEqual([ - expect.objectContaining({ - id: '1', - type: 'alert', - attributes: expect.objectContaining({ - params: expect.objectContaining({ - index: ['test-index-*'], + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + params: expect.objectContaining({ + index: ['test-index-*'], + }), }), }), - }), - ]); + ], + { overwrite: true } + ); }); }); @@ -893,8 +901,8 @@ describe('bulkEdit()', () => { }); describe('task manager', () => { - test('should call task manager method bulkUpdateSchedules if operation set new schedules', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + test('should call task manager method bulkCreateSchedules if operation set new schedules', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', @@ -932,8 +940,8 @@ describe('bulkEdit()', () => { }); }); - test('should not call task manager method bulkUpdateSchedules if operation is not set schedule', async () => { - unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({ + test('should not call task manager method bulkCreateSchedules if operation is not set schedule', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [ { id: '1', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index 7f14ec3681bb1..5cdf4c7744220 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -273,7 +273,9 @@ const aggregateResults = { ], }, executionUuidCardinality: { - value: 374, + executionUuidCardinality: { + value: 374, + }, }, }, }, @@ -453,9 +455,9 @@ describe('getExecutionLogForRule()', () => { aggs: getExecutionLogAggregation({ page: 1, perPage: 10, + filter: 'event.outcome: success', sort: [{ timestamp: { order: 'desc' } }], }), - filter: 'event.outcome: success', end: mockedDateString, start: '2019-02-12T20:01:22.479Z', }, diff --git a/x-pack/plugins/apm/ftr_e2e/apis/fixtures/package_registry_config.yml b/x-pack/plugins/apm/ftr_e2e/apis/fixtures/package_registry_config.yml index 9f2300dedc82b..a6c51976af986 100644 --- a/x-pack/plugins/apm/ftr_e2e/apis/fixtures/package_registry_config.yml +++ b/x-pack/plugins/apm/ftr_e2e/apis/fixtures/package_registry_config.yml @@ -1,4 +1,2 @@ package_paths: - - /packages/production - - /packages/snapshot - - /packages/test-packages + - /packages/package-storage \ No newline at end of file diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index aa8a2e14e42c4..d8ff2086fd291 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -24,6 +24,7 @@ "optionalPlugins": [ "actions", "alerting", + "cases", "cloud", "fleet", "home", diff --git a/x-pack/plugins/apm/server/routes/services/annotations/index.test.ts b/x-pack/plugins/apm/server/routes/services/annotations/index.test.ts index 681bc5a8b5ded..481ab22ece4cc 100644 --- a/x-pack/plugins/apm/server/routes/services/annotations/index.test.ts +++ b/x-pack/plugins/apm/server/routes/services/annotations/index.test.ts @@ -16,7 +16,8 @@ import * as GetStoredAnnotations from './get_stored_annotations'; import { Annotation, AnnotationType } from '../../../../common/annotations'; import { errors } from '@elastic/elasticsearch'; -describe('getServiceAnnotations', () => { +// FLAKY: https://github.com/elastic/kibana/issues/138039 +describe.skip('getServiceAnnotations', () => { const storedAnnotations = [ { type: AnnotationType.VERSION, diff --git a/x-pack/plugins/canvas/shareable_runtime/webpack/ci_stats_plugin.ts b/x-pack/plugins/canvas/shareable_runtime/webpack/ci_stats_plugin.ts index 20facc666f47c..fb1e93ddbe956 100644 --- a/x-pack/plugins/canvas/shareable_runtime/webpack/ci_stats_plugin.ts +++ b/x-pack/plugins/canvas/shareable_runtime/webpack/ci_stats_plugin.ts @@ -41,7 +41,7 @@ export class CiStatsPlugin { return; } - compiler.hooks.emit.tapAsync('CiStatsPlugin', async (compilation) => { + compiler.hooks.emit.tapPromise('CiStatsPlugin', async (compilation) => { const { entryName } = this.options; const assets = Object.entries(compilation.assets) diff --git a/x-pack/plugins/cases/common/api/cases/assignee.ts b/x-pack/plugins/cases/common/api/cases/assignee.ts new file mode 100644 index 0000000000000..6f6a23870066b --- /dev/null +++ b/x-pack/plugins/cases/common/api/cases/assignee.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { CaseUserProfileRt } from './user_profiles'; + +export const CaseAssigneesRt = rt.array(CaseUserProfileRt); + +export type CaseAssignees = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index ec855d98e7144..a6b81dc42af74 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -12,6 +12,7 @@ import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; import { CasesStatusResponseRt, CaseStatusRt } from './status'; import { CaseConnectorRt } from '../connectors'; +import { CaseAssigneesRt } from './assignee'; const BucketsAggs = rt.array( rt.type({ @@ -86,6 +87,10 @@ const CaseBasicRt = rt.type({ * The severity of the case */ severity: CaseSeverityRt, + /** + * The users assigned to this case + */ + assignees: CaseAssigneesRt, }); /** @@ -153,6 +158,10 @@ export const CasePostRequestRt = rt.intersection([ owner: rt.string, }), rt.partial({ + /** + * The users assigned to the case + */ + assignees: CaseAssigneesRt, /** * The severity of the case. The severity is * default it to "low" if not provided. @@ -174,6 +183,10 @@ export const CasesFindRequestRt = rt.partial({ * The severity of the case */ severity: CaseSeverityRt, + /** + * The uids of the user profiles to filter by + */ + assignees: rt.union([rt.array(rt.string), rt.string]), /** * The reporters to filter by */ diff --git a/x-pack/plugins/cases/common/api/cases/index.ts b/x-pack/plugins/cases/common/api/cases/index.ts index 3a3eab2ca3198..2b561a1dff124 100644 --- a/x-pack/plugins/cases/common/api/cases/index.ts +++ b/x-pack/plugins/cases/common/api/cases/index.ts @@ -13,3 +13,4 @@ export * from './user_actions'; export * from './constants'; export * from './alerts'; export * from './user_profiles'; +export * from './assignee'; diff --git a/x-pack/plugins/cases/common/api/cases/suggest_user_profiles.ts b/x-pack/plugins/cases/common/api/cases/suggest_user_profiles.ts new file mode 100644 index 0000000000000..75cd1f9cb9f94 --- /dev/null +++ b/x-pack/plugins/cases/common/api/cases/suggest_user_profiles.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +export const SuggestUserProfilesRequestRt = rt.intersection([ + rt.type({ + name: rt.string, + owners: rt.array(rt.string), + }), + rt.partial({ size: rt.number }), +]); + +export type SuggestUserProfilesRequest = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/assignees.ts b/x-pack/plugins/cases/common/api/cases/user_actions/assignees.ts new file mode 100644 index 0000000000000..e4b8b8aa1f6e9 --- /dev/null +++ b/x-pack/plugins/cases/common/api/cases/user_actions/assignees.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { CaseAssigneesRt } from '../assignee'; +import { ActionTypes, UserActionWithAttributes } from './common'; + +export const AssigneesUserActionPayloadRt = rt.type({ assignees: CaseAssigneesRt }); + +export const AssigneesUserActionRt = rt.type({ + type: rt.literal(ActionTypes.assignees), + payload: AssigneesUserActionPayloadRt, +}); + +export type AssigneesUserAction = UserActionWithAttributes>; diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/common.ts b/x-pack/plugins/cases/common/api/cases/user_actions/common.ts index 5665ab524071a..5297d2ca46833 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/common.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/common.ts @@ -9,6 +9,7 @@ import * as rt from 'io-ts'; import { UserRT } from '../../user'; export const ActionTypes = { + assignees: 'assignees', comment: 'comment', connector: 'connector', description: 'description', @@ -22,6 +23,9 @@ export const ActionTypes = { delete_case: 'delete_case', } as const; +export type ActionTypeKeys = keyof typeof ActionTypes; +export type ActionTypeValues = typeof ActionTypes[ActionTypeKeys]; + export const Actions = { add: 'add', create: 'create', @@ -30,6 +34,9 @@ export const Actions = { push_to_service: 'push_to_service', } as const; +export type ActionOperationKeys = keyof typeof Actions; +export type ActionOperationValues = typeof Actions[ActionOperationKeys]; + /* To the next developer, if you add/removed fields here * make sure to check this file (x-pack/plugins/cases/server/services/user_actions/helpers.ts) too */ diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/create_case.ts b/x-pack/plugins/cases/common/api/cases/user_actions/create_case.ts index 53d2320b5afd4..b4bc133ee73f3 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/create_case.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/create_case.ts @@ -6,6 +6,7 @@ */ import * as rt from 'io-ts'; +import { AssigneesUserActionPayloadRt } from './assignees'; import { ActionTypes, UserActionWithAttributes } from './common'; import { ConnectorUserActionPayloadRt, @@ -21,6 +22,7 @@ export const CommonFieldsRt = rt.type({ }); const CommonPayloadAttributesRt = rt.type({ + assignees: AssigneesUserActionPayloadRt.props.assignees, description: DescriptionUserActionPayloadRt.props.description, status: rt.string, severity: rt.string, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/index.ts b/x-pack/plugins/cases/common/api/cases/user_actions/index.ts index d19ee5fcbe9f8..6312e670533b9 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/index.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/index.ts @@ -24,6 +24,7 @@ import { SettingsUserActionRt } from './settings'; import { StatusUserActionRt } from './status'; import { DeleteCaseUserActionRt } from './delete_case'; import { SeverityUserActionRt } from './severity'; +import { AssigneesUserActionRt } from './assignees'; export * from './common'; export * from './comment'; @@ -36,6 +37,7 @@ export * from './settings'; export * from './status'; export * from './tags'; export * from './title'; +export * from './assignees'; const CommonUserActionsRt = rt.union([ DescriptionUserActionRt, @@ -45,6 +47,7 @@ const CommonUserActionsRt = rt.union([ SettingsUserActionRt, StatusUserActionRt, SeverityUserActionRt, + AssigneesUserActionRt, ]); export const UserActionsRt = rt.union([ diff --git a/x-pack/plugins/cases/common/api/cases/user_profiles.ts b/x-pack/plugins/cases/common/api/cases/user_profiles.ts index 75cd1f9cb9f94..d6ec5b05910f3 100644 --- a/x-pack/plugins/cases/common/api/cases/user_profiles.ts +++ b/x-pack/plugins/cases/common/api/cases/user_profiles.ts @@ -16,3 +16,9 @@ export const SuggestUserProfilesRequestRt = rt.intersection([ ]); export type SuggestUserProfilesRequest = rt.TypeOf; + +export const CaseUserProfileRt = rt.type({ + uid: rt.string, +}); + +export type CaseUserProfile = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index e88a7baf2f583..9e85d6e4cbf7a 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -157,3 +157,9 @@ export const READ_CASES_CAPABILITY = 'read_cases' as const; export const UPDATE_CASES_CAPABILITY = 'update_cases' as const; export const DELETE_CASES_CAPABILITY = 'delete_cases' as const; export const PUSH_CASES_CAPABILITY = 'push_cases' as const; + +/** + * User profiles + */ + +export const DEFAULT_USER_SIZE = 10; diff --git a/x-pack/plugins/cases/kibana.json b/x-pack/plugins/cases/kibana.json index 00b630cb7cab7..d027023b7c96a 100644 --- a/x-pack/plugins/cases/kibana.json +++ b/x-pack/plugins/cases/kibana.json @@ -11,7 +11,6 @@ "kibanaVersion":"kibana", "optionalPlugins":[ "home", - "security", "taskManager", "usageCollection" ], @@ -30,7 +29,8 @@ "kibanaUtils", "triggersActionsUi", "management", - "spaces" + "spaces", + "security" ], "requiredBundles": [ "savedObjects" diff --git a/x-pack/plugins/cases/public/client/helpers/group_alerts_by_rule.ts b/x-pack/plugins/cases/public/client/helpers/group_alerts_by_rule.ts index 0c1d73ba03b71..09e334cb3c848 100644 --- a/x-pack/plugins/cases/public/client/helpers/group_alerts_by_rule.ts +++ b/x-pack/plugins/cases/public/client/helpers/group_alerts_by_rule.ts @@ -8,7 +8,7 @@ import { CommentRequestAlertType } from '../../../common/api'; import { CommentType, Ecs } from '../../../common'; import { getRuleIdFromEvent } from './get_rule_id_from_event'; -import { CaseAttachments } from '../../types'; +import { CaseAttachmentsWithoutOwner } from '../../types'; type Maybe = T | null; interface Event { @@ -20,25 +20,29 @@ interface EventNonEcsData { value?: Maybe; } -export const groupAlertsByRule = (items: Event[], owner: string): CaseAttachments => { - const attachmentsByRule = items.reduce>((acc, item) => { - const rule = getRuleIdFromEvent(item); - if (!acc[rule.id]) { - acc[rule.id] = { - alertId: [], - index: [], - owner, - type: CommentType.alert as const, - rule, - }; - } - const alerts = acc[rule.id].alertId; - const indexes = acc[rule.id].index; - if (Array.isArray(alerts) && Array.isArray(indexes)) { - alerts.push(item.ecs._id ?? ''); - indexes.push(item.ecs._index ?? ''); - } - return acc; - }, {}); +type CommentRequestAlertTypeWithoutOwner = Omit; + +export const groupAlertsByRule = (items: Event[]): CaseAttachmentsWithoutOwner => { + const attachmentsByRule = items.reduce>( + (acc, item) => { + const rule = getRuleIdFromEvent(item); + if (!acc[rule.id]) { + acc[rule.id] = { + alertId: [], + index: [], + type: CommentType.alert as const, + rule, + }; + } + const alerts = acc[rule.id].alertId; + const indexes = acc[rule.id].index; + if (Array.isArray(alerts) && Array.isArray(indexes)) { + alerts.push(item.ecs._id ?? ''); + indexes.push(item.ecs._index ?? ''); + } + return acc; + }, + {} + ); return Object.values(attachmentsByRule); }; diff --git a/x-pack/plugins/cases/public/common/use_cases_toast.tsx b/x-pack/plugins/cases/public/common/use_cases_toast.tsx index 36ca8340036a5..5e88831144b6b 100644 --- a/x-pack/plugins/cases/public/common/use_cases_toast.tsx +++ b/x-pack/plugins/cases/public/common/use_cases_toast.tsx @@ -12,7 +12,7 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { Case, CommentType } from '../../common'; import { useToasts } from './lib/kibana'; import { useCaseViewNavigation } from './navigation'; -import { CaseAttachments } from '../types'; +import { CaseAttachmentsWithoutOwner } from '../types'; import { CASE_ALERT_SUCCESS_SYNC_TEXT, CASE_ALERT_SUCCESS_TOAST, @@ -34,7 +34,7 @@ const EuiTextStyled = styled(EuiText)` `} `; -function getAlertsCount(attachments: CaseAttachments): number { +function getAlertsCount(attachments: CaseAttachmentsWithoutOwner): number { let alertsCount = 0; for (const attachment of attachments) { if (attachment.type === CommentType.alert) { @@ -57,7 +57,7 @@ function getToastTitle({ }: { theCase: Case; title?: string; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; }): string { if (title !== undefined) { return title; @@ -78,7 +78,7 @@ function getToastContent({ }: { theCase: Case; content?: string; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; }): string | undefined { if (content !== undefined) { return content; @@ -106,7 +106,7 @@ export const useCasesToast = () => { content, }: { theCase: Case; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; title?: string; content?: string; }) => { diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index 8def93fef325d..d8bb10fce78fc 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -12,12 +12,13 @@ import { noop } from 'lodash/fp'; import { noCreateCasesPermissions, TestProviders } from '../../common/mock'; -import { CommentRequest, CommentType } from '../../../common/api'; +import { CommentType } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { useCreateAttachments } from '../../containers/use_create_attachments'; import { AddComment, AddCommentProps, AddCommentRefObject } from '.'; import { CasesTimelineIntegrationProvider } from '../timeline_context'; import { timelineIntegrationMock } from '../__mock__/timeline'; +import { CaseAttachmentWithoutOwner } from '../../types'; jest.mock('../../containers/use_create_attachments'); @@ -41,10 +42,9 @@ const defaultResponse = { createAttachments, }; -const sampleData: CommentRequest = { +const sampleData: CaseAttachmentWithoutOwner = { comment: 'what a cool comment', - type: CommentType.user, - owner: SECURITY_SOLUTION_OWNER, + type: CommentType.user as const, }; describe('AddComment ', () => { @@ -73,6 +73,7 @@ describe('AddComment ', () => { expect(onCommentSaving).toBeCalled(); expect(createAttachments).toBeCalledWith({ caseId: addCommentProps.caseId, + caseOwner: SECURITY_SOLUTION_OWNER, data: [sampleData], updateCase: onCommentPosted, }); diff --git a/x-pack/plugins/cases/public/components/add_comment/index.tsx b/x-pack/plugins/cases/public/components/add_comment/index.tsx index 98e505b113ecd..6710a4a4471e0 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.tsx @@ -105,7 +105,8 @@ export const AddComment = React.memo( } createAttachments({ caseId, - data: [{ ...data, type: CommentType.user, owner: owner[0] }], + caseOwner: owner[0], + data: [{ ...data, type: CommentType.user }], updateCase: onCommentPosted, }); reset(); 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 35b15c4b21587..690849567f4f8 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 @@ -581,6 +581,7 @@ describe('AllCasesListGeneric', () => { wrapper.find('[data-test-subj="cases-table-row-select-1"]').first().simulate('click'); await waitFor(() => { expect(onRowClick).toHaveBeenCalledWith({ + assignees: [], closedAt: null, closedBy: null, comments: [], 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 46ff28e96160e..3c056ccf996dd 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 @@ -70,7 +70,7 @@ export const AllCasesList = React.memo( const firstAvailableStatus = head(difference(caseStatuses, hiddenStatuses)); const initialFilterOptions = { ...(!isEmpty(hiddenStatuses) && firstAvailableStatus && { status: firstAvailableStatus }), - owner: hasOwner ? owner : availableSolutions, + owner: hasOwner ? owner : [], }; const [filterOptions, setFilterOptions] = useState({ ...DEFAULT_FILTER_OPTIONS, diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index e4a6927c2e840..0929f8971cf06 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -250,7 +250,12 @@ export const useCasesColumns = ({ render: (caseOwner: CasesOwners) => { const ownerInfo = OWNER_INFO[caseOwner]; return ownerInfo ? ( - + ) : ( getEmptyTagValue() ); diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx index 2e7a12ae6d68e..8152986e6c75b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx @@ -13,7 +13,7 @@ import { Case } from '../../../containers/types'; import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; import { useCasesContext } from '../../cases_context/use_cases_context'; import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout'; -import { CaseAttachments } from '../../../types'; +import { CaseAttachmentsWithoutOwner } from '../../../types'; import { useCreateAttachments } from '../../../containers/use_create_attachments'; type AddToExistingFlyoutProps = AllCasesSelectorModalProps & { @@ -51,7 +51,7 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps = }, [dispatch]); const handleOnRowClick = useCallback( - async (theCase: Case | undefined, attachments: CaseAttachments) => { + async (theCase: Case | undefined, attachments: CaseAttachmentsWithoutOwner) => { // when the case is undefined in the modal // the user clicked "create new case" if (theCase === undefined) { @@ -65,6 +65,7 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps = if (attachments !== undefined && attachments.length > 0) { await createAttachments({ caseId: theCase.id, + caseOwner: theCase.owner, data: attachments, throwOnError: true, }); @@ -89,7 +90,7 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps = ); const openModal = useCallback( - ({ attachments }: { attachments?: CaseAttachments } = {}) => { + ({ attachments }: { attachments?: CaseAttachmentsWithoutOwner } = {}) => { dispatch({ type: CasesContextStoreActionsList.OPEN_ADD_TO_CASE_MODAL, payload: { diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx index 63c5b55099cf3..9df5757525202 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx @@ -190,7 +190,7 @@ describe('CasesTableFilters ', () => { ); }); - describe('dynamic Solution filter', () => { + describe('Solution filter', () => { it('shows Solution filter when provided more than 1 availableSolutions', () => { const wrapper = mount( @@ -215,28 +215,58 @@ describe('CasesTableFilters ', () => { wrapper.find(`[data-test-subj="options-filter-popover-button-Solution"]`).exists() ).toBeFalsy(); }); - }); - it('should call onFilterChange when selected solution changes', () => { - const wrapper = mount( - - - - ); - wrapper - .find(`[data-test-subj="options-filter-popover-button-Solution"]`) - .last() - .simulate('click'); + it('should call onFilterChange when selected solution changes', () => { + const wrapper = mount( + + + + ); + wrapper + .find(`[data-test-subj="options-filter-popover-button-Solution"]`) + .last() + .simulate('click'); - wrapper - .find(`[data-test-subj="options-filter-popover-item-${SECURITY_SOLUTION_OWNER}"]`) - .last() - .simulate('click'); + wrapper + .find(`[data-test-subj="options-filter-popover-item-${SECURITY_SOLUTION_OWNER}"]`) + .last() + .simulate('click'); + + expect(onFilterChanged).toBeCalledWith({ owner: [SECURITY_SOLUTION_OWNER] }); + }); - expect(onFilterChanged).toBeCalledWith({ owner: [SECURITY_SOLUTION_OWNER] }); + it('should deselect all solutions', () => { + const wrapper = mount( + + + + ); + + wrapper + .find(`[data-test-subj="options-filter-popover-button-Solution"]`) + .last() + .simulate('click'); + + wrapper + .find(`[data-test-subj="options-filter-popover-item-${SECURITY_SOLUTION_OWNER}"]`) + .last() + .simulate('click'); + + expect(onFilterChanged).toBeCalledWith({ owner: [SECURITY_SOLUTION_OWNER] }); + + wrapper + .find(`[data-test-subj="options-filter-popover-item-${SECURITY_SOLUTION_OWNER}"]`) + .last() + .simulate('click'); + + expect(onFilterChanged).toBeCalledWith({ owner: [] }); + }); }); describe('create case button', () => { diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx index 5d0d6ca0017a6..cedd7c9b64718 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx @@ -112,7 +112,7 @@ const CasesTableFiltersComponent = ({ const handleSelectedSolution = useCallback( (newOwner) => { - if (!isEqual(newOwner, selectedOwner) && newOwner.length) { + if (!isEqual(newOwner, selectedOwner)) { setSelectedOwner(newOwner); onFilterChanged({ owner: newOwner }); } diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index ac73444800dea..bc4df5265a3b0 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -371,6 +371,38 @@ describe('CaseViewPage', () => { }); describe('Tabs', () => { + jest.mock('@kbn/kibana-react-plugin/public', () => ({ + useKibana: () => ({ + services: { + application: { + capabilities: { + fakeCases: { + create_cases: true, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + }, + }, + }, + cases: { + ui: { + getCasesContext: () => null, + }, + helpers: { + getUICapabilities: () => ({ + all: true, + read: true, + create: true, + update: true, + delete: true, + push: true, + }), + }, + }, + }, + }), + })); it('renders tabs correctly', async () => { const result = appMockRenderer.render(); await act(async () => { diff --git a/x-pack/plugins/cases/public/components/cases_context/use_application.tsx b/x-pack/plugins/cases/public/components/cases_context/use_application.tsx index 86cfded0bc9d0..f21c40b6ee603 100644 --- a/x-pack/plugins/cases/public/components/cases_context/use_application.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/use_application.tsx @@ -18,7 +18,9 @@ export const useApplication = (): UseApplicationReturn => { const appId = useObservable(currentAppId$); const applications = useObservable(applications$); - const appTitle = appId ? applications?.get(appId)?.category?.label : undefined; + const appTitle = appId + ? applications?.get(appId)?.category?.label ?? applications?.get(appId)?.title + : undefined; return { appId, appTitle }; }; diff --git a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx index 94e72c43f6ad9..be758e1718451 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx @@ -14,7 +14,7 @@ import * as i18n from '../translations'; import { Case } from '../../../../common/ui/types'; import { CreateCaseForm } from '../form'; import { UseCreateAttachments } from '../../../containers/use_create_attachments'; -import { CaseAttachments } from '../../../types'; +import { CaseAttachmentsWithoutOwner } from '../../../types'; import { casesQueryClient } from '../../cases_context/query_client'; export interface CreateCaseFlyoutProps { @@ -24,7 +24,7 @@ export interface CreateCaseFlyoutProps { ) => Promise; onClose?: () => void; onSuccess?: (theCase: Case) => Promise; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; } const StyledFlyout = styled(EuiFlyout)` diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx index 8dc49758695e5..8b2d2b02cce79 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx @@ -6,7 +6,7 @@ */ import { useCallback } from 'react'; -import { CaseAttachments } from '../../../types'; +import { CaseAttachmentsWithoutOwner } from '../../../types'; import { useCasesToast } from '../../../common/use_cases_toast'; import { Case } from '../../../containers/types'; import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; @@ -29,7 +29,7 @@ export const useCasesAddToNewCaseFlyout = (props: AddToNewCaseFlyoutProps = {}) }, [dispatch]); const openFlyout = useCallback( - ({ attachments }: { attachments?: CaseAttachments } = {}) => { + ({ attachments }: { attachments?: CaseAttachmentsWithoutOwner } = {}) => { dispatch({ type: CasesContextStoreActionsList.OPEN_CREATE_CASE_FLYOUT, payload: { diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx index 50a3c69f2073e..78f5a4e9d5c54 100644 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ b/x-pack/plugins/cases/public/components/create/form.tsx @@ -34,7 +34,7 @@ import { useCasesFeatures } from '../cases_context/use_cases_features'; import { CreateCaseOwnerSelector } from './owner_selector'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useAvailableCasesOwners } from '../app/use_available_owners'; -import { CaseAttachments } from '../../types'; +import { CaseAttachmentsWithoutOwner } from '../../types'; import { Severity } from './severity'; interface ContainerProps { @@ -67,7 +67,7 @@ export interface CreateCaseFormProps extends Pick Promise; timelineIntegration?: CasesTimelineIntegration; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; } const empty: ActionConnector[] = []; diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index bdc6f68ddf077..57bc23d9aaa02 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -822,7 +822,11 @@ describe('Create case', () => { }); expect(createAttachments).toHaveBeenCalledTimes(1); - expect(createAttachments).toHaveBeenCalledWith({ caseId: 'case-id', data: attachments }); + expect(createAttachments).toHaveBeenCalledWith({ + caseId: 'case-id', + data: attachments, + caseOwner: 'securitySolution', + }); }); it('should NOT call createAttachments if the attachments are an empty array', async () => { diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 70b4fb4ec9ab0..dc8c3e593c87f 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -21,7 +21,7 @@ import { import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesFeatures } from '../cases_context/use_cases_features'; import { getConnectorById } from '../utils'; -import { CaseAttachments } from '../../types'; +import { CaseAttachmentsWithoutOwner } from '../../types'; import { useGetConnectors } from '../../containers/configure/use_connectors'; const initialCaseValue: FormProps = { @@ -33,6 +33,7 @@ const initialCaseValue: FormProps = { fields: null, syncAlerts: true, selectedOwner: null, + assignees: [], }; interface Props { @@ -42,7 +43,7 @@ interface Props { ) => Promise; children?: JSX.Element | JSX.Element[]; onSuccess?: (theCase: Case) => Promise; - attachments?: CaseAttachments; + attachments?: CaseAttachmentsWithoutOwner; } export const FormContext: React.FC = ({ @@ -87,6 +88,7 @@ export const FormContext: React.FC = ({ if (updatedCase && Array.isArray(attachments) && attachments.length > 0) { await createAttachments({ caseId: updatedCase.id, + caseOwner: updatedCase.owner, data: attachments, }); } diff --git a/x-pack/plugins/cases/public/components/create/owner_selector.tsx b/x-pack/plugins/cases/public/components/create/owner_selector.tsx index 34bef0b98ff78..bcc8709ee5c5d 100644 --- a/x-pack/plugins/cases/public/components/create/owner_selector.tsx +++ b/x-pack/plugins/cases/public/components/create/owner_selector.tsx @@ -19,7 +19,7 @@ import { import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { SECURITY_SOLUTION_OWNER } from '../../../common'; -import { OBSERVABILITY_OWNER, OWNER_INFO } from '../../../common/constants'; +import { OWNER_INFO } from '../../../common/constants'; import { FieldHook, getFieldValidityAndErrorMessage, UseField } from '../../common/shared_imports'; import * as i18n from './translations'; @@ -35,7 +35,7 @@ interface Props { isLoading: boolean; } -const DEFAULT_SELECTABLE_OWNERS = [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER] as const; +const DEFAULT_SELECTABLE_OWNERS = Object.keys(OWNER_INFO) as Array; const FIELD_NAME = 'selectedOwner'; diff --git a/x-pack/plugins/cases/public/components/user_actions/assignees.tsx b/x-pack/plugins/cases/public/components/user_actions/assignees.tsx new file mode 100644 index 0000000000000..42580ede0b3f2 --- /dev/null +++ b/x-pack/plugins/cases/public/components/user_actions/assignees.tsx @@ -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 type { UserActionBuilder } from './types'; + +export const createAssigneesUserActionBuilder: UserActionBuilder = ({ + userAction, + handleOutlineComment, +}) => ({ + build: () => { + return []; + }, +}); diff --git a/x-pack/plugins/cases/public/components/user_actions/builder.tsx b/x-pack/plugins/cases/public/components/user_actions/builder.tsx index 36298bbae601b..903c1c190d8dd 100644 --- a/x-pack/plugins/cases/public/components/user_actions/builder.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/builder.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { createAssigneesUserActionBuilder } from './assignees'; import { createCommentUserActionBuilder } from './comment/comment'; import { createConnectorUserActionBuilder } from './connector'; import { createDescriptionUserActionBuilder } from './description'; @@ -26,4 +27,5 @@ export const builderMap: UserActionBuilderMap = { comment: createCommentUserActionBuilder, description: createDescriptionUserActionBuilder, settings: createSettingsUserActionBuilder, + assignees: createAssigneesUserActionBuilder, }; diff --git a/x-pack/plugins/cases/public/containers/constants.ts b/x-pack/plugins/cases/public/containers/constants.ts index cfd7d5020e20f..3a04b411cb8e7 100644 --- a/x-pack/plugins/cases/public/containers/constants.ts +++ b/x-pack/plugins/cases/public/containers/constants.ts @@ -16,3 +16,11 @@ export const CASE_LIST_CACHE_KEY = 'case-list'; export const CASE_CONNECTORS_CACHE_KEY = 'case-connectors'; export const CASE_LICENSE_CACHE_KEY = 'case-license-action'; export const CASE_TAGS_CACHE_KEY = 'case-tags'; + +/** + * User profiles + */ + +export const USER_PROFILES_CACHE_KEY = 'user-profiles'; +export const USER_PROFILES_SUGGEST_CACHE_KEY = 'suggest'; +export const USER_PROFILES_BULK_GET_CACHE_KEY = 'bulk-get'; diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index b09420c59800f..0236175557adb 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -228,6 +228,7 @@ export const basicCase: Case = { settings: { syncAlerts: true, }, + assignees: [], }; export const caseWithAlerts = { @@ -329,6 +330,7 @@ export const mockCase: Case = { settings: { syncAlerts: true, }, + assignees: [], }; export const basicCasePost: Case = { @@ -631,6 +633,7 @@ export const getUserAction = ( tags: ['a tag'], settings: { syncAlerts: true }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, ...overrides, }; diff --git a/x-pack/plugins/cases/public/containers/use_create_attachments.test.tsx b/x-pack/plugins/cases/public/containers/use_create_attachments.test.tsx index 428b28e02dcc5..7a4b1c7f6523a 100644 --- a/x-pack/plugins/cases/public/containers/use_create_attachments.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_create_attachments.test.tsx @@ -26,13 +26,18 @@ describe('useCreateAttachments', () => { }); const abortCtrl = new AbortController(); - const samplePost = [ + const attachmentsWithoutOwner = [ { comment: 'a comment', type: CommentType.user as const, - owner: SECURITY_SOLUTION_OWNER, }, ]; + + const attachmentsWithOwner = attachmentsWithoutOwner.map((attachment) => ({ + ...attachment, + owner: SECURITY_SOLUTION_OWNER, + })); + const updateCaseCallback = jest.fn(); beforeEach(() => { jest.clearAllMocks(); @@ -64,11 +69,17 @@ describe('useCreateAttachments', () => { result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, }); + await waitForNextUpdate(); - expect(spyOnBulkCreateAttachments).toBeCalledWith(samplePost, basicCaseId, abortCtrl.signal); + expect(spyOnBulkCreateAttachments).toBeCalledWith( + attachmentsWithOwner, + basicCaseId, + abortCtrl.signal + ); expect(toastErrorMock).not.toHaveBeenCalled(); }); }); @@ -84,11 +95,17 @@ describe('useCreateAttachments', () => { result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, }); + await waitForNextUpdate(); - expect(spyOnBulkCreateAttachments).toBeCalledWith(samplePost, basicCaseId, abortCtrl.signal); + expect(spyOnBulkCreateAttachments).toBeCalledWith( + attachmentsWithOwner, + basicCaseId, + abortCtrl.signal + ); expect(toastErrorMock).not.toHaveBeenCalled(); }); }); @@ -98,12 +115,15 @@ describe('useCreateAttachments', () => { const { result, waitForNextUpdate } = renderHook(() => useCreateAttachments() ); + await waitForNextUpdate(); result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, }); + await waitForNextUpdate(); expect(result.current).toEqual({ isLoading: false, @@ -118,10 +138,12 @@ describe('useCreateAttachments', () => { const { result, waitForNextUpdate } = renderHook(() => useCreateAttachments() ); + await waitForNextUpdate(); result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, }); @@ -139,10 +161,12 @@ describe('useCreateAttachments', () => { const { result, waitForNextUpdate } = renderHook(() => useCreateAttachments() ); + await waitForNextUpdate(); result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, }); @@ -168,11 +192,14 @@ describe('useCreateAttachments', () => { const { result, waitForNextUpdate } = renderHook(() => useCreateAttachments() ); + await waitForNextUpdate(); + async function test() { await result.current.createAttachments({ caseId: basicCaseId, - data: samplePost, + caseOwner: SECURITY_SOLUTION_OWNER, + data: attachmentsWithoutOwner, updateCase: updateCaseCallback, throwOnError: true, }); diff --git a/x-pack/plugins/cases/public/containers/use_create_attachments.tsx b/x-pack/plugins/cases/public/containers/use_create_attachments.tsx index e6de525f36e6a..779677cd89cc6 100644 --- a/x-pack/plugins/cases/public/containers/use_create_attachments.tsx +++ b/x-pack/plugins/cases/public/containers/use_create_attachments.tsx @@ -6,12 +6,12 @@ */ import { useReducer, useCallback, useRef, useEffect } from 'react'; -import { BulkCreateCommentRequest } from '../../common/api'; import { createAttachments } from './api'; import * as i18n from './translations'; import { Case } from './types'; import { useToasts } from '../common/lib/kibana'; +import { CaseAttachmentsWithoutOwner } from '../types'; interface NewCommentState { isLoading: boolean; @@ -43,7 +43,8 @@ const dataFetchReducer = (state: NewCommentState, action: Action): NewCommentSta export interface PostComment { caseId: string; - data: BulkCreateCommentRequest; + caseOwner: string; + data: CaseAttachmentsWithoutOwner; updateCase?: (newCase: Case) => void; throwOnError?: boolean; } @@ -61,14 +62,15 @@ export const useCreateAttachments = (): UseCreateAttachments => { const abortCtrlRef = useRef(new AbortController()); const fetch = useCallback( - async ({ caseId, data, updateCase, throwOnError }: PostComment) => { + async ({ caseId, caseOwner, data, updateCase, throwOnError }: PostComment) => { try { isCancelledRef.current = false; abortCtrlRef.current.abort(); abortCtrlRef.current = new AbortController(); dispatch({ type: 'FETCH_INIT' }); - const response = await createAttachments(data, caseId, abortCtrlRef.current.signal); + const attachments = data.map((attachment) => ({ ...attachment, owner: caseOwner })); + const response = await createAttachments(attachments, caseId, abortCtrlRef.current.signal); if (!isCancelledRef.current) { dispatch({ type: 'FETCH_SUCCESS' }); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/user_profiles/__mocks__/api.ts new file mode 100644 index 0000000000000..36c88451124ca --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/__mocks__/api.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserProfile } from '@kbn/security-plugin/common'; +import { userProfiles } from '../api.mock'; + +export const suggestUserProfiles = async (): Promise => + Promise.resolve(userProfiles); + +export const bulkGetUserProfiles = async (): Promise => + Promise.resolve(userProfiles); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/api.mock.ts b/x-pack/plugins/cases/public/containers/user_profiles/api.mock.ts new file mode 100644 index 0000000000000..57578086ceddf --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/api.mock.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserProfile } from '@kbn/security-plugin/common'; + +export const userProfiles: UserProfile[] = [ + { + uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + data: {}, + user: { + username: 'damaged_raccoon', + email: 'damaged_raccoon@elastic.co', + full_name: 'Damaged Raccoon', + }, + }, + { + uid: 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + data: {}, + user: { + username: 'physical_dinosaur', + email: 'physical_dinosaur@elastic.co', + full_name: 'Physical Dinosaur', + }, + }, + { + uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + data: {}, + user: { + username: 'wet_dingo', + email: 'wet_dingo@elastic.co', + full_name: 'Wet Dingo', + }, + }, +]; + +export const userProfilesIds = userProfiles.map((profile) => profile.uid); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/api.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/api.test.ts new file mode 100644 index 0000000000000..7234cc9fb54fe --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/api.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GENERAL_CASES_OWNER } from '../../../common/constants'; +import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; +import { bulkGetUserProfiles, suggestUserProfiles } from './api'; +import { userProfiles, userProfilesIds } from './api.mock'; + +describe('User profiles API', () => { + describe('suggestUserProfiles', () => { + const abortCtrl = new AbortController(); + const { http } = createStartServicesMock(); + + beforeEach(() => { + jest.clearAllMocks(); + http.post = jest.fn().mockResolvedValue(userProfiles); + }); + + it('returns the user profiles correctly', async () => { + const res = await suggestUserProfiles({ + http, + name: 'elastic', + owner: [GENERAL_CASES_OWNER], + signal: abortCtrl.signal, + }); + + expect(res).toEqual(userProfiles); + }); + + it('calls http.post correctly', async () => { + await suggestUserProfiles({ + http, + name: 'elastic', + owner: [GENERAL_CASES_OWNER], + signal: abortCtrl.signal, + }); + + expect(http.post).toHaveBeenCalledWith('/internal/cases/_suggest_user_profiles', { + body: '{"name":"elastic","size":10,"owner":["cases"]}', + signal: abortCtrl.signal, + }); + }); + }); + + describe('bulkGetUserProfiles', () => { + const { security } = createStartServicesMock(); + + beforeEach(() => { + jest.clearAllMocks(); + security.userProfiles.bulkGet = jest.fn().mockResolvedValue(userProfiles); + }); + + it('returns the user profiles correctly', async () => { + const res = await bulkGetUserProfiles({ + security, + uids: userProfilesIds, + }); + + expect(res).toEqual(userProfiles); + }); + + it('calls http.post correctly', async () => { + await bulkGetUserProfiles({ + security, + uids: userProfilesIds, + }); + + expect(security.userProfiles.bulkGet).toHaveBeenCalledWith({ + dataPath: 'avatar', + uids: new Set([ + 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0', + 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', + 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0', + ]), + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/api.ts b/x-pack/plugins/cases/public/containers/user_profiles/api.ts new file mode 100644 index 0000000000000..6da84d1991423 --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/api.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpStart } from '@kbn/core/public'; +import { UserProfile } from '@kbn/security-plugin/common'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { INTERNAL_SUGGEST_USER_PROFILES_URL, DEFAULT_USER_SIZE } from '../../../common/constants'; + +export interface SuggestUserProfilesArgs { + http: HttpStart; + name: string; + owner: string[]; + signal: AbortSignal; + size?: number; +} + +export const suggestUserProfiles = async ({ + http, + name, + size = DEFAULT_USER_SIZE, + owner, + signal, +}: SuggestUserProfilesArgs): Promise => { + const response = await http.post(INTERNAL_SUGGEST_USER_PROFILES_URL, { + body: JSON.stringify({ name, size, owner }), + signal, + }); + + return response; +}; + +export interface BulkGetUserProfilesArgs { + security: SecurityPluginStart; + uids: string[]; +} + +export const bulkGetUserProfiles = async ({ + security, + uids, +}: BulkGetUserProfilesArgs): Promise => { + return security.userProfiles.bulkGet({ uids: new Set(uids), dataPath: 'avatar' }); +}; diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.test.ts new file mode 100644 index 0000000000000..7591bf394d5c1 --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useToasts } from '../../common/lib/kibana'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import * as api from './api'; +import { useBulkGetUserProfiles } from './use_bulk_get_user_profiles'; +import { userProfilesIds } from './api.mock'; + +jest.mock('../../common/lib/kibana'); +jest.mock('./api'); + +describe('useBulkGetUserProfiles', () => { + const props = { + uids: userProfilesIds, + }; + + const addSuccess = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError: jest.fn() }); + + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('calls bulkGetUserProfiles with correct arguments', async () => { + const spyOnSuggestUserProfiles = jest.spyOn(api, 'bulkGetUserProfiles'); + + const { result, waitFor } = renderHook(() => useBulkGetUserProfiles(props), { + wrapper: appMockRender.AppWrapper, + }); + + await waitFor(() => result.current.isSuccess); + + expect(spyOnSuggestUserProfiles).toBeCalledWith({ + ...props, + security: expect.anything(), + }); + }); + + it('shows a toast error message when an error occurs in the response', async () => { + const spyOnSuggestUserProfiles = jest.spyOn(api, 'bulkGetUserProfiles'); + + spyOnSuggestUserProfiles.mockImplementation(() => { + throw new Error('Something went wrong'); + }); + + const addError = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); + + const { result, waitFor } = renderHook(() => useBulkGetUserProfiles(props), { + wrapper: appMockRender.AppWrapper, + }); + + await waitFor(() => result.current.isError); + + expect(addError).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts new file mode 100644 index 0000000000000..cdaee1848613c --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery, UseQueryResult } from 'react-query'; +import { UserProfile } from '@kbn/security-plugin/common'; +import * as i18n from '../translations'; +import { useKibana, useToasts } from '../../common/lib/kibana'; +import { ServerError } from '../../types'; +import { USER_PROFILES_CACHE_KEY, USER_PROFILES_BULK_GET_CACHE_KEY } from '../constants'; +import { bulkGetUserProfiles } from './api'; + +export const useBulkGetUserProfiles = ({ uids }: { uids: string[] }) => { + const { security } = useKibana().services; + + const toasts = useToasts(); + + return useQuery( + [USER_PROFILES_CACHE_KEY, USER_PROFILES_BULK_GET_CACHE_KEY, uids], + () => { + return bulkGetUserProfiles({ security, uids }); + }, + { + onError: (error: ServerError) => { + if (error.name !== 'AbortError') { + toasts.addError( + error.body && error.body.message ? new Error(error.body.message) : error, + { + title: i18n.ERROR_TITLE, + } + ); + } + }, + } + ); +}; + +export type UseSuggestUserProfiles = UseQueryResult; diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts new file mode 100644 index 0000000000000..5ae16c8fa6e00 --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GENERAL_CASES_OWNER } from '../../../common/constants'; +import { renderHook } from '@testing-library/react-hooks'; +import { useToasts } from '../../common/lib/kibana'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import * as api from './api'; +import { useSuggestUserProfiles } from './use_suggest_user_profiles'; + +jest.mock('../../common/lib/kibana'); +jest.mock('./api'); + +describe('useSuggestUserProfiles', () => { + const props = { + name: 'elastic', + owner: [GENERAL_CASES_OWNER], + }; + + const addSuccess = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError: jest.fn() }); + + let appMockRender: AppMockRenderer; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('calls suggestUserProfiles with correct arguments', async () => { + const spyOnSuggestUserProfiles = jest.spyOn(api, 'suggestUserProfiles'); + + const { result, waitFor } = renderHook(() => useSuggestUserProfiles(props), { + wrapper: appMockRender.AppWrapper, + }); + + jest.advanceTimersByTime(500); + await waitFor(() => result.current.isSuccess); + + expect(spyOnSuggestUserProfiles).toBeCalledWith({ + ...props, + size: 10, + http: expect.anything(), + signal: expect.anything(), + }); + }); + + it('shows a toast error message when an error occurs in the response', async () => { + const spyOnSuggestUserProfiles = jest.spyOn(api, 'suggestUserProfiles'); + + spyOnSuggestUserProfiles.mockImplementation(() => { + throw new Error('Something went wrong'); + }); + + const addError = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); + + const { result, waitFor } = renderHook(() => useSuggestUserProfiles(props), { + wrapper: appMockRender.AppWrapper, + }); + + jest.advanceTimersByTime(500); + await waitFor(() => result.current.isError); + + expect(addError).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts new file mode 100644 index 0000000000000..3705224e1d0c8 --- /dev/null +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState } from 'react'; +import { useQuery, UseQueryResult } from 'react-query'; +import useDebounce from 'react-use/lib/useDebounce'; +import { UserProfile } from '@kbn/security-plugin/common'; +import { DEFAULT_USER_SIZE } from '../../../common/constants'; +import * as i18n from '../translations'; +import { useKibana, useToasts } from '../../common/lib/kibana'; +import { ServerError } from '../../types'; +import { USER_PROFILES_CACHE_KEY, USER_PROFILES_SUGGEST_CACHE_KEY } from '../constants'; +import { suggestUserProfiles, SuggestUserProfilesArgs } from './api'; + +const DEBOUNCE_MS = 500; + +export const useSuggestUserProfiles = ({ + name, + owner, + size = DEFAULT_USER_SIZE, +}: Omit) => { + const { http } = useKibana().services; + const [debouncedName, setDebouncedName] = useState(name); + + useDebounce(() => setDebouncedName(name), DEBOUNCE_MS, [name]); + + const toasts = useToasts(); + + return useQuery( + [ + USER_PROFILES_CACHE_KEY, + USER_PROFILES_SUGGEST_CACHE_KEY, + { name: debouncedName, owner, size }, + ], + () => { + const abortCtrlRef = new AbortController(); + return suggestUserProfiles({ + http, + name: debouncedName, + owner, + size, + signal: abortCtrlRef.signal, + }); + }, + { + retry: false, + onError: (error: ServerError) => { + if (error.name !== 'AbortError') { + toasts.addError( + error.body && error.body.message ? new Error(error.body.message) : error, + { + title: i18n.ERROR_TITLE, + } + ); + } + }, + } + ); +}; + +export type UseSuggestUserProfiles = UseQueryResult; diff --git a/x-pack/plugins/cases/public/index.tsx b/x-pack/plugins/cases/public/index.tsx index e267960d91a80..554fa8bad2688 100644 --- a/x-pack/plugins/cases/public/index.tsx +++ b/x-pack/plugins/cases/public/index.tsx @@ -21,7 +21,11 @@ export type { GetCreateCaseFlyoutProps } from './client/ui/get_create_case_flyou export type { GetAllCasesSelectorModalProps } from './client/ui/get_all_cases_selector_modal'; export type { GetRecentCasesProps } from './client/ui/get_recent_cases'; -export type { CaseAttachments, SupportedCaseAttachment } from './types'; +export type { + CaseAttachments, + SupportedCaseAttachment, + CaseAttachmentsWithoutOwner, +} from './types'; export type { ICasesDeepLinkId } from './common/navigation'; export { diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index f07dd8cd9fb04..00ffa538c2f90 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -15,9 +15,10 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { ManagementSetup, ManagementAppMountParams } from '@kbn/management-plugin/public'; import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/public'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { TriggersAndActionsUIPublicPluginStart as TriggersActionsStart } from '@kbn/triggers-actions-ui-plugin/public'; +import type { DistributiveOmit } from '@elastic/eui'; import type { CasesByAlertId, CasesByAlertIDRequest, @@ -56,6 +57,7 @@ export interface CasesPluginStart { storage: Storage; triggersActionsUi: TriggersActionsStart; features: FeaturesPluginStart; + security: SecurityPluginStart; spaces?: SpacesPluginStart; } @@ -65,10 +67,7 @@ export interface CasesPluginStart { * Leaving it out currently in lieu of RBAC changes */ -export type StartServices = CoreStart & - CasesPluginStart & { - security: SecurityPluginSetup; - }; +export type StartServices = CoreStart & CasesPluginStart; export interface RenderAppProps { mountParams: ManagementAppMountParams; @@ -147,5 +146,7 @@ export interface CasesUiStart { export type SupportedCaseAttachment = CommentRequestAlertType | CommentRequestUserType; export type CaseAttachments = SupportedCaseAttachment[]; +export type CaseAttachmentWithoutOwner = DistributiveOmit; +export type CaseAttachmentsWithoutOwner = CaseAttachmentWithoutOwner[]; export type ServerError = IHttpFetchError; diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 0a2c785faef4c..dad55f528569a 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -86,7 +86,11 @@ export const create = async ( unsecuredSavedObjectsClient, caseId: newCase.id, user, - payload: { ...query, severity: query.severity ?? CaseSeverity.LOW }, + payload: { + ...query, + severity: query.severity ?? CaseSeverity.LOW, + assignees: query.assignees ?? [], + }, owner: newCase.attributes.owner, }); diff --git a/x-pack/plugins/cases/server/client/cases/find.ts b/x-pack/plugins/cases/server/client/cases/find.ts index 3bf9d0fd30ea7..eeb1cf6757268 100644 --- a/x-pack/plugins/cases/server/client/cases/find.ts +++ b/x-pack/plugins/cases/server/client/cases/find.ts @@ -62,6 +62,7 @@ export const find = async ( owner: queryParams.owner, from: queryParams.from, to: queryParams.to, + assignees: queryParams.assignees, }; const statusStatsOptions = constructQueryOptions({ diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 4c0698b209bef..3f912cda3eb3c 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -242,6 +242,7 @@ export const userActions: CaseUserActionsResponse = [ status: 'open', severity: 'low', owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/types.ts b/x-pack/plugins/cases/server/client/types.ts index 962b3bc85e1db..8ffd3aa5299f9 100644 --- a/x-pack/plugins/cases/server/client/types.ts +++ b/x-pack/plugins/cases/server/client/types.ts @@ -57,4 +57,5 @@ export interface ConstructQueryParams { authorizationFilter?: KueryNode; from?: string; to?: string; + assignees?: string | string[]; } diff --git a/x-pack/plugins/cases/server/client/utils.test.ts b/x-pack/plugins/cases/server/client/utils.test.ts index 9c7d3e77cbd38..30464f21b564c 100644 --- a/x-pack/plugins/cases/server/client/utils.test.ts +++ b/x-pack/plugins/cases/server/client/utils.test.ts @@ -5,7 +5,13 @@ * 2.0. */ -import { arraysDifference, buildRangeFilter, constructQueryOptions, sortToSnake } from './utils'; +import { + arraysDifference, + buildNestedFilter, + buildRangeFilter, + constructQueryOptions, + sortToSnake, +} from './utils'; import { toElasticsearchQuery } from '@kbn/es-query'; import { CaseStatuses } from '../../common'; import { CaseSeverity } from '../../common/api'; @@ -490,6 +496,163 @@ describe('utils', () => { }); }); + describe('buildNestedFilter', () => { + it('returns undefined if filters is undefined', () => { + expect(buildNestedFilter({ field: '', nestedField: '', operator: 'or' })).toBeUndefined(); + }); + + it('returns undefined when the filters array is empty', () => { + expect( + buildNestedFilter({ filters: [], field: '', nestedField: '', operator: 'or' }) + ).toBeUndefined(); + }); + + it('returns a KueryNode for a single filter', () => { + expect( + toElasticsearchQuery( + buildNestedFilter({ + filters: ['hello'], + field: 'uid', + nestedField: 'nestedField', + operator: 'or', + })! + ) + ).toMatchInlineSnapshot(` + Object { + "nested": Object { + "path": "cases.attributes.nestedField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "cases.attributes.nestedField.uid": "hello", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + } + `); + }); + + it("returns a KueryNode for multiple filters or'd together", () => { + expect( + toElasticsearchQuery( + buildNestedFilter({ + filters: ['uid1', 'uid2'], + field: 'uid', + nestedField: 'nestedField', + operator: 'or', + })! + ) + ).toMatchInlineSnapshot(` + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "nested": Object { + "path": "cases.attributes.nestedField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "cases.attributes.nestedField.uid": "uid1", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + }, + Object { + "nested": Object { + "path": "cases.attributes.nestedField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "cases.attributes.nestedField.uid": "uid2", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + }, + ], + }, + } + `); + }); + + it("returns a KueryNode for multiple filters and'ed together", () => { + expect( + toElasticsearchQuery( + buildNestedFilter({ + filters: ['uid1', 'uid2'], + field: 'uid', + nestedField: 'nestedField', + operator: 'and', + })! + ) + ).toMatchInlineSnapshot(` + Object { + "bool": Object { + "filter": Array [ + Object { + "nested": Object { + "path": "cases.attributes.nestedField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "cases.attributes.nestedField.uid": "uid1", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + }, + Object { + "nested": Object { + "path": "cases.attributes.nestedField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "cases.attributes.nestedField.uid": "uid2", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + }, + ], + }, + } + `); + }); + }); + describe('arraysDifference', () => { it('returns null if originalValue is null', () => { expect(arraysDifference(null, [])).toBeNull(); diff --git a/x-pack/plugins/cases/server/client/utils.ts b/x-pack/plugins/cases/server/client/utils.ts index 786c0ad582980..009a097d1f095 100644 --- a/x-pack/plugins/cases/server/client/utils.ts +++ b/x-pack/plugins/cases/server/client/utils.ts @@ -177,6 +177,10 @@ interface FilterField { type?: string; } +interface NestedFilterField extends FilterField { + nestedField: string; +} + export const buildFilter = ({ filters, field, @@ -194,7 +198,48 @@ export const buildFilter = ({ } return nodeBuilder[operator]( - filtersAsArray.map((filter) => nodeBuilder.is(`${type}.attributes.${field}`, filter)) + filtersAsArray.map((filter) => + nodeBuilder.is(`${escapeKuery(type)}.attributes.${escapeKuery(field)}`, escapeKuery(filter)) + ) + ); +}; + +/** + * Creates a KueryNode filter for the Saved Object find API's filter field. This handles constructing a filter for + * a nested field. + * + * @param filters is a string or array of strings that defines the values to search for + * @param field is the location to search for + * @param nestedField is the field in the saved object that has a type of 'nested' + * @param operator whether to 'or'/'and' the created filters together + * @type the type of saved object being searched + * @returns a constructed KueryNode representing the filter or undefined if one could not be built + */ +export const buildNestedFilter = ({ + filters, + field, + nestedField, + operator, + type = CASE_SAVED_OBJECT, +}: NestedFilterField): KueryNode | undefined => { + if (filters === undefined) { + return; + } + + const filtersAsArray = Array.isArray(filters) ? filters : [filters]; + + if (filtersAsArray.length === 0) { + return; + } + + return nodeBuilder[operator]( + filtersAsArray.map((filter) => + fromKueryExpression( + `${escapeKuery(type)}.attributes.${escapeKuery(nestedField)}:{ ${escapeKuery( + field + )}: ${escapeKuery(filter)} }` + ) + ) ); }; @@ -284,6 +329,7 @@ export const constructQueryOptions = ({ authorizationFilter, from, to, + assignees, }: ConstructQueryParams): SavedObjectFindOptionsKueryNode => { const tagsFilter = buildFilter({ filters: tags, field: 'tags', operator: 'or' }); const reportersFilter = buildFilter({ @@ -297,6 +343,11 @@ export const constructQueryOptions = ({ const statusFilter = status != null ? addStatusFilter({ status }) : undefined; const severityFilter = severity != null ? addSeverityFilter({ severity }) : undefined; const rangeFilter = buildRangeFilter({ from, to }); + const assigneesFilter = buildFilter({ + filters: assignees, + field: 'assignees.uid', + operator: 'or', + }); const filters = combineFilters([ statusFilter, @@ -305,6 +356,7 @@ export const constructQueryOptions = ({ reportersFilter, rangeFilter, ownerFilter, + assigneesFilter, ]); return { diff --git a/x-pack/plugins/cases/server/common/utils.test.ts b/x-pack/plugins/cases/server/common/utils.test.ts index 918a48863cac0..1587a751b7c08 100644 --- a/x-pack/plugins/cases/server/common/utils.test.ts +++ b/x-pack/plugins/cases/server/common/utils.test.ts @@ -103,6 +103,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "connector": Object { @@ -155,6 +156,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "connector": Object { @@ -192,6 +194,63 @@ describe('common utils', () => { } `); }); + + it('transform correctly with assignees provided', () => { + const myCase = { + newCase: { ...newCase, connector, assignees: [{ uid: '1' }] }, + user: { + email: 'elastic@elastic.co', + full_name: 'Elastic', + username: 'elastic', + }, + }; + + const res = transformNewCase(myCase); + + expect(res).toMatchInlineSnapshot(` + Object { + "assignees": Array [ + Object { + "uid": "1", + }, + ], + "closed_at": null, + "closed_by": null, + "connector": Object { + "fields": Object { + "issueType": "Task", + "parent": null, + "priority": "High", + }, + "id": "123", + "name": "My connector", + "type": ".jira", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic", + "username": "elastic", + }, + "description": "A description", + "duration": null, + "external_service": null, + "owner": "securitySolution", + "settings": Object { + "syncAlerts": true, + }, + "severity": "low", + "status": "open", + "tags": Array [ + "new", + "case", + ], + "title": "My new case", + "updated_at": null, + "updated_by": null, + } + `); + }); }); describe('transformCases', () => { @@ -214,6 +273,7 @@ describe('common utils', () => { Object { "cases": Array [ Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], @@ -254,6 +314,7 @@ describe('common utils', () => { "version": "WzAsMV0=", }, Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], @@ -294,6 +355,7 @@ describe('common utils', () => { "version": "WzQsMV0=", }, Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], @@ -338,6 +400,7 @@ describe('common utils', () => { "version": "WzUsMV0=", }, Object { + "assignees": Array [], "closed_at": "2019-11-25T22:32:17.947Z", "closed_by": Object { "email": "testemail@elastic.co", @@ -407,6 +470,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], @@ -463,6 +527,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], @@ -520,6 +585,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [ @@ -600,6 +666,7 @@ describe('common utils', () => { expect(res).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "comments": Array [], diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index 19c840a25d048..669472023d62a 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -69,6 +69,7 @@ export const transformNewCase = ({ status: CaseStatuses.open, updated_at: null, updated_by: null, + assignees: newCase.assignees ?? [], }); export const transformCases = ({ diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts index 77e1a64012c6d..1eb180edb09e4 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts @@ -52,6 +52,7 @@ export const mockCases: Array> = [ syncAlerts: true, }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, references: [], updated_at: '2019-11-25T21:54:48.952Z', @@ -92,6 +93,7 @@ export const mockCases: Array> = [ syncAlerts: true, }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, references: [], updated_at: '2019-11-25T22:32:00.900Z', @@ -132,6 +134,7 @@ export const mockCases: Array> = [ syncAlerts: true, }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, references: [], updated_at: '2019-11-25T22:32:17.947Z', @@ -176,6 +179,7 @@ export const mockCases: Array> = [ syncAlerts: true, }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }, references: [], updated_at: '2019-11-25T22:32:17.947Z', diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index 9b2ea975c4dcd..b1e3f9a76e2e7 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -27,6 +27,13 @@ export const createCaseSavedObjectType = ( convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { properties: { + assignees: { + properties: { + uid: { + type: 'keyword', + }, + }, + }, closed_at: { type: 'date', }, diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index b4d3421643a41..11eb477be73a5 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -16,7 +16,13 @@ import { import { CASE_SAVED_OBJECT } from '../../../common/constants'; import { getNoneCaseConnector } from '../../common/utils'; import { createExternalService, ESCaseConnectorWithId } from '../../services/test_utils'; -import { addDuration, addSeverity, caseConnectorIdMigration, removeCaseType } from './cases'; +import { + addAssignees, + addDuration, + addSeverity, + caseConnectorIdMigration, + removeCaseType, +} from './cases'; // eslint-disable-next-line @typescript-eslint/naming-convention const create_7_14_0_case = ({ @@ -538,4 +544,41 @@ describe('case migrations', () => { }); }); }); + + describe('addAssignees', () => { + it('adds the assignees field correctly when none is present', () => { + const doc = { + id: '123', + attributes: {}, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + expect(addAssignees(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + assignees: [], + }, + }); + }); + + it('keeps the existing assignees value if the field already exists', () => { + const assignees = [{ uid: '1' }]; + const doc = { + id: '123', + attributes: { + assignees, + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + expect(addAssignees(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + assignees, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index c4961f742abc7..2e3672c6f72c2 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -122,6 +122,13 @@ export const addSeverity = ( return { ...doc, attributes: { ...doc.attributes, severity }, references: doc.references ?? [] }; }; +export const addAssignees = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + const assignees = doc.attributes.assignees ?? []; + return { ...doc, attributes: { ...doc.attributes, assignees }, references: doc.references ?? [] }; +}; + export const caseMigrations = { '7.10.0': ( doc: SavedObjectUnsanitizedDoc @@ -184,4 +191,5 @@ export const caseMigrations = { '7.15.0': caseConnectorIdMigration, '8.1.0': removeCaseType, '8.3.0': pipeMigrations(addDuration, addSeverity), + '8.5.0': addAssignees, }; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.test.ts new file mode 100644 index 0000000000000..d918cbb5f9076 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.test.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 { ActionTypes } from '../../../../common/api'; +import { CASE_USER_ACTION_SAVED_OBJECT } from '../../../../common/constants'; +import { addAssigneesToCreateUserAction } from './assignees'; + +describe('assignees migration', () => { + const userAction = { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '1', + attributes: { + action_at: '2022-01-09T22:00:00.000Z', + action_by: { + email: 'elastic@elastic.co', + full_name: 'Elastic User', + username: 'elastic', + }, + payload: {}, + type: ActionTypes.create_case, + }, + }; + + it('adds the assignees field to the create_case user action', () => { + // @ts-expect-error payload does not include the required fields + const migratedUserAction = addAssigneesToCreateUserAction(userAction); + expect(migratedUserAction).toEqual({ + attributes: { + action_at: '2022-01-09T22:00:00.000Z', + action_by: { + email: 'elastic@elastic.co', + full_name: 'Elastic User', + username: 'elastic', + }, + payload: { + assignees: [], + }, + type: 'create_case', + }, + id: '1', + references: [], + type: 'cases-user-actions', + }); + }); + + it('does NOT add the assignees field non-create_case user actions', () => { + Object.keys(ActionTypes) + .filter((type) => type !== ActionTypes.create_case) + .forEach((type) => { + const migratedUserAction = addAssigneesToCreateUserAction({ + ...userAction, + // @ts-expect-error override the type, it is only expecting create_case + attributes: { ...userAction.attributes, type }, + }); + + expect(migratedUserAction).toEqual({ + attributes: { + action_at: '2022-01-09T22:00:00.000Z', + action_by: { + email: 'elastic@elastic.co', + full_name: 'Elastic User', + username: 'elastic', + }, + payload: {}, + type, + }, + id: '1', + references: [], + type: 'cases-user-actions', + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.ts new file mode 100644 index 0000000000000..d7b1110bbd5e1 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/assignees.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc } from '@kbn/core/server'; +import { ActionTypes, CreateCaseUserAction } from '../../../../common/api'; + +export const addAssigneesToCreateUserAction = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + if (doc.attributes.type !== ActionTypes.create_case) { + return { ...doc, references: doc.references ?? [] }; + } + + const payload = { + ...doc.attributes.payload, + assignees: doc?.attributes?.payload?.assignees ?? [], + }; + return { ...doc, attributes: { ...doc.attributes, payload }, references: doc.references ?? [] }; +}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts index 8b59716b222e1..98a1e2454ffc4 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts @@ -32,6 +32,7 @@ import { payloadMigration } from './payload'; import { addSeverityToCreateUserAction } from './severity'; import { UserActions } from './types'; import { getAllPersistableAttachmentMigrations } from '../get_all_persistable_attachment_migrations'; +import { addAssigneesToCreateUserAction } from './assignees'; export interface UserActionsMigrationsDeps { persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; @@ -98,6 +99,7 @@ export const createUserActionsMigrations = ( '8.0.0': removeRuleInformation, '8.1.0': payloadMigration, '8.3.0': addSeverityToCreateUserAction, + '8.5.0': addAssigneesToCreateUserAction, }; return mergeSavedObjectMigrationMaps(persistableStateAttachmentMigrations, userActionsMigrations); diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 1088187c7b46b..b5902778ae73d 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -157,6 +157,7 @@ describe('CasesService', () => { } = unsecuredSavedObjectsClient.update.mock.calls[0][2] as Partial; expect(restUpdateAttributes).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "created_at": "2019-11-25T21:54:48.952Z", @@ -481,6 +482,7 @@ describe('CasesService', () => { expect(creationAttributes.external_service).not.toHaveProperty('connector_id'); expect(creationAttributes).toMatchInlineSnapshot(` Object { + "assignees": Array [], "closed_at": null, "closed_by": null, "connector": Object { diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index ff86783ae8e9c..8206eee3515e3 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -126,14 +126,17 @@ export const basicCaseFields: CaseAttributes = { syncAlerts: true, }, owner: SECURITY_SOLUTION_OWNER, + assignees: [], }; export const createCaseSavedObjectResponse = ({ connector, externalService, + overrides, }: { connector?: ESCaseConnectorWithId; externalService?: CaseFullExternalService; + overrides?: Partial; } = {}): SavedObject => { const references: SavedObjectReference[] = createSavedObjectReferences({ connector, @@ -168,6 +171,7 @@ export const createCaseSavedObjectResponse = ({ id: '1', attributes: { ...basicCaseFields, + ...overrides, // if connector is null we'll default this to an incomplete jira value because the service // should switch it to a none connector when the id can't be found in the references array connector: formattedConnector, diff --git a/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts b/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts index 818bc69120e7f..1815b5abe7491 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts @@ -546,6 +546,47 @@ describe('UserActionBuilder', () => { `); }); + it('builds an assign user action correctly', () => { + const builder = builderFactory.getBuilder(ActionTypes.assignees)!; + const userAction = builder.build({ + payload: { assignees: [{ uid: '1' }, { uid: '2' }] }, + ...commonArgs, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "add", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "assignees": Array [ + Object { + "uid": "1", + }, + Object { + "uid": "2", + }, + ], + }, + "type": "assignees", + }, + "references": Array [ + Object { + "id": "123", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + it('builds a settings user action correctly', () => { const builder = builderFactory.getBuilder(ActionTypes.settings)!; const userAction = builder.build({ @@ -601,6 +642,11 @@ describe('UserActionBuilder', () => { }, "owner": "securitySolution", "payload": Object { + "assignees": Array [ + Object { + "uid": "1", + }, + ], "connector": Object { "fields": Object { "category": "Denial of Service", diff --git a/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts b/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts index 14b3afe2e8750..fd3af0b0670cb 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts @@ -20,8 +20,10 @@ import { UserActionBuilder } from './abstract_builder'; import { SeverityUserActionBuilder } from './builders/severity'; import { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; import { BuilderDeps } from './types'; +import { AssigneesUserActionBuilder } from './builders/assignees'; const builderMap = { + assignees: AssigneesUserActionBuilder, title: TitleUserActionBuilder, create_case: CreateCaseUserActionBuilder, connector: ConnectorUserActionBuilder, diff --git a/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts b/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts new file mode 100644 index 0000000000000..87e4a6ab19c76 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ActionTypes, Actions } from '../../../../common/api'; +import { UserActionBuilder } from '../abstract_builder'; +import { UserActionParameters, BuilderReturnValue } from '../types'; + +export class AssigneesUserActionBuilder extends UserActionBuilder { + build(args: UserActionParameters<'assignees'>): BuilderReturnValue { + return this.buildCommonUserAction({ + ...args, + action: args.action ?? Actions.add, + valueKey: 'assignees', + value: args.payload.assignees, + type: ActionTypes.assignees, + }); + } +} diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts index cda0fe8237d82..151c8a6d40ed0 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.test.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.test.ts @@ -13,11 +13,13 @@ import { SavedObjectReference, SavedObjectsFindResponse, SavedObjectsFindResult, + SavedObjectsUpdateResponse, } from '@kbn/core/server'; import { ACTION_SAVED_OBJECT_TYPE } from '@kbn/actions-plugin/server'; import { Actions, ActionTypes, + CaseAttributes, CaseSeverity, CaseStatuses, CaseUserActionAttributes, @@ -39,6 +41,7 @@ import { } from '../../common/constants'; import { + createCaseSavedObjectResponse, createConnectorObject, createExternalService, createJiraConnector, @@ -51,6 +54,9 @@ import { updatedCases, comment, attachments, + updatedAssigneesCases, + originalCasesWithAssignee, + updatedTagsCases, } from './mocks'; import { CaseUserActionService, transformFindResponseToExternalModel } from '.'; import { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; @@ -667,6 +673,7 @@ describe('CaseUserActionService', () => { type: 'create_case', owner: 'securitySolution', payload: { + assignees: [{ uid: '1' }], connector: { fields: { category: 'Denial of Service', @@ -1068,6 +1075,267 @@ describe('CaseUserActionService', () => { { refresh: undefined } ); }); + + it('creates the correct user actions when an assignee is added', async () => { + await service.bulkCreateUpdateCase({ + ...commonArgs, + originalCases, + updatedCases: updatedAssigneesCases, + user: commonArgs.user, + }); + + expect(unsecuredSavedObjectsClient.bulkCreate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "attributes": Object { + "action": "add", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "assignees": Array [ + Object { + "uid": "1", + }, + ], + }, + "type": "assignees", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + ], + Object { + "refresh": undefined, + }, + ] + `); + }); + + it('creates the correct user actions when an assignee is removed', async () => { + const casesWithAssigneeRemoved: Array> = [ + { + ...createCaseSavedObjectResponse(), + id: '1', + attributes: { + assignees: [], + }, + }, + ]; + + await service.bulkCreateUpdateCase({ + ...commonArgs, + originalCases: originalCasesWithAssignee, + updatedCases: casesWithAssigneeRemoved, + user: commonArgs.user, + }); + + expect(unsecuredSavedObjectsClient.bulkCreate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "attributes": Object { + "action": "delete", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "assignees": Array [ + Object { + "uid": "1", + }, + ], + }, + "type": "assignees", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + ], + Object { + "refresh": undefined, + }, + ] + `); + }); + + it('creates the correct user actions when assignees are added and removed', async () => { + const caseAssignees: Array> = [ + { + ...createCaseSavedObjectResponse(), + id: '1', + attributes: { + assignees: [{ uid: '2' }], + }, + }, + ]; + + await service.bulkCreateUpdateCase({ + ...commonArgs, + originalCases: originalCasesWithAssignee, + updatedCases: caseAssignees, + user: commonArgs.user, + }); + + expect(unsecuredSavedObjectsClient.bulkCreate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "attributes": Object { + "action": "add", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "assignees": Array [ + Object { + "uid": "2", + }, + ], + }, + "type": "assignees", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + Object { + "attributes": Object { + "action": "delete", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "assignees": Array [ + Object { + "uid": "1", + }, + ], + }, + "type": "assignees", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + ], + Object { + "refresh": undefined, + }, + ] + `); + }); + + it('creates the correct user actions when tags are added and removed', async () => { + await service.bulkCreateUpdateCase({ + ...commonArgs, + originalCases, + updatedCases: updatedTagsCases, + user: commonArgs.user, + }); + + expect(unsecuredSavedObjectsClient.bulkCreate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "attributes": Object { + "action": "add", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "tags": Array [ + "a", + "b", + ], + }, + "type": "tags", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + Object { + "attributes": Object { + "action": "delete", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "tags": Array [ + "defacement", + ], + }, + "type": "tags", + }, + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + ], + "type": "cases-user-actions", + }, + ], + Object { + "refresh": undefined, + }, + ] + `); + }); }); describe('bulkCreateAttachmentDeletion', () => { diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index 4135352bbc641..27ff69240e0ca 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -27,12 +27,16 @@ import { isCommentUserAction, } from '../../../common/utils/user_actions'; import { + ActionOperationValues, Actions, ActionTypes, + ActionTypeValues, CaseAttributes, CaseUserActionAttributes, CaseUserActionAttributesWithoutConnectorId, CaseUserActionResponse, + CaseUserProfile, + CaseAssignees, CommentRequest, NONE_CONNECTOR_ID, User, @@ -53,13 +57,19 @@ import { } from '../../common/constants'; import { findConnectorIdReference } from '../transform'; import { buildFilter, combineFilters, arraysDifference } from '../../client/utils'; -import { BuilderParameters, BuilderReturnValue, CommonArguments, CreateUserAction } from './types'; +import { + BuilderParameters, + BuilderReturnValue, + CommonArguments, + CreateUserAction, + UserActionParameters, +} from './types'; import { BuilderFactory } from './builder_factory'; import { defaultSortField, isCommentRequestTypeExternalReferenceSO } from '../../common/utils'; import { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; import { injectPersistableReferencesToSO } from '../../attachment_framework/so_references'; import { IndexRefresh } from '../types'; -import { isStringArray } from './type_guards'; +import { isAssigneesArray, isStringArray } from './type_guards'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -92,6 +102,11 @@ interface GetUserActionItemByDifference extends CommonUserActionArgs { newValue: unknown; } +interface TypedUserActionDiffedItems extends GetUserActionItemByDifference { + originalValue: T[]; + newValue: T[]; +} + interface BulkCreateBulkUpdateCaseUserActions extends ClientArgs, IndexRefresh { originalCases: Array>; updatedCases: Array>; @@ -106,6 +121,10 @@ type CreateUserActionClient = CreateUserActio CommonUserActionArgs & IndexRefresh; +type CreatePayloadFunction = ( + items: Item[] +) => UserActionParameters['payload']; + export class CaseUserActionService { private static readonly userActionFieldsAllowed: Set = new Set(Object.keys(ActionTypes)); @@ -120,55 +139,26 @@ export class CaseUserActionService { }); } - private getUserActionItemByDifference({ - field, - originalValue, - newValue, - caseId, - owner, - user, - }: GetUserActionItemByDifference): BuilderReturnValue[] { + private getUserActionItemByDifference( + params: GetUserActionItemByDifference + ): BuilderReturnValue[] { + const { field, originalValue, newValue, caseId, owner, user } = params; + if (!CaseUserActionService.userActionFieldsAllowed.has(field)) { return []; - } - - if (field === ActionTypes.tags && isStringArray(originalValue) && isStringArray(newValue)) { - const tagsUserActionBuilder = this.builderFactory.getBuilder(ActionTypes.tags); - const compareValues = arraysDifference(originalValue, newValue); - const userActions = []; - - if (compareValues && compareValues.addedItems.length > 0) { - const tagAddUserAction = tagsUserActionBuilder?.build({ - action: Actions.add, - caseId, - user, - owner, - payload: { tags: compareValues.addedItems }, - }); - - if (tagAddUserAction) { - userActions.push(tagAddUserAction); - } - } - - if (compareValues && compareValues.deletedItems.length > 0) { - const tagsDeleteUserAction = tagsUserActionBuilder?.build({ - action: Actions.delete, - caseId, - user, - owner, - payload: { tags: compareValues.deletedItems }, - }); - - if (tagsDeleteUserAction) { - userActions.push(tagsDeleteUserAction); - } - } - - return userActions; - } - - if (isUserActionType(field) && newValue != null) { + } else if ( + field === ActionTypes.assignees && + isAssigneesArray(originalValue) && + isAssigneesArray(newValue) + ) { + return this.buildAssigneesUserActions({ ...params, originalValue, newValue }); + } else if ( + field === ActionTypes.tags && + isStringArray(originalValue) && + isStringArray(newValue) + ) { + return this.buildTagsUserActions({ ...params, originalValue, newValue }); + } else if (isUserActionType(field) && newValue != null) { const userActionBuilder = this.builderFactory.getBuilder(ActionTypes[field]); const fieldUserAction = userActionBuilder?.build({ caseId, @@ -183,6 +173,85 @@ export class CaseUserActionService { return []; } + private buildAssigneesUserActions(params: TypedUserActionDiffedItems) { + const createPayload: CreatePayloadFunction = ( + items: CaseAssignees + ) => ({ assignees: items }); + + return this.buildAddDeleteUserActions(params, createPayload, ActionTypes.assignees); + } + + private buildTagsUserActions(params: TypedUserActionDiffedItems) { + const createPayload: CreatePayloadFunction = ( + items: string[] + ) => ({ + tags: items, + }); + + return this.buildAddDeleteUserActions(params, createPayload, ActionTypes.tags); + } + + private buildAddDeleteUserActions( + params: TypedUserActionDiffedItems, + createPayload: CreatePayloadFunction, + actionType: ActionType + ) { + const { originalValue, newValue } = params; + const compareValues = arraysDifference(originalValue, newValue); + + const addUserAction = this.buildUserAction({ + commonArgs: params, + actionType, + action: Actions.add, + createPayload, + modifiedItems: compareValues?.addedItems, + }); + const deleteUserAction = this.buildUserAction({ + commonArgs: params, + actionType, + action: Actions.delete, + createPayload, + modifiedItems: compareValues?.deletedItems, + }); + + return [ + ...(addUserAction ? [addUserAction] : []), + ...(deleteUserAction ? [deleteUserAction] : []), + ]; + } + + private buildUserAction({ + commonArgs, + actionType, + action, + createPayload, + modifiedItems, + }: { + commonArgs: CommonUserActionArgs; + actionType: ActionType; + action: ActionOperationValues; + createPayload: CreatePayloadFunction; + modifiedItems?: Item[] | null; + }) { + const userActionBuilder = this.builderFactory.getBuilder(actionType); + + if (!userActionBuilder || !modifiedItems || modifiedItems.length <= 0) { + return; + } + + const { caseId, owner, user } = commonArgs; + + const userAction = userActionBuilder.build({ + action, + caseId, + user, + owner, + payload: createPayload(modifiedItems), + }); + + return userAction; + } + public async bulkCreateCaseDeletion({ unsecuredSavedObjectsClient, cases, diff --git a/x-pack/plugins/cases/server/services/user_actions/mocks.ts b/x-pack/plugins/cases/server/services/user_actions/mocks.ts index bc35f98bf926e..80f2979716ca2 100644 --- a/x-pack/plugins/cases/server/services/user_actions/mocks.ts +++ b/x-pack/plugins/cases/server/services/user_actions/mocks.ts @@ -7,11 +7,17 @@ import { CASE_SAVED_OBJECT } from '../../../common/constants'; import { SECURITY_SOLUTION_OWNER } from '../../../common'; -import { CaseSeverity, CaseStatuses, CommentType, ConnectorTypes } from '../../../common/api'; +import { + CasePostRequest, + CaseSeverity, + CaseStatuses, + CommentType, + ConnectorTypes, +} from '../../../common/api'; import { createCaseSavedObjectResponse } from '../test_utils'; import { transformSavedObjectToExternalModel } from '../cases/transform'; -export const casePayload = { +export const casePayload: CasePostRequest = { title: 'Case SIR', tags: ['sir'], description: 'testing sir', @@ -32,6 +38,7 @@ export const casePayload = { settings: { syncAlerts: true }, severity: CaseSeverity.LOW, owner: SECURITY_SOLUTION_OWNER, + assignees: [{ uid: '1' }], }; export const externalService = { @@ -76,6 +83,30 @@ export const updatedCases = [ }, ]; +export const originalCasesWithAssignee = [ + { ...createCaseSavedObjectResponse({ overrides: { assignees: [{ uid: '1' }] } }), id: '1' }, +].map((so) => transformSavedObjectToExternalModel(so)); + +export const updatedAssigneesCases = [ + { + ...createCaseSavedObjectResponse(), + id: '1', + attributes: { + assignees: [{ uid: '1' }], + }, + }, +]; + +export const updatedTagsCases = [ + { + ...createCaseSavedObjectResponse(), + id: '1', + attributes: { + tags: ['a', 'b'], + }, + }, +]; + export const comment = { comment: 'a comment', type: CommentType.user as const, diff --git a/x-pack/plugins/cases/server/services/user_actions/type_guards.test.ts b/x-pack/plugins/cases/server/services/user_actions/type_guards.test.ts index 566693ec11299..209116e3d4fd4 100644 --- a/x-pack/plugins/cases/server/services/user_actions/type_guards.test.ts +++ b/x-pack/plugins/cases/server/services/user_actions/type_guards.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isObjectArray, isStringArray } from './type_guards'; +import { isAssigneesArray, isStringArray } from './type_guards'; describe('type_guards', () => { describe('isStringArray', () => { @@ -30,25 +30,33 @@ describe('type_guards', () => { }); }); - describe('isObjectArray', () => { + describe('isAssigneesArray', () => { it('returns true when the value is an empty array', () => { - expect(isObjectArray([])).toBeTruthy(); + expect(isAssigneesArray([])).toBeTruthy(); }); - it('returns true when the value is an array of a single string', () => { - expect(isObjectArray([{ a: '1' }])).toBeTruthy(); + it('returns false when the value is not an array of assignees', () => { + expect(isAssigneesArray([{ a: '1' }])).toBeFalsy(); }); - it('returns true when the value is an array of multiple strings', () => { - expect(isObjectArray([{ a: 'a' }, { b: 'b' }])).toBeTruthy(); + it('returns false when the value is an array of assignees and non assignee objects', () => { + expect(isAssigneesArray([{ uid: '1' }, { hi: '2' }])).toBeFalsy(); }); - it('returns false when the value is an array of strings and numbers', () => { - expect(isObjectArray([{ a: 'a' }, 1])).toBeFalsy(); + it('returns true when the value is an array of a single assignee', () => { + expect(isAssigneesArray([{ uid: '1' }])).toBeTruthy(); + }); + + it('returns true when the value is an array of multiple assignees', () => { + expect(isAssigneesArray([{ uid: 'a' }, { uid: 'b' }])).toBeTruthy(); + }); + + it('returns false when the value is an array of assignees and numbers', () => { + expect(isAssigneesArray([{ uid: 'a' }, 1])).toBeFalsy(); }); it('returns false when the value is an array of strings and objects', () => { - expect(isObjectArray(['a', {}])).toBeFalsy(); + expect(isAssigneesArray(['a', {}])).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/cases/server/services/user_actions/type_guards.ts b/x-pack/plugins/cases/server/services/user_actions/type_guards.ts index 25c9341b47363..d1afd1b91c072 100644 --- a/x-pack/plugins/cases/server/services/user_actions/type_guards.ts +++ b/x-pack/plugins/cases/server/services/user_actions/type_guards.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { isPlainObject, isString } from 'lodash'; +import { isString } from 'lodash'; +import { CaseAssignees, CaseAssigneesRt } from '../../../common/api/cases/assignee'; export const isStringArray = (value: unknown): value is string[] => { return Array.isArray(value) && value.every((val) => isString(val)); }; -export const isObjectArray = (value: unknown): value is Array> => { - return Array.isArray(value) && value.every((val) => isPlainObject(val)); +export const isAssigneesArray = (value: unknown): value is CaseAssignees => { + return CaseAssigneesRt.is(value); }; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts index f1b03dc8a8c56..667811b2b5317 100644 --- a/x-pack/plugins/cases/server/services/user_actions/types.ts +++ b/x-pack/plugins/cases/server/services/user_actions/types.ts @@ -6,6 +6,7 @@ */ import { SavedObjectReference } from '@kbn/core/server'; +import { CaseAssignees } from '../../../common/api/cases/assignee'; import { CasePostRequest, CaseSettings, @@ -36,6 +37,9 @@ export interface BuilderParameters { tags: { parameters: { payload: { tags: string[] } }; }; + assignees: { + parameters: { payload: { assignees: CaseAssignees } }; + }; pushed: { parameters: { payload: { diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 3fe552fef322a..f79b651f826ca 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -34,6 +34,7 @@ export interface Cluster { meta: { clusterId: string; benchmarkName: string; + benchmarkId: BenchmarkId; lastUpdate: number; // unix epoch time }; stats: Stats; diff --git a/x-pack/plugins/cloud_security_posture/public/assets/icons/cis_eks_logo.svg b/x-pack/plugins/cloud_security_posture/public/assets/icons/cis_eks_logo.svg new file mode 100644 index 0000000000000..e31c56edc8f08 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/assets/icons/cis_eks_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx new file mode 100644 index 0000000000000..95be9d3cf42ff --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiIcon } from '@elastic/eui'; +import type { BenchmarkId } from '../../common/types'; +import cisK8sVanillaIcon from '../assets/icons/k8s_logo.svg'; +import cisEksIcon from '../assets/icons/cis_eks_logo.svg'; + +interface Props { + type: BenchmarkId; +} + +const getBenchmarkIdIconType = (props: Props): string => { + switch (props.type) { + case 'cis_eks': + return cisEksIcon; + case 'cis_k8s': + default: + return cisK8sVanillaIcon; + } +}; + +export const CISBenchmarkIcon = (props: Props) => ( + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index c5085d48ef6a8..64ab75c1a210c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -20,6 +20,7 @@ import moment from 'moment'; import { PartitionElementEvent } from '@elastic/charts'; import { EuiThemeComputed } from '@elastic/eui/src/services/theme/types'; import { i18n } from '@kbn/i18n'; +import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart'; import { ChartPanel } from '../../../components/chart_panel'; import type { ComplianceDashboardData, Evaluation } from '../../../../common/types'; @@ -61,7 +62,6 @@ export const BenchmarksSection = ({ <> {complianceData.clusters.map((cluster) => { const shortId = cluster.meta.clusterId.slice(0, 6); - return ( @@ -82,8 +82,7 @@ export const BenchmarksSection = ({
- {/* TODO: change default k8s logo to use a getBenchmarkLogo function */} - + {INTERNAL_FEATURE_FLAGS.showManageRulesMock && ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx index a13def04f5802..b1b35e3333d0f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx @@ -30,7 +30,8 @@ import { ResourceTab } from './resource_tab'; import { JsonTab } from './json_tab'; import { OverviewTab } from './overview_tab'; import { RuleTab } from './rule_tab'; -import k8sLogoIcon from '../../../assets/icons/k8s_logo.svg'; +import type { BenchmarkId } from '../../../../common/types'; +import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; const tabs = [ { @@ -74,13 +75,13 @@ export const Markdown: React.FC> = (props) => ); -export const CisKubernetesIcons = () => ( +export const CisKubernetesIcons = ({ benchmarkId }: { benchmarkId: BenchmarkId }) => ( - + ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/overview_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/overview_tab.tsx index 94d221ddf9af7..1e3aa90d5169c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/overview_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/overview_tab.tsx @@ -49,7 +49,7 @@ const getDetailsList = (data: CspFinding, discoverIndexLink: string | undefined) title: i18n.translate('xpack.csp.findings.findingsFlyout.overviewTab.frameworkSourcesTitle', { defaultMessage: 'Framework Sources', }), - description: , + description: , }, { title: i18n.translate('xpack.csp.findings.findingsFlyout.overviewTab.cisSectionTitle', { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/rule_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/rule_tab.tsx index 3d2f867fbdc81..9b5c6d65d7d49 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/rule_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/rule_tab.tsx @@ -28,7 +28,7 @@ export const getRuleList = (rule: CspFinding['rule']) => [ title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.frameworkSourcesTitle', { defaultMessage: 'Framework Sources', }), - description: , + description: , }, { title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.cisSectionTitle', { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts index a24e60dd7be8f..2b3ec5fc00182 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts @@ -11,9 +11,12 @@ const mockClusterBuckets: ClusterBucket[] = [ { key: 'cluster_id', doc_count: 10, - benchmarks: { + benchmarkName: { buckets: [{ key: 'CIS Kubernetes', doc_count: 10 }], }, + benchmarkId: { + buckets: [{ key: 'cis_k8s', doc_count: 10 }], + }, timestamps: { buckets: [{ key: 123, doc_count: 1 }], }, @@ -59,6 +62,7 @@ describe('getClustersFromAggs', () => { lastUpdate: 123, clusterId: 'cluster_id', benchmarkName: 'CIS Kubernetes', + benchmarkId: 'cis_k8s', }, stats: { totalFindings: 12, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 38c3edcd9c096..c930dba9fb641 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -11,7 +11,7 @@ import type { QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import { Cluster } from '../../../common/types'; +import type { BenchmarkId, Cluster } from '../../../common/types'; import { getFailedFindingsFromAggs, failedFindingsAggQuery, @@ -29,7 +29,8 @@ export interface ClusterBucket extends FailedFindingsQueryResult, KeyDocCount { passed_findings: { doc_count: number; }; - benchmarks: Aggregation; + benchmarkName: Aggregation; + benchmarkId: Aggregation>; timestamps: Aggregation>; } @@ -48,11 +49,16 @@ export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string): field: 'cluster_id', }, aggs: { - benchmarks: { + benchmarkName: { terms: { field: 'rule.benchmark.name', }, }, + benchmarkId: { + terms: { + field: 'rule.benchmark.id', + }, + }, timestamps: { terms: { field: '@timestamp', @@ -75,14 +81,21 @@ export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string): export const getClustersFromAggs = (clusters: ClusterBucket[]): ClusterWithoutTrend[] => clusters.map((cluster) => { // get cluster's meta data - const benchmarks = cluster.benchmarks.buckets; - if (!Array.isArray(benchmarks)) throw new Error('missing aggs by benchmarks per cluster'); + const benchmarkNames = cluster.benchmarkName.buckets; + const benchmarkIds = cluster.benchmarkId.buckets; + + if (!Array.isArray(benchmarkIds) || benchmarkIds.length === 0) + throw new Error('missing aggs by benchmarkIds per cluster'); + + if (!Array.isArray(benchmarkNames)) throw new Error('missing aggs by benchmarks per cluster'); + const timestamps = cluster.timestamps.buckets; if (!Array.isArray(timestamps)) throw new Error('missing aggs by timestamps per cluster'); const meta = { clusterId: cluster.key, - benchmarkName: benchmarks[0].key, + benchmarkName: benchmarkNames[0].key, + benchmarkId: benchmarkIds[0].key, lastUpdate: timestamps[0].key, }; diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 98f34d57c364a..d762fa4960a61 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -96,3 +96,5 @@ export const ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID = 'ent-search-audit-logs'; export const APP_SEARCH_URL = '/app/enterprise_search/app_search'; export const ENTERPRISE_SEARCH_ELASTICSEARCH_URL = '/app/enterprise_search/elasticsearch'; export const WORKPLACE_SEARCH_URL = '/app/enterprise_search/workplace_search'; + +export const ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT = 25; diff --git a/x-pack/plugins/enterprise_search/common/types/indices.ts b/x-pack/plugins/enterprise_search/common/types/indices.ts index bf19d8789519e..38f4b9873eba2 100644 --- a/x-pack/plugins/enterprise_search/common/types/indices.ts +++ b/x-pack/plugins/enterprise_search/common/types/indices.ts @@ -16,12 +16,13 @@ import { Connector } from './connectors'; import { Crawler } from './crawler'; export interface ElasticsearchIndex { + count: number; // Elasticsearch _count health?: HealthStatus; name: IndexName; status?: IndicesStatsIndexMetadataState; total: { docs: { - count: number; + count: number; // Lucene count (includes nested documents) deleted: number; }; store: { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_creation_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_creation_logic.mock.ts index e256933cacccd..6cfba782698b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_creation_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_creation_logic.mock.ts @@ -7,6 +7,8 @@ import dedent from 'dedent'; +import { ElasticsearchIndexWithPrivileges } from '../../../../common/types/indices'; + import { EngineCreationSteps } from '../components/engine_creation/engine_creation_logic'; import { SearchIndexSelectableOption } from '../components/engine_creation/search_index_selectable'; @@ -32,8 +34,9 @@ export const DEFAULT_VALUES = { selectedIndexFormatted: undefined, }; -export const mockElasticsearchIndices = [ +export const mockElasticsearchIndices: ElasticsearchIndexWithPrivileges[] = [ { + count: 0, health: 'yellow', status: 'open', name: 'search-my-index-1', @@ -51,6 +54,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'my-index-2', @@ -68,6 +72,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'search-my-index-2', @@ -85,6 +90,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'alias-my-index-2', @@ -102,6 +108,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'index-without-read-privilege', @@ -119,6 +126,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'index-without-manage-privilege', @@ -136,6 +144,7 @@ export const mockElasticsearchIndices = [ }, }, { + count: 100, health: 'green', status: 'open', name: 'alias-without-manage-privilege', @@ -156,6 +165,7 @@ export const mockElasticsearchIndices = [ export const mockSearchIndexOptions: SearchIndexSelectableOption[] = [ { + count: 0, label: 'search-my-index-1', health: 'yellow', status: 'open', @@ -181,6 +191,7 @@ export const mockSearchIndexOptions: SearchIndexSelectableOption[] = [ }, }, { + count: 100, label: 'my-index-2', health: 'green', status: 'open', @@ -207,6 +218,7 @@ export const mockSearchIndexOptions: SearchIndexSelectableOption[] = [ }, }, { + count: 100, label: 'search-my-index-2', health: 'green', status: 'open', @@ -229,6 +241,7 @@ export const mockSearchIndexOptions: SearchIndexSelectableOption[] = [ }, }, { + count: 100, label: 'alias-my-index-2', health: 'green', status: 'open', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.test.tsx index c3c1bb012b755..b798b71e305e5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.test.tsx @@ -15,6 +15,7 @@ import { shallow } from 'enzyme'; import { IndexStatusDetails, SearchIndexSelectableOption } from './search_index_selectable'; const mockOption: SearchIndexSelectableOption = { + count: 123, label: 'string', alias: true, badge: { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.tsx index 848e0d230c90a..93e045d2ecb26 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/search_index_selectable.tsx @@ -45,6 +45,7 @@ export interface SearchIndexSelectableOption { }; }; checked?: 'on'; + count: number; } const healthColorsMap = { @@ -89,7 +90,7 @@ export const IndexStatusDetails: React.FC = ({ option } { defaultMessage: 'Docs count' } )} - : {option.total?.docs?.count ?? '-'} + : {option.count ?? '-'} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/utils.ts index f823f5f54d652..41b34e746f9e9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/utils.ts @@ -91,6 +91,7 @@ export const formatIndicesToSelectable = ( label: index.alias ? 'Alias' : 'Index', ...(icon ? { icon } : {}), }, + count: index.count, disabled: index.alias && !index.name.startsWith('search-'), label: index.name, health: index.health, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts index 2624df01c5c09..522f79ccca7f1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts @@ -227,6 +227,17 @@ describe('EnginesLogic', () => { expect(EnginesLogic.actions.loadEngines).toHaveBeenCalled(); }); + it('should call loadEngines if engine.type === elasticsearch', () => { + jest.spyOn(EnginesLogic.actions, 'loadEngines'); + + EnginesLogic.actions.onDeleteEngineSuccess({ + ...MOCK_ENGINE, + type: 'elasticsearch' as EngineTypes.elasticsearch, + }); + + expect(EnginesLogic.actions.loadEngines).toHaveBeenCalled(); + }); + it('should call loadMetaEngines if engine.type === meta', () => { jest.spyOn(EnginesLogic.actions, 'loadMetaEngines'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts index c6fb1a401c591..827ee23d0a267 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts @@ -150,7 +150,9 @@ export const EnginesLogic = kea>({ }, onDeleteEngineSuccess: async ({ engine }) => { flashSuccessToast(DELETE_ENGINE_MESSAGE(engine.name)); - if ([EngineTypes.default, EngineTypes.indexed].includes(engine.type)) { + if ( + [EngineTypes.default, EngineTypes.indexed, EngineTypes.elasticsearch].includes(engine.type) + ) { actions.loadEngines(); } else if (engine.type === EngineTypes.meta) { actions.loadMetaEngines(); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts index 9ff72636d3466..ba72c8ada0dd1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts @@ -10,6 +10,7 @@ import { ElasticsearchIndexWithIngestion } from '../../../../common/types/indice export const indices: ElasticsearchIndexWithIngestion[] = [ { + count: 1, name: 'api', total: { docs: { @@ -39,6 +40,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ status: ConnectorStatus.CONFIGURED, sync_now: false, }, + count: 1, name: 'connector', total: { docs: { @@ -49,6 +51,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ }, }, { + count: 1, crawler: { id: '3', index_name: 'crawler', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts index 1e31815b73ca8..9ade186d55380 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts @@ -16,6 +16,7 @@ import { } from '../types'; export const apiIndex: ApiViewIndex = { + count: 1, ingestionMethod: IngestionMethod.API, ingestionStatus: IngestionStatus.CONNECTED, lastUpdated: null, @@ -48,6 +49,7 @@ export const connectorIndex: ConnectorViewIndex = { status: ConnectorStatus.CONFIGURED, sync_now: false, }, + count: 1, ingestionMethod: IngestionMethod.CONNECTOR, ingestionStatus: IngestionStatus.INCOMPLETE, lastUpdated: 'never', @@ -61,6 +63,7 @@ export const connectorIndex: ConnectorViewIndex = { }, }; export const crawlerIndex: CrawlerViewIndex = { + count: 1, crawler: { id: '3', index_name: 'crawler', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/search_documents/search_documents_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/search_documents/search_documents_logic.ts index c4acf606efca3..b423b00c97ed1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/search_documents/search_documents_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/search_documents/search_documents_logic.ts @@ -7,19 +7,31 @@ import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; +import { Meta } from '../../../../../common/types'; + import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; import { HttpLogic } from '../../../shared/http'; export const searchDocuments = async ({ + docsPerPage, indexName, - query, + pagination, + query: q, }: { + docsPerPage?: number; indexName: string; + pagination: { pageIndex: number; pageSize: number; totalItemCount: number }; query: string; }) => { - const route = `/internal/enterprise_search/indices/${indexName}/search/${query}`; + const route = `/internal/enterprise_search/indices/${indexName}/search/${q}`; + const query = { + page: pagination.pageIndex, + size: docsPerPage || pagination.pageSize, + }; - return await HttpLogic.values.http.get(route); + return await HttpLogic.values.http.get<{ meta: Meta; results: SearchResponseBody }>(route, { + query, + }); }; export const SearchDocumentsApiLogic = createApiLogic( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx index f83dfa3eb4379..c505428449f8a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx @@ -13,6 +13,7 @@ import { EuiSteps, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexTemplate } from '../new_search_index_template'; import { MethodApiLogic } from './method_api_logic'; @@ -32,30 +33,7 @@ export const MethodApi: React.FC = () => { > -

- {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.content', - { - defaultMessage: - 'Provide a unique name for your index and select an optional index language.', - } - )} -

- - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', - { - defaultMessage: 'Create an Elasticsearch index', - } - ), - - titleSize: 'xs', - }, + CREATE_ELASTICSEARCH_INDEX_STEP, { children: ( @@ -79,29 +57,7 @@ export const MethodApi: React.FC = () => { ), titleSize: 'xs', }, - { - children: ( - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.api.steps.buildSearchExperience.content', - { - defaultMessage: - 'Connect your newly created Elasticsearch index to an App Search engine to build a cusomtizable search experience.', - } - )} -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title', - { - defaultMessage: 'Build a search experience', - } - ), - titleSize: 'xs', - }, + BUILD_SEARCH_EXPERIENCE_STEP, ]} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx index acc89074343ee..a3d3456e21f40 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx @@ -15,17 +15,12 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - APP_SEARCH_URL, - ENTERPRISE_SEARCH_ELASTICSEARCH_URL, -} from '../../../../../../common/constants'; - import { HttpError, Status } from '../../../../../../common/types/api'; import { ErrorCode } from '../../../../../../common/types/error_codes'; import { docLinks } from '../../../../shared/doc_links'; -import { EuiLinkTo } from '../../../../shared/react_router_helpers'; import { AddConnectorPackageApiLogic } from '../../../api/connector_package/add_connector_package_api_logic'; +import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexLogic } from '../new_search_index_logic'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -133,37 +128,7 @@ export const MethodConnector: React.FC = () => { > -

- - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.createConnectorIndex.languageAnalyzerLink', - { defaultMessage: 'language analyzer' } - )} - - ), - }} - /> -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', - { - defaultMessage: 'Create an Elasticsearch index', - } - ), - - titleSize: 'xs', - }, + CREATE_ELASTICSEARCH_INDEX_STEP, { children: ( @@ -194,52 +159,7 @@ export const MethodConnector: React.FC = () => { ), titleSize: 'xs', }, - { - children: ( - -

- - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.appSearchLink', - { defaultMessage: 'App Search' } - )} - - ), - elasticsearchLink: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.elasticsearchLink', - { defaultMessage: 'Elasticsearch' } - )} - - ), - searchEngineLink: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.searchEngineLink', - { defaultMessage: 'search engine' } - )} - - ), - }} - /> -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title', - { - defaultMessage: 'Build a search experience', - } - ), - titleSize: 'xs', - }, + BUILD_SEARCH_EXPERIENCE_STEP, ]} /> {confirmModal} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx index 1634efd5420c5..4c947ddb0bb3a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx @@ -16,6 +16,7 @@ import { i18n } from '@kbn/i18n'; import { Status } from '../../../../../../common/types/api'; import { docLinks } from '../../../../shared/doc_links'; import { CreateCrawlerIndexApiLogic } from '../../../api/crawler/create_crawler_index_api_logic'; +import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexTemplate } from '../new_search_index_template'; import { MethodCrawlerLogic } from './method_crawler_logic'; @@ -41,30 +42,7 @@ export const MethodCrawler: React.FC = () => { > -

- {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.content', - { - defaultMessage: - 'Provide a unique name for your index and select an optional index language.', - } - )} -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', - { - defaultMessage: 'Create an Elasticsearch index', - } - ), - - titleSize: 'xs', - }, + CREATE_ELASTICSEARCH_INDEX_STEP, { children: ( @@ -88,29 +66,7 @@ export const MethodCrawler: React.FC = () => { ), titleSize: 'xs', }, - { - children: ( - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.crawler.steps.buildSearchExperience.content', - { - defaultMessage: - 'Connect your newly created Elasticsearch index to an App Search engine to build a cusomtizable search experience.', - } - )} -

-
- ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title', - { - defaultMessage: 'Build a search experience', - } - ), - titleSize: 'xs', - }, + BUILD_SEARCH_EXPERIENCE_STEP, ]} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx new file mode 100644 index 0000000000000..20ca643be580d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiLink, EuiText } from '@elastic/eui'; + +import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; +import { i18n } from '@kbn/i18n'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { + APP_SEARCH_URL, + ENTERPRISE_SEARCH_ELASTICSEARCH_URL, +} from '../../../../../common/constants'; +import { docLinks } from '../../../shared/doc_links'; +import { EuiLinkTo } from '../../../shared/react_router_helpers'; + +export const CREATE_ELASTICSEARCH_INDEX_STEP: EuiContainedStepProps = { + children: ( + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink', + { defaultMessage: 'language analyzer' } + )} + + ), + }} + /> +

+
+ ), + status: 'incomplete', + title: i18n.translate('xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', { + defaultMessage: 'Create an Elasticsearch index', + }), + + titleSize: 'xs', +}; + +export const BUILD_SEARCH_EXPERIENCE_STEP: EuiContainedStepProps = { + children: ( + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink', + { defaultMessage: 'App Search' } + )} + + ), + elasticsearchLink: ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink', + { defaultMessage: 'Elasticsearch' } + )} + + ), + searchEngineLink: ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink', + { defaultMessage: 'search engine' } + )} + + ), + }} + /> +

+
+ ), + status: 'incomplete', + title: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title', + { + defaultMessage: 'Build a search experience', + } + ), + titleSize: 'xs', +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.test.tsx new file mode 100644 index 0000000000000..f59f6f4e54a58 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.test.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues, setMockActions } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiCallOut, EuiPagination } from '@elastic/eui'; + +import { Status } from '../../../../../../../common/types/api'; + +import { Result } from '../../../../../shared/result/result'; + +import { INDEX_DOCUMENTS_META_DEFAULT } from '../../documents_logic'; + +import { DocumentList } from './document_list'; + +const mockActions = {}; + +export const DEFAULT_VALUES = { + data: undefined, + indexName: 'indexName', + isLoading: true, + mappingData: undefined, + mappingStatus: 0, + meta: INDEX_DOCUMENTS_META_DEFAULT, + query: '', + results: [], + status: Status.IDLE, +}; + +const mockValues = { ...DEFAULT_VALUES }; + +describe('DocumentList', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(mockValues); + setMockActions(mockActions); + }); + it('renders empty', () => { + const wrapper = shallow(); + expect(wrapper.find(Result)).toHaveLength(0); + expect(wrapper.find(EuiPagination)).toHaveLength(2); + }); + + it('renders documents when results when there is data and mappings', () => { + setMockValues({ + ...mockValues, + results: [ + { + _id: 'M9ntXoIBTq5dF-1Xnc8A', + _index: 'kibana_sample_data_flights', + _score: 1, + _source: { + AvgTicketPrice: 268.24159591388866, + }, + }, + { + _id: 'NNntXoIBTq5dF-1Xnc8A', + _index: 'kibana_sample_data_flights', + _score: 1, + _source: { + AvgTicketPrice: 68.91388866, + }, + }, + ], + simplifiedMapping: { + AvgTicketPrice: { + type: 'float', + }, + }, + }); + + const wrapper = shallow(); + expect(wrapper.find(Result)).toHaveLength(2); + }); + + it('renders callout when total results are 10.000', () => { + setMockValues({ + ...mockValues, + meta: { + page: { + ...INDEX_DOCUMENTS_META_DEFAULT.page, + total_results: 10000, + }, + }, + }); + const wrapper = shallow(); + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.tsx new file mode 100644 index 0000000000000..779c636968435 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/document_list/document_list.tsx @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; + +import { + EuiButtonEmpty, + EuiCallOut, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPagination, + EuiProgress, + EuiPopover, + EuiText, + EuiSpacer, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { Result } from '../../../../../shared/result/result'; + +import { DocumentsLogic } from '../../documents_logic'; + +export const DocumentList: React.FC = () => { + const { + docsPerPage, + isLoading, + meta, + results, + simplifiedMapping: mappings, + } = useValues(DocumentsLogic); + const { onPaginate, setDocsPerPage } = useActions(DocumentsLogic); + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const resultToField = (result: SearchHit) => { + if (mappings && result._source && !Array.isArray(result._source)) { + if (typeof result._source === 'object') { + return Object.entries(result._source).map(([key, value]) => { + return { + fieldName: key, + fieldType: mappings[key]?.type ?? 'object', + fieldValue: JSON.stringify(value, null, 2), + }; + }); + } + } + return []; + }; + + const docsPerPageButton = ( + { + setIsPopoverOpen(true); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage', + { + defaultMessage: 'Documents per page: {docPerPage}', + values: { docPerPage: docsPerPage }, + } + )} + + ); + + const getIconType = (size: number) => { + return size === docsPerPage ? 'check' : 'empty'; + }; + + const docsPerPageOptions = [ + { + setIsPopoverOpen(false); + setDocsPerPage(10); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option', + { defaultMessage: '{docCount} documents', values: { docCount: 10 } } + )} + , + + { + setIsPopoverOpen(false); + setDocsPerPage(25); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option', + { defaultMessage: '{docCount} documents', values: { docCount: 25 } } + )} + , + { + setIsPopoverOpen(false); + setDocsPerPage(50); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option', + { defaultMessage: '{docCount} documents', values: { docCount: 50 } } + )} + , + ]; + + return ( + <> + + + +

+ Showing {results.length} of {meta.page.total_results}. + Search results maxed at 10.000 documents. +

+
+ {isLoading && } + + {results.map((result) => { + return ( + + + + + ); + })} + + + + + + + { + setIsPopoverOpen(false); + }} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + + + + {meta.page.total_results === 10000 && ( + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndex.documents.documentList.resultLimit', + { + defaultMessage: + 'Only the first 10,000 results are available for paging. Please use the search bar to filter down your results.', + } + )} +

+
+ )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx index a0b9d08e457c3..c4709b1f48c6d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling.tsx @@ -94,7 +94,7 @@ export const ConnectorSchedulingComponent: React.FC = () => { { selectedDomainUrls: ['https://www.elastic.co', 'https://swiftype.com'], }); CrawlerLogic.mount(); - jest.spyOn(CrawlerLogic.actions, 'startCrawl'); + jest.spyOn(CrawlCustomSettingsFlyoutLogic.actions, 'startCrawl'); CrawlCustomSettingsFlyoutLogic.actions.startCustomCrawl(); await nextTick(); - expect(CrawlerLogic.actions.startCrawl).toHaveBeenCalledWith({ + expect(CrawlCustomSettingsFlyoutLogic.actions.startCrawl).toHaveBeenCalledWith({ domain_allowlist: ['https://www.elastic.co', 'https://swiftype.com'], max_crawl_depth: 5, sitemap_discovery_disabled: false, @@ -382,12 +382,12 @@ describe('CrawlCustomSettingsFlyoutLogic', () => { ], }); CrawlerLogic.mount(); - jest.spyOn(CrawlerLogic.actions, 'startCrawl'); + jest.spyOn(CrawlCustomSettingsFlyoutLogic.actions, 'startCrawl'); CrawlCustomSettingsFlyoutLogic.actions.startCustomCrawl(); await nextTick(); - expect(CrawlerLogic.actions.startCrawl).toHaveBeenCalledWith({ + expect(CrawlCustomSettingsFlyoutLogic.actions.startCrawl).toHaveBeenCalledWith({ domain_allowlist: ['https://www.elastic.co', 'https://swiftype.com'], max_crawl_depth: 5, seed_urls: ['https://www.elastic.co/guide', 'https://swiftype.com/documentation'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts index ae2f0fef68567..5d4ed848c8569 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts @@ -10,11 +10,10 @@ import { kea, MakeLogicType } from 'kea'; import { Meta } from '../../../../../../../common/types'; import { flashAPIErrors } from '../../../../../shared/flash_messages'; import { HttpLogic } from '../../../../../shared/http'; -import { GetCrawlerApiLogic } from '../../../../api/crawler/get_crawler_api_logic'; import { DomainConfig, DomainConfigFromServer } from '../../../../api/crawler/types'; import { domainConfigServerToClient } from '../../../../api/crawler/utils'; import { IndexNameLogic } from '../../index_name_logic'; -import { CrawlerLogic, CrawlRequestOverrides } from '../crawler_logic'; +import { CrawlerActions, CrawlerLogic, CrawlRequestOverrides } from '../crawler_logic'; import { extractDomainAndEntryPointFromUrl } from '../domain_management/add_domain/utils'; export interface CrawlCustomSettingsFlyoutLogicValues { @@ -49,6 +48,7 @@ export interface CrawlCustomSettingsFlyoutLogicActions { onSelectSitemapUrls(sitemapUrls: string[]): { sitemapUrls: string[] }; showFlyout(): void; startCustomCrawl(): void; + startCrawl: CrawlerActions['startCrawl']; toggleIncludeSitemapsInRobotsTxt(): void; } @@ -69,7 +69,7 @@ export const CrawlCustomSettingsFlyoutLogic = kea< >({ path: ['enterprise_search', 'crawler', 'crawl_custom_settings_flyout_logic'], connect: { - actions: [GetCrawlerApiLogic, ['apiSuccess', 'apiError', 'makeRequest']], + actions: [CrawlerLogic, ['startCrawl']], }, actions: () => ({ fetchDomainConfigData: true, @@ -124,9 +124,8 @@ export const CrawlCustomSettingsFlyoutLogic = kea< isFormSubmitting: [ false, { - makeRequest: () => true, - apiSuccess: () => false, - apiError: () => false, + startCustomCrawl: () => true, + startCrawl: () => false, }, ], isFlyoutVisible: [ @@ -134,8 +133,7 @@ export const CrawlCustomSettingsFlyoutLogic = kea< { showFlyout: () => true, hideFlyout: () => false, - apiSuccess: () => false, - apiError: () => false, + startCrawl: () => false, }, ], maxCrawlDepth: [ @@ -253,7 +251,7 @@ export const CrawlCustomSettingsFlyoutLogic = kea< overrides.sitemap_urls = sitemapUrls; } - CrawlerLogic.actions.startCrawl(overrides); + actions.startCrawl(overrides); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx index 836490a925218..b67e866151ff0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx @@ -9,53 +9,40 @@ import React, { useEffect, ChangeEvent } from 'react'; import { useActions, useValues } from 'kea'; -import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; - import { EuiFieldSearch, - EuiTitle, - EuiSpacer, - EuiPanel, EuiFlexGroup, EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Result } from '../../../shared/result/result'; +import { DocumentList } from './components/document_list/document_list'; +import { DocumentsLogic, DEFAULT_PAGINATION } from './documents_logic'; -import { DocumentsLogic } from './documents_logic'; import { IndexNameLogic } from './index_name_logic'; import './documents.scss'; export const SearchIndexDocuments: React.FC = () => { const { indexName } = useValues(IndexNameLogic); - const { simplifiedMapping, results } = useValues(DocumentsLogic); + const { simplifiedMapping } = useValues(DocumentsLogic); const { makeRequest, makeMappingRequest, setSearchQuery } = useActions(DocumentsLogic); useEffect(() => { - makeRequest({ indexName, query: '' }); + makeRequest({ + indexName, + pagination: DEFAULT_PAGINATION, + query: '', + }); makeMappingRequest({ indexName }); }, [indexName]); - const resultToField = (result: SearchHit) => { - if (simplifiedMapping && result._source && !Array.isArray(result._source)) { - if (typeof result._source === 'object') { - return Object.entries(result._source).map(([key, value]) => { - return { - fieldName: key, - fieldType: simplifiedMapping[key]?.type ?? 'object', - fieldValue: JSON.stringify(value, null, 2), - }; - }); - } - } - return []; - }; - return ( - + @@ -91,21 +78,7 @@ export const SearchIndexDocuments: React.FC = () => { i18n.translate('xpack.enterpriseSearch.content.searchIndex.documents.noMappings', { defaultMessage: 'No mappings found for index', })} - - {simplifiedMapping && - results.map((result) => { - return ( - <> - - - - ); - })} + {simplifiedMapping && } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.test.ts index dafc0294e8c3e..ad80a0655b33b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.test.ts @@ -10,19 +10,24 @@ import { LogicMounter, mockFlashMessageHelpers } from '../../../__mocks__/kea_lo import { nextTick } from '@kbn/test-jest-helpers'; import { HttpError, Status } from '../../../../../common/types/api'; - import { MappingsApiLogic } from '../../api/mappings/mappings_logic'; import { SearchDocumentsApiLogic } from '../../api/search_documents/search_documents_logic'; -import { DocumentsLogic } from './documents_logic'; +import { + DocumentsLogic, + INDEX_DOCUMENTS_META_DEFAULT, + convertMetaToPagination, +} from './documents_logic'; import { IndexNameLogic } from './index_name_logic'; -const DEFAULT_VALUES = { +export const DEFAULT_VALUES = { data: undefined, + docsPerPage: 25, indexName: 'indexName', isLoading: true, mappingData: undefined, mappingStatus: 0, + meta: INDEX_DOCUMENTS_META_DEFAULT, query: '', results: [], status: Status.IDLE, @@ -57,6 +62,25 @@ describe('DocumentsLogic', () => { expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES, query: newQueryString }); }); }); + describe('setDocsPerPage', () => { + it('sets documents to show per page', () => { + const docsToShow = 50; + expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES }); + DocumentsLogic.actions.setDocsPerPage(docsToShow); + expect(DocumentsLogic.values).toEqual({ + ...DEFAULT_VALUES, + docsPerPage: docsToShow, + meta: { + page: { + ...INDEX_DOCUMENTS_META_DEFAULT.page, + size: docsToShow, + }, + }, + simplifiedMapping: undefined, + status: Status.LOADING, + }); + }); + }); }); describe('listeners', () => { describe('setSearchQuery', () => { @@ -69,7 +93,9 @@ describe('DocumentsLogic', () => { jest.advanceTimersByTime(250); await nextTick(); expect(DocumentsLogic.actions.makeRequest).toHaveBeenCalledWith({ + docsPerPage: 25, indexName: 'indexName', + pagination: convertMetaToPagination(INDEX_DOCUMENTS_META_DEFAULT), query: 'test', }); jest.useRealTimers(); @@ -84,7 +110,11 @@ describe('DocumentsLogic', () => { expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledTimes(1); }); it('clears flash messages on new makeRequest', () => { - DocumentsLogic.actions.makeRequest({ indexName: 'index', query: '' }); + DocumentsLogic.actions.makeRequest({ + indexName: 'index', + pagination: convertMetaToPagination(INDEX_DOCUMENTS_META_DEFAULT), + query: '', + }); expect(mockFlashMessageHelpers.clearFlashMessages).toHaveBeenCalledTimes(1); }); }); @@ -99,11 +129,17 @@ describe('DocumentsLogic', () => { }; expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES }); MappingsApiLogic.actions.apiSuccess({ mappings: {} }); - SearchDocumentsApiLogic.actions.apiSuccess(mockSuccessData); + SearchDocumentsApiLogic.actions.apiSuccess({ + meta: INDEX_DOCUMENTS_META_DEFAULT, + results: mockSuccessData, + }); expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES, - data: mockSuccessData, + data: { + meta: INDEX_DOCUMENTS_META_DEFAULT, + results: mockSuccessData, + }, isLoading: false, mappingData: { mappings: {}, @@ -135,17 +171,43 @@ describe('DocumentsLogic', () => { }; MappingsApiLogic.actions.apiSuccess({ mappings: {} }); - SearchDocumentsApiLogic.actions.apiSuccess(mockSuccessData); + SearchDocumentsApiLogic.actions.apiSuccess({ + meta: { + page: { + ...INDEX_DOCUMENTS_META_DEFAULT.page, + total_pages: 1, + total_results: 1, + }, + }, + results: mockSuccessData, + }); expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES, - data: mockSuccessData, + data: { + meta: { + page: { + ...INDEX_DOCUMENTS_META_DEFAULT.page, + total_pages: 1, + total_results: 1, + }, + }, + results: mockSuccessData, + }, isLoading: false, mappingData: { mappings: {}, }, mappingStatus: Status.SUCCESS, + meta: { + page: { + ...INDEX_DOCUMENTS_META_DEFAULT.page, + total_pages: 1, + total_results: 1, + }, + }, results: [{ _id: '123', _index: 'indexName', searchHit: true }], + simplifiedMapping: undefined, status: Status.SUCCESS, }); }); @@ -158,11 +220,14 @@ describe('DocumentsLogic', () => { }; MappingsApiLogic.actions.apiSuccess({ mappings: {} }); - SearchDocumentsApiLogic.actions.apiSuccess(mockSuccessData); + SearchDocumentsApiLogic.actions.apiSuccess({ + meta: INDEX_DOCUMENTS_META_DEFAULT, + results: mockSuccessData, + }); expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES, - data: mockSuccessData, + data: { meta: INDEX_DOCUMENTS_META_DEFAULT, results: mockSuccessData }, isLoading: false, mappingData: { mappings: {}, @@ -182,7 +247,7 @@ describe('DocumentsLogic', () => { expect(DocumentsLogic.values).toEqual({ ...DEFAULT_VALUES, - isLoading: false, + isLoading: true, mappingData: { mappings: { properties: { some: { type: 'text' } } }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.ts index 23983f2858d8d..aaa3c44541f2a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents_logic.ts @@ -14,44 +14,74 @@ import { SearchHit, } from '@elastic/elasticsearch/lib/api/types'; +import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } from '../../../../../common/constants'; +import { Meta } from '../../../../../common/types'; import { HttpError, Status } from '../../../../../common/types/api'; import { flashAPIErrors, clearFlashMessages } from '../../../shared/flash_messages'; +import { updateMetaPageIndex } from '../../../shared/table_pagination'; import { MappingsApiLogic } from '../../api/mappings/mappings_logic'; import { SearchDocumentsApiLogic } from '../../api/search_documents/search_documents_logic'; import { IndexNameLogic } from './index_name_logic'; +export const INDEX_DOCUMENTS_META_DEFAULT = { + page: { + current: 0, + size: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, + total_pages: 0, + total_results: 0, + }, +}; + +export const DEFAULT_PAGINATION = { + pageIndex: INDEX_DOCUMENTS_META_DEFAULT.page.current, + pageSize: INDEX_DOCUMENTS_META_DEFAULT.page.size, + totalItemCount: INDEX_DOCUMENTS_META_DEFAULT.page.total_results, +}; + interface DocumentsLogicActions { apiError(error: HttpError): HttpError; apiReset: typeof SearchDocumentsApiLogic.actions.apiReset; makeMappingRequest: typeof MappingsApiLogic.actions.makeRequest; makeRequest: typeof SearchDocumentsApiLogic.actions.makeRequest; mappingsApiError(error: HttpError): HttpError; + onPaginate(newPageIndex: number): { newPageIndex: number }; + setDocsPerPage(docsPerPage: number): { docsPerPage: number }; setSearchQuery(query: string): { query: string }; } -interface DocumentsLogicValues { +export interface DocumentsLogicValues { data: typeof SearchDocumentsApiLogic.values.data; + docsPerPage: number; indexName: typeof IndexNameLogic.values.indexName; isLoading: boolean; mappingData: IndicesGetMappingIndexMappingRecord; mappingStatus: Status; + meta: Meta; query: string; results: SearchHit[]; simplifiedMapping: Record | undefined; status: Status; } +export const convertMetaToPagination = (meta: Meta) => ({ + pageIndex: meta.page.current, + pageSize: meta.page.size, + totalItemCount: meta.page.total_results, +}); + export const DocumentsLogic = kea>({ actions: { + onPaginate: (newPageIndex) => ({ newPageIndex }), + setDocsPerPage: (docsPerPage) => ({ docsPerPage }), setSearchQuery: (query) => ({ query }), }, connect: { actions: [ SearchDocumentsApiLogic, - ['apiReset', 'makeRequest', 'apiError'], + ['apiReset', 'makeRequest', 'apiError', 'apiSuccess'], MappingsApiLogic, ['makeRequest as makeMappingRequest', 'apiError as mappingsApiError'], ], @@ -68,13 +98,50 @@ export const DocumentsLogic = kea flashAPIErrors(e), makeRequest: () => clearFlashMessages(), mappingsApiError: (e) => flashAPIErrors(e), + onPaginate: () => { + actions.makeRequest({ + docsPerPage: values.docsPerPage, + indexName: values.indexName, + pagination: convertMetaToPagination(values.meta), + query: values.query, + }); + }, + setDocsPerPage: () => { + actions.makeRequest({ + docsPerPage: values.docsPerPage, + indexName: values.indexName, + pagination: convertMetaToPagination(values.meta), + query: values.query, + }); + }, setSearchQuery: async (_, breakpoint) => { await breakpoint(250); - actions.makeRequest({ indexName: values.indexName, query: values.query }); + actions.makeRequest({ + docsPerPage: values.docsPerPage, + indexName: values.indexName, + pagination: convertMetaToPagination(values.meta), + query: values.query, + }); }, }), path: ['enterprise_search', 'search_index', 'documents'], reducers: () => ({ + docsPerPage: [ + ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, + { + setDocsPerPage: (_, { docsPerPage }) => docsPerPage, + }, + ], + meta: [ + INDEX_DOCUMENTS_META_DEFAULT, + { + apiSuccess: (_, { meta }) => meta, + onPaginate: (state, { newPageIndex }) => updateMetaPageIndex(state, newPageIndex), + setDocsPerPage: (_, { docsPerPage }) => ({ + page: { ...INDEX_DOCUMENTS_META_DEFAULT.page, size: docsPerPage }, + }), + }, + ], query: [ '', { @@ -85,14 +152,12 @@ export const DocumentsLogic = kea ({ isLoading: [ () => [selectors.status, selectors.mappingStatus], - (status, mappingStatus) => status !== Status.SUCCESS && mappingStatus !== Status.SUCCESS, + (status, mappingStatus) => status !== Status.SUCCESS || mappingStatus !== Status.SUCCESS, ], results: [ - () => [selectors.data, selectors.isLoading], - (data: SearchResponseBody, isLoading) => { - if (isLoading) return []; - - return data?.hits?.hits || []; + () => [selectors.data], + (data: { results: SearchResponseBody }) => { + return data?.results?.hits?.hits || []; }, ], simplifiedMapping: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/total_stats.tsx index dfc4ccae2385c..dd55dd399392c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/total_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/total_stats.tsx @@ -23,7 +23,7 @@ interface TotalStatsProps { export const TotalStats: React.FC = ({ ingestionType, additionalItems = [] }) => { const { indexData, isError, isLoading } = useValues(OverviewLogic); - const documentCount = indexData?.total.docs.count ?? 0; + const documentCount = indexData?.count ?? 0; const hideStats = isLoading || isError; const stats: EuiStatProps[] = [ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx index 6189ffdc879e0..2627f89ab3b88 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx @@ -17,7 +17,6 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedRelative } from '@kbn/i18n-react'; import { Meta } from '../../../../../common/types'; import { EuiLinkTo, EuiButtonIconTo } from '../../../shared/react_router_helpers'; @@ -55,6 +54,7 @@ const columns: Array> = [ ), sortable: true, truncateText: true, + width: '40%', }, { field: 'health', @@ -69,14 +69,16 @@ const columns: Array> = [ ), sortable: true, truncateText: true, + width: '10%', }, { - field: 'total.docs.count', + field: 'count', name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.docsCount.columnTitle', { defaultMessage: 'Docs count', }), sortable: true, truncateText: true, + width: '10%', }, { field: 'ingestionMethod', @@ -90,34 +92,7 @@ const columns: Array> = [ {ingestionMethodToText(ingestionMethod)} ), truncateText: true, - }, - { - field: 'lastUpdated', - name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.lastUpdated.columnTitle', { - defaultMessage: 'Last updated', - }), - render: (dateString: string) => { - if (dateString === 'never') { - return ( - - {i18n.translate('xpack.enterpriseSearch.content.searchIndices.lastUpdated.never', { - defaultMessage: 'Never', - })} - - ); - } - return dateString ? ( - - ) : ( - - {i18n.translate('xpack.enterpriseSearch.content.searchIndices.lastUpdated.none', { - defaultMessage: 'Unknown', - })} - - ); - }, - sortable: true, - truncateText: true, + width: '10%', }, { name: i18n.translate( @@ -150,6 +125,7 @@ const columns: Array> = [ } }, truncateText: true, + width: '10%', }, { actions: [ @@ -169,6 +145,7 @@ const columns: Array> = [ name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.actions.columnTitle', { defaultMessage: 'Actions', }), + width: '5%', }, ]; @@ -190,7 +167,7 @@ export const IndicesTable: React.FC = ({ columns={columns} onChange={onChange} pagination={{ ...convertMetaToPagination(meta), showPerPageOptions: false }} - tableLayout="auto" + tableLayout="fixed" loading={isLoading} /> ); diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index f8c73a4ac27b5..659d9695703b7 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -363,7 +363,7 @@ export const registerEnterpriseSearchIntegrations = ( defaultMessage: 'API', }), description: i18n.translate('xpack.enterpriseSearch.integrations.apiDescription', { - defaultMessage: "Add search to your application with App Search's robust APIs.", + defaultMessage: "Add search to your application with Elasticsearch's robust APIs.", }), categories: ['enterprise_search', 'custom', 'elastic_stack'], uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=api', diff --git a/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.test.ts b/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.test.ts index c60d6bf185190..9c3bb302ca45e 100644 --- a/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.test.ts @@ -8,8 +8,11 @@ import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; import { IScopedClusterClient } from '@kbn/core/server'; +import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } from '../../common/constants'; + import { fetchSearchResults } from './fetch_search_results'; +const DEFAULT_FROM_VALUE = 0; describe('fetchSearchResults lib function', () => { const mockClient = { asCurrentUser: { @@ -82,8 +85,26 @@ describe('fetchSearchResults lib function', () => { ).resolves.toEqual(regularSearchResultsResponse); expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: DEFAULT_FROM_VALUE, index: indexName, q: query, + size: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, + }); + }); + + it('should return search results with hits when no query is passed', async () => { + mockClient.asCurrentUser.search.mockImplementation( + () => regularSearchResultsResponse as SearchResponseBody + ); + + await expect( + fetchSearchResults(mockClient as unknown as IScopedClusterClient, indexName) + ).resolves.toEqual(regularSearchResultsResponse); + + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: DEFAULT_FROM_VALUE, + index: indexName, + size: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, }); }); @@ -97,8 +118,10 @@ describe('fetchSearchResults lib function', () => { ).resolves.toEqual(emptySearchResultsResponse); expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: DEFAULT_FROM_VALUE, index: indexName, q: query, + size: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.ts b/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.ts index 6e1c8362c2dc3..d44ef938af98d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.ts +++ b/x-pack/plugins/enterprise_search/server/lib/fetch_search_results.ts @@ -8,13 +8,19 @@ import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; import { IScopedClusterClient } from '@kbn/core/server'; +import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } from '../../common/constants'; + export const fetchSearchResults = async ( client: IScopedClusterClient, indexName: string, - query?: string + query?: string, + from: number = 0, + size: number = ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT ): Promise => { const results = await client.asCurrentUser.search({ + from, index: indexName, + size, ...(!!query ? { q: query } : {}), }); return results; diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts index 1de3b0372a47a..9bf9f2b2d2f1d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts @@ -24,6 +24,7 @@ jest.mock('../crawler/fetch_crawlers', () => ({ describe('fetchIndex lib function', () => { const mockClient = { asCurrentUser: { + count: jest.fn().mockReturnValue({ count: 100 }), index: jest.fn(), indices: { get: jest.fn(), @@ -60,6 +61,7 @@ describe('fetchIndex lib function', () => { const result = { aliases: [], + count: 100, health: 'green', name: 'index_name', status: 'open', diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts index b4845cb72391a..1505d4501ac12 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts @@ -20,11 +20,17 @@ export const fetchIndex = async ( const indexDataResult = await client.asCurrentUser.indices.get({ index }); const indexData = indexDataResult[index]; const { indices } = await client.asCurrentUser.indices.stats({ index }); + + const { count } = await client.asCurrentUser.count({ index }); + if (!indices || !indices[index] || !indexData) { throw new Error('404'); } const indexStats = indices[index]; - const indexResult = mapIndexStats(indexData, indexStats, index); + const indexResult = { + count, + ...mapIndexStats(indexData, indexStats, index), + }; const connector = await fetchConnectorByIndexName(client, index); if (connector) { diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.test.ts index ce4666c589a15..d7f7b6185eaa6 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.test.ts @@ -20,6 +20,7 @@ describe('fetchIndices lib function', () => { security: { hasPrivileges: jest.fn(), }, + count: jest.fn().mockReturnValue({ count: 100 }), }, asInternalUser: {}, }; @@ -75,6 +76,7 @@ describe('fetchIndices lib function', () => { fetchIndices(mockClient as unknown as IScopedClusterClient, 'search-*', false, true) ).resolves.toEqual([ { + count: 100, health: 'green', name: 'search-regular-index', status: 'open', @@ -123,6 +125,7 @@ describe('fetchIndices lib function', () => { fetchIndices(mockClient as unknown as IScopedClusterClient, 'search-*', true, true) ).resolves.toEqual([ { + count: 100, health: 'green', name: 'search-regular-index', status: 'open', @@ -183,6 +186,7 @@ describe('fetchIndices lib function', () => { fetchIndices(mockClient as unknown as IScopedClusterClient, 'search-*', false, true) ).resolves.toEqual([ { + count: 100, health: 'green', name: 'index-without-prefix', status: 'open', @@ -200,6 +204,7 @@ describe('fetchIndices lib function', () => { uuid: '83a81e7e-5955-4255-b008-5d6961203f57', }, { + count: 100, health: 'green', name: 'search-aliased', status: 'open', @@ -217,6 +222,7 @@ describe('fetchIndices lib function', () => { uuid: '83a81e7e-5955-4255-b008-5d6961203f57', }, { + count: 100, health: 'green', name: 'search-double-aliased', status: 'open', @@ -234,6 +240,7 @@ describe('fetchIndices lib function', () => { uuid: '83a81e7e-5955-4255-b008-5d6961203f57', }, { + count: 100, health: 'green', name: 'second-index', status: 'open', @@ -282,6 +289,7 @@ describe('fetchIndices lib function', () => { fetchIndices(mockClient as unknown as IScopedClusterClient, 'search-*', false, false) ).resolves.toEqual([ { + count: 100, health: 'green', name: 'index-without-prefix', status: 'open', @@ -299,6 +307,7 @@ describe('fetchIndices lib function', () => { uuid: '83a81e7e-5955-4255-b008-5d6961203f57', }, { + count: 100, health: 'green', name: 'second-index', status: 'open', @@ -332,6 +341,7 @@ describe('fetchIndices lib function', () => { fetchIndices(mockClient as unknown as IScopedClusterClient, 'search-*', false, true) ).resolves.toEqual([ { + count: 100, health: undefined, name: 'search-regular-index', status: undefined, diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.ts index 69df076a7ccbf..aa2ac7853dc10 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_indices.ts @@ -34,6 +34,7 @@ export const mapIndexStats = ( size_in_bytes: sizeInBytes, }, }; + return { aliases, health: indexStats?.health, @@ -44,6 +45,16 @@ export const mapIndexStats = ( }; }; +export const fetchIndexCounts = async (client: IScopedClusterClient, indicesNames: string[]) => { + // TODO: is there way to batch this? Passing multiple index names or a pattern still returns a singular count + const countPromises = indicesNames.map(async (indexName) => { + const { count } = await client.asCurrentUser.count({ index: indexName }); + return { [indexName]: count }; + }); + const indexCountArray = await Promise.all(countPromises); + return indexCountArray.reduce((acc, current) => ({ ...acc, ...current }), {}); +}; + export const fetchIndices = async ( client: IScopedClusterClient, indexPattern: string, @@ -97,6 +108,8 @@ export const fetchIndices = async ( ], }); + const indexCounts = await fetchIndexCounts(client, indexAndAliasNames); + return indicesNames .map((indexName: string) => { const indexData = totalIndices[indexName]; @@ -108,6 +121,7 @@ export const fetchIndices = async ( const indicesAndAliases = [] as ElasticsearchIndexWithPrivileges[]; indicesAndAliases.push({ name, + count: indexCounts[name] ?? 0, alias: false, privileges: { read: false, manage: false, ...indexPrivileges[name] }, ...indexData, @@ -117,6 +131,7 @@ export const fetchIndices = async ( aliases.forEach((alias) => { indicesAndAliases.push({ name: alias, + count: indexCounts[alias] ?? 0, alias: true, privileges: { read: false, manage: false, ...indexPrivileges[name] }, ...indexData, diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts index beff8408b9486..ac6d8b70fde07 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts @@ -16,7 +16,7 @@ import { fetchSearchResults } from '../../lib/fetch_search_results'; import { registerSearchRoute } from './search'; -describe('Elasticsearch Index Mapping', () => { +describe('Elasticsearch Search', () => { let mockRouter: MockRouter; const mockClient = {}; @@ -76,10 +76,104 @@ describe('Elasticsearch Index Mapping', () => { params: { index_name: 'search-index-name', query: 'banana' }, }); - expect(fetchSearchResults).toHaveBeenCalledWith(mockClient, 'search-index-name', 'banana'); + expect(fetchSearchResults).toHaveBeenCalledWith( + mockClient, + 'search-index-name', + 'banana', + 0, + 25 + ); expect(mockRouter.response.ok).toHaveBeenCalledWith({ - body: mockData, + body: { + meta: { + page: { + current: 0, + size: 1, + total_pages: 1, + total_results: 1, + }, + }, + results: mockData, + }, + + headers: { 'content-type': 'application/json' }, + }); + }); + }); + + describe('GET /internal/enterprise_search/indices/{index_name}/search', () => { + let mockRouterNoQuery: MockRouter; + beforeEach(() => { + const context = { + core: Promise.resolve({ elasticsearch: { client: mockClient } }), + } as jest.Mocked; + + mockRouterNoQuery = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/indices/{index_name}/search', + }); + + registerSearchRoute({ + ...mockDependencies, + router: mockRouterNoQuery.router, + }); + }); + it('fails validation without index_name', () => { + const request = { params: {} }; + mockRouterNoQuery.shouldThrow(request); + }); + + it('searches returns first 25 search results by default', async () => { + const mockData = { + _shards: { failed: 0, skipped: 0, successful: 2, total: 2 }, + hits: { + hits: [ + { + _id: '5a12292a0f5ae10021650d7e', + _index: 'search-regular-index', + _score: 4.437291, + _source: { id: '5a12292a0f5ae10021650d7e', name: 'banana' }, + }, + ], + + max_score: null, + total: { relation: 'eq', value: 1 }, + }, + timed_out: false, + took: 4, + }; + + (fetchSearchResults as jest.Mock).mockImplementationOnce(() => { + return Promise.resolve(mockData); + }); + + await mockRouterNoQuery.callRoute({ + params: { index_name: 'search-index-name' }, + }); + + expect(fetchSearchResults).toHaveBeenCalledWith( + mockClient, + 'search-index-name', + 'banana', + 0, + 25 + ); + + expect(mockRouterNoQuery.response.ok).toHaveBeenCalledWith({ + body: { + meta: { + page: { + current: 0, + size: 1, + total_pages: 1, + total_results: 1, + }, + }, + results: mockData, + }, + headers: { 'content-type': 'application/json' }, }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts index af9c578d07494..68391bf8b4f4b 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts @@ -5,11 +5,36 @@ * 2.0. */ +import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; + import { schema } from '@kbn/config-schema'; +import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } from '../../../common/constants'; + import { fetchSearchResults } from '../../lib/fetch_search_results'; import { RouteDependencies } from '../../plugin'; +const calculateMeta = (searchResults: SearchResponseBody, page: number, size: number) => { + let totalResults = 0; + if (searchResults.hits.total === null || searchResults.hits.total === undefined) { + totalResults = 0; + } else if (typeof searchResults.hits.total === 'number') { + totalResults = searchResults.hits.total; + } else { + totalResults = searchResults.hits.total.value; + } + const totalPages = Math.ceil(totalResults / size) || 1; + + return { + page: { + current: page, + size: searchResults.hits.hits.length, + total_pages: (Number.isFinite(totalPages) && totalPages) || 1, + total_results: totalResults, + }, + }; +}; + export function registerSearchRoute({ router }: RouteDependencies) { router.get( { @@ -18,14 +43,33 @@ export function registerSearchRoute({ router }: RouteDependencies) { params: schema.object({ index_name: schema.string(), }), + query: schema.object({ + page: schema.number({ defaultValue: 0, min: 0 }), + size: schema.number({ + defaultValue: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, + min: 0, + }), + }), }, }, async (context, request, response) => { const { client } = (await context.core).elasticsearch; + const { page = 0, size = ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } = request.query; + const from = page * size; try { - const searchResults = await fetchSearchResults(client, request.params.index_name, ''); + const searchResults: SearchResponseBody = await fetchSearchResults( + client, + request.params.index_name, + '', + from, + size + ); + return response.ok({ - body: searchResults, + body: { + meta: calculateMeta(searchResults, page, size), + results: searchResults, + }, headers: { 'content-type': 'application/json' }, }); } catch (error) { @@ -44,18 +88,33 @@ export function registerSearchRoute({ router }: RouteDependencies) { index_name: schema.string(), query: schema.string(), }), + query: schema.object({ + page: schema.number({ defaultValue: 0, min: 0 }), + size: schema.number({ + defaultValue: ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, + min: 0, + }), + }), }, }, async (context, request, response) => { const { client } = (await context.core).elasticsearch; + const { page = 0, size = ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } = request.query; + const from = page * size; try { const searchResults = await fetchSearchResults( client, request.params.index_name, - request.params.query + request.params.query, + from, + size ); + return response.ok({ - body: searchResults, + body: { + meta: calculateMeta(searchResults, page, size), + results: searchResults, + }, headers: { 'content-type': 'application/json' }, }); } catch (error) { diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/bulk_upgrade_agents.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/bulk_upgrade_agents.yaml index 0fcd93da6f61a..5e4378b9631f7 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/bulk_upgrade_agents.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/bulk_upgrade_agents.yaml @@ -21,6 +21,9 @@ properties: items: type: string description: list of agent IDs + force: + type: boolean + description: Force upgrade, skipping validation (should be used with caution) required: - agents - version diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/upgrade_agent.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/upgrade_agent.yaml index 19c796f8f8404..4d7da58a4ae4c 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/upgrade_agent.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/upgrade_agent.yaml @@ -1,16 +1,12 @@ title: Upgrade agent -oneOf: - - type: object - properties: - version: - type: string - required: - - version - - type: object - properties: - version: - type: string - source_uri: - type: string - required: - - version +type: object +properties: + version: + type: string + source_uri: + type: string + force: + type: boolean + description: Force upgrade, skipping validation (should be used with caution) +required: + - version diff --git a/x-pack/plugins/fleet/cypress/integration/agent.spec.ts b/x-pack/plugins/fleet/cypress/integration/agent.spec.ts index 6dc7ba2c88104..084ba548ff6c6 100644 --- a/x-pack/plugins/fleet/cypress/integration/agent.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/agent.spec.ts @@ -14,7 +14,9 @@ const createAgentDocs = (kibanaVersion: string) => [ ]; let docs: any[] = []; -describe('View agents', () => { +// TODO: create fleet server, fix version of agent to upgrade to an allowed version (>= fleet server's, < kibana) +// https://github.com/elastic/kibana/issues/138121 +describe.skip('View agents', () => { before(() => { cy.task('deleteDocsByQuery', { index: '.fleet-agents', diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx index 02cc458295677..e13d9b5394dc9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiSteps } from '@elastic/eui'; import React from 'react'; +import { EuiLoadingContent, EuiSteps } from '@elastic/eui'; import { useAdvancedForm } from './hooks'; @@ -24,6 +24,7 @@ interface AdvancedTabProps { export const AdvancedTab: React.FunctionComponent = ({ selectedPolicyId }) => { const { + isSelectFleetServerPolicyLoading, eligibleFleetServerPolicies, refreshEligibleFleetServerPolicies, fleetServerPolicyId, @@ -70,5 +71,9 @@ export const AdvancedTab: React.FunctionComponent = ({ selecte getConfirmFleetServerConnectionStep({ isFleetServerReady, disabled: !Boolean(serviceToken) }), ]; - return ; + return isSelectFleetServerPolicyLoading ? ( + + ) : ( + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts index 5ed9f9faa0372..122f7045ac84c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts @@ -19,6 +19,7 @@ import { useWaitForFleetServer } from './use_wait_for_fleet_server'; */ export const useAdvancedForm = (defaultAgentPolicyId?: string) => { const { + isSelectFleetServerPolicyLoading, eligibleFleetServerPolicies, refreshEligibleFleetServerPolicies, fleetServerPolicyId, @@ -31,6 +32,7 @@ export const useAdvancedForm = (defaultAgentPolicyId?: string) => { const [deploymentMode, setDeploymentMode] = useState('quickstart'); return { + isSelectFleetServerPolicyLoading, eligibleFleetServerPolicies, refreshEligibleFleetServerPolicies, fleetServerPolicyId, diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts index b15fbc0c396a7..c22559d07509f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts @@ -14,7 +14,12 @@ export const useSelectFleetServerPolicy = (defaultAgentPolicyId?: string) => { const [fleetServerPolicyId, setFleetServerPolicyId] = useState( defaultAgentPolicyId ); - const { data: agentPoliciesData, resendRequest } = useGetAgentPolicies({ + const { + isLoading, + isInitialRequest, + data: agentPoliciesData, + resendRequest, + } = useGetAgentPolicies({ full: true, }); @@ -34,6 +39,7 @@ export const useSelectFleetServerPolicy = (defaultAgentPolicyId?: string) => { }, [eligibleFleetServerPolicies, fleetServerPolicyId]); return { + isSelectFleetServerPolicyLoading: isLoading && isInitialRequest, fleetServerPolicyId, setFleetServerPolicyId, eligibleFleetServerPolicies, diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx index 70753e37f8e8a..62b11e3295ecf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -106,7 +106,6 @@ export const AddFleetServerHostStepContent = ({ } buttonColor="danger" + data-test-subj="confirmForceInstallModal" > { let esServer: kbnTestServer.TestElasticsearchUtils; let kbnServer: kbnTestServer.TestKibanaUtils; @@ -183,6 +185,10 @@ describe('Uprade package install version', () => { res.saved_objects.forEach((so) => { expect(so.attributes.install_format_schema_version).toBe(FLEET_INSTALL_FORMAT_VERSION); + if (!PACKAGES.includes(so.attributes.name)) { + return; + } + if (!OUTDATED_PACKAGES.includes(so.attributes.name)) { expect(new Date(so.updated_at as string).getTime()).toBeLessThan(now); } else { diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.test.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.test.ts index 8be5f4655b480..ad1787c97aa19 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.test.ts @@ -624,4 +624,50 @@ describe('processFields', () => { ]; expect(processFields(fields)).toEqual(fieldsExpected); }); + + test('handle wildcard field', () => { + const wildcardFields = [ + { + name: 'a.*.b', + type: 'keyword', + }, + { + name: 'a.b.*', + type: 'scaled_float', + }, + ]; + + expect(processFields(wildcardFields)).toMatchInlineSnapshot(` + [ + { + "name": "a", + "type": "group", + "fields": [ + { + "name": "*", + "type": "group", + "fields": [ + { + "name": "b", + "type": "object", + "object_type": "keyword" + } + ] + }, + { + "name": "b", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "object_type": "scaled_float" + } + ] + } + ] + } + ] + `); + }); }); diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index 8d784e0cffc22..1759946f4f524 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -246,8 +246,23 @@ export const getField = (fields: Fields, pathNames: string[]): Field | undefined return undefined; }; +export function processFieldsWithWildcard(fields: Fields): Fields { + const newFields: Fields = []; + for (const field of fields) { + const hasWildcard = field.name.includes('*'); + const hasObjectType = field.object_type; + if (hasWildcard && !hasObjectType) { + newFields.push({ ...field, type: 'object', object_type: field.type }); + } else { + newFields.push({ ...field }); + } + } + return newFields; +} + export function processFields(fields: Fields): Fields { - const expandedFields = expandFields(fields); + const processedFields = processFieldsWithWildcard(fields); + const expandedFields = expandFields(processedFields); const dedupedFields = dedupFields(expandedFields); return validateFields(dedupedFields, dedupedFields); } diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index ff7d618527cac..4b1dde82b3b41 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -157,8 +157,8 @@ export async function ensurePreconfiguredPackagesAndPolicies( policy ); - const newFields = { - defaultDownloadSourceId: defaultDownloadSource.id, + const newFields: Partial = { + download_source_id: defaultDownloadSource.id, ...fields, }; if (hasChanged) { diff --git a/x-pack/plugins/fleet/server/telemetry/sender.ts b/x-pack/plugins/fleet/server/telemetry/sender.ts index 90013ff98324a..7684284165ee1 100644 --- a/x-pack/plugins/fleet/server/telemetry/sender.ts +++ b/x-pack/plugins/fleet/server/telemetry/sender.ts @@ -105,12 +105,15 @@ export class TelemetryEventsSender { this.isSending = false; } - private async fetchClusterInfo(): Promise { - if (this.esClient === undefined || this.esClient === null) { - throw Error('elasticsearch client is unavailable: cannot retrieve cluster infomation'); + private async fetchClusterInfo(): Promise { + try { + if (this.esClient === undefined || this.esClient === null) { + throw Error('elasticsearch client is unavailable: cannot retrieve cluster infomation'); + } + return await this.esClient.info(); + } catch (e) { + this.logger.debug(`Error fetching cluster information: ${e}`); } - - return await this.esClient.info(); } public async sendEvents( diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json index d9d432fc702e4..2226c89ab90f6 100644 --- a/x-pack/plugins/infra/kibana.json +++ b/x-pack/plugins/infra/kibana.json @@ -14,7 +14,8 @@ "alerting", "triggersActionsUi", "observability", - "ruleRegistry" + "ruleRegistry", + "unifiedSearch" ], "optionalPlugins": ["ml", "home", "embeddable", "osquery"], "server": true, diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx index d54b12511a05c..e264da646949c 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx @@ -76,7 +76,7 @@ export class AutocompleteField extends React.Component< data-test-subj="infraSearchField" /> {areSuggestionsVisible && !isLoadingSuggestions && suggestions.length > 0 ? ( - + {suggestions.map((suggestion, suggestionIndex) => ( ; + kibana: KibanaReactContextValue; children: RendererFunction<{ isLoadingSuggestions: boolean; loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void; @@ -66,7 +65,7 @@ class WithKueryAutocompletionComponent extends React.Component< const { indexPattern } = this.props; const language = 'kuery'; const hasQuerySuggestions = - this.props.kibana.services.unifiedSearch?.autocomplete.hasQuerySuggestions(language); + this.props.kibana.services.unifiedSearch.autocomplete.hasQuerySuggestions(language); if (!hasQuerySuggestions) { return; diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 10b2aa25873d6..fcd806eb34ff9 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -6,6 +6,7 @@ */ import type { CoreSetup, CoreStart, Plugin as PluginClass } from '@kbn/core/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; @@ -62,6 +63,7 @@ export interface InfraClientSetupDeps { export interface InfraClientStartDeps { data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; dataViews: DataViewsPublicPluginStart; observability: ObservabilityPublicStart; spaces: SpacesPluginStart; diff --git a/x-pack/plugins/kubernetes_security/common/constants.ts b/x-pack/plugins/kubernetes_security/common/constants.ts index 002e669c6839f..04c273290eca7 100644 --- a/x-pack/plugins/kubernetes_security/common/constants.ts +++ b/x-pack/plugins/kubernetes_security/common/constants.ts @@ -11,6 +11,7 @@ export const LOCAL_STORAGE_HIDE_WIDGETS_KEY = 'kubernetesSecurity:shouldHideWidg export const AGGREGATE_ROUTE = '/internal/kubernetes_security/aggregate'; export const COUNT_ROUTE = '/internal/kubernetes_security/count'; +export const MULTI_TERMS_AGGREGATE_ROUTE = '/internal/kubernetes_security/multi_terms_aggregate'; export const AGGREGATE_PAGE_SIZE = 10; // so, bucket sort can only page through what we request at the top level agg, which means there is a ceiling to how many aggs we can page through. @@ -28,11 +29,12 @@ export const ENTRY_LEADER_INTERACTIVE = 'process.entry_leader.interactive'; export const ENTRY_LEADER_USER_ID = 'process.entry_leader.user.id'; export const ENTRY_LEADER_ENTITY_ID = 'process.entry_leader.entity_id'; -export const ORCHESTRATOR_CLUSTER_ID = 'orchestrator.cluster.name'; +export const ORCHESTRATOR_CLUSTER_ID = 'orchestrator.cluster.id'; +export const ORCHESTRATOR_CLUSTER_NAME = 'orchestrator.cluster.name'; export const ORCHESTRATOR_NAMESPACE = 'orchestrator.namespace'; +export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; export const ORCHESTRATOR_RESOURCE_ID = 'orchestrator.resource.name'; export const CONTAINER_IMAGE_NAME = 'container.image.name'; -export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; export const COUNT_WIDGET_KEY_CLUSTERS = 'CountClustersWidget'; export const COUNT_WIDGET_KEY_NAMESPACE = 'CountNamespaceWidgets'; diff --git a/x-pack/plugins/kubernetes_security/common/types/aggregate/index.ts b/x-pack/plugins/kubernetes_security/common/types/aggregate/index.ts index 57a1df44292e9..a00a3996eaa8d 100644 --- a/x-pack/plugins/kubernetes_security/common/types/aggregate/index.ts +++ b/x-pack/plugins/kubernetes_security/common/types/aggregate/index.ts @@ -10,18 +10,19 @@ interface Aggregate { doc_count: number; } -interface Buckets extends Aggregate { +interface Bucket extends Aggregate { key_as_string?: string; - count_by_aggs: { + count_by_aggs?: { value: number; }; } + export interface AggregateResult { - buckets: Buckets[]; + buckets: Bucket[]; hasNextPage: boolean; } export interface AggregateBucketPaginationResult { - buckets: Aggregate[]; + buckets: Bucket[]; hasNextPage: boolean; } diff --git a/x-pack/plugins/kubernetes_security/common/types/multi_terms_aggregate/index.ts b/x-pack/plugins/kubernetes_security/common/types/multi_terms_aggregate/index.ts new file mode 100644 index 0000000000000..0da95a0c2e74c --- /dev/null +++ b/x-pack/plugins/kubernetes_security/common/types/multi_terms_aggregate/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface MultiTermsAggregateGroupBy { + field: string; + maybe?: string; +} + +interface MultiTermsAggregate { + key: Array; + doc_count: number; +} + +export interface Bucket extends MultiTermsAggregate { + key_as_string?: string; + count_by_aggs?: { + value: number; + }; +} + +export interface MultiTermsAggregateResult { + buckets: Bucket[]; + hasNextPage: boolean; +} + +export interface MultiTermsAggregateBucketPaginationResult { + buckets: Bucket[]; + hasNextPage: boolean; +} diff --git a/x-pack/plugins/kubernetes_security/public/components/container_name_widget/index.tsx b/x-pack/plugins/kubernetes_security/public/components/container_name_widget/index.tsx index 7617072207524..6c43088b662e2 100644 --- a/x-pack/plugins/kubernetes_security/public/components/container_name_widget/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/container_name_widget/index.tsx @@ -171,7 +171,7 @@ export const ContainerNameWidget = ({ return aggsData?.buckets.map((aggData) => { return { name: aggData.key as string, - count: addCommasToNumber(aggData.count_by_aggs.value), + count: addCommasToNumber(aggData.count_by_aggs?.value ?? 0), }; }); }) diff --git a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx index e5e7798d7bfe3..85fe011bef998 100644 --- a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx @@ -71,7 +71,7 @@ const KubernetesSecurityRoutesComponent = ({ (result: AggregateResult): Record => result.buckets.reduce((groupedByKeyValue, aggregate) => { groupedByKeyValue[aggregate.key_as_string || (aggregate.key.toString() as string)] = - aggregate.count_by_aggs.value; + aggregate.count_by_aggs?.value ?? 0; return groupedByKeyValue; }, {} as Record), [] @@ -81,10 +81,10 @@ const KubernetesSecurityRoutesComponent = ({ (result: AggregateResult): Record => result.buckets.reduce((groupedByKeyValue, aggregate) => { if (aggregate.key.toString() === '0') { - groupedByKeyValue[aggregate.key] = aggregate.count_by_aggs.value; + groupedByKeyValue[aggregate.key] = aggregate.count_by_aggs?.value ?? 0; } else { groupedByKeyValue.nonRoot = - (groupedByKeyValue.nonRoot || 0) + aggregate.count_by_aggs.value; + (groupedByKeyValue.nonRoot || 0) + (aggregate.count_by_aggs?.value ?? 0); } return groupedByKeyValue; }, {} as Record), diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/__snapshots__/index.test.tsx.snap b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/__snapshots__/index.test.tsx.snap index 97a7fdd38f611..3e24ec861a76f 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/__snapshots__/index.test.tsx.snap @@ -9,89 +9,105 @@ Object { css="[object Object]" > - + + - + + - + + - + +
, @@ -100,89 +116,105 @@ Object { css="[object Object]" > - + + - + + - + + - + + , "debug": [Function], @@ -248,9 +280,93 @@ Object { css="[object Object]" > + + + + + + + + + + + + + + + + , + "container":
+
+ + - - + + + + - - + + + + + +
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Tree view Breadcrumb component When Breadcrumb is mounted returns cluster id when no cluster name is provided 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+ + + + + + + + + + + + + + + + + + +
, @@ -317,67 +606,105 @@ Object { css="[object Object]" > - + + - + + + + + + + - + + , "debug": [Function], diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.test.tsx index 1c127294ea7bb..82eedbaf10650 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.test.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.test.tsx @@ -11,7 +11,8 @@ import { KubernetesCollection, TreeNavSelection } from '../../../types'; import { Breadcrumb } from '.'; const MOCK_TREE_SELECTION: TreeNavSelection = { - [KubernetesCollection.cluster]: 'selected cluster', + [KubernetesCollection.clusterId]: 'selected cluster id', + [KubernetesCollection.clusterName]: 'selected cluster name', [KubernetesCollection.namespace]: 'selected namespace', [KubernetesCollection.node]: 'selected node', [KubernetesCollection.pod]: 'selected pod', @@ -39,7 +40,7 @@ describe('Tree view Breadcrumb component', () => { ); expect( - renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.cluster]!) + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.clusterName]!) ).toBeVisible(); expect( renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.namespace]!) @@ -60,10 +61,38 @@ describe('Tree view Breadcrumb component', () => { expect(renderResult.container).toBeEmptyDOMElement(); }); + it('returns cluster id when no cluster name is provided', async () => { + renderResult = mockedContext.render( + + ); + + expect( + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.clusterId]!) + ).toBeVisible(); + expect( + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.namespace]!) + ).toBeVisible(); + expect(renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.node]!)).toBeFalsy(); + expect( + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.pod]!) + ).toBeVisible(); + expect( + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.containerImage]!) + ).toBeVisible(); + expect(renderResult).toMatchSnapshot(); + }); + it('returns null when no cluster in selection', async () => { renderResult = mockedContext.render( ); @@ -71,11 +100,25 @@ describe('Tree view Breadcrumb component', () => { expect(renderResult.container).toBeEmptyDOMElement(); }); + it('clicking on breadcrumb item triggers onSelect', async () => { + renderResult = mockedContext.render( + + ); + + renderResult.getByText(MOCK_TREE_SELECTION[KubernetesCollection.clusterName]!).click(); + expect(onSelect).toHaveBeenCalledTimes(1); + }); + it('renders provided collections only', async () => { renderResult = mockedContext.render( { ); expect( - renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.cluster]!) + renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.clusterName]!) ).toBeVisible(); expect( renderResult.queryByText(MOCK_TREE_SELECTION[KubernetesCollection.namespace]!) diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.tsx index ddef4abd86481..0d1f83cfc2c56 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/breadcrumb/index.tsx @@ -6,9 +6,11 @@ */ import React, { useCallback } from 'react'; -import { EuiButtonEmpty, EuiIcon } from '@elastic/eui'; +import { EuiButtonEmpty, EuiIcon, EuiToolTip } from '@elastic/eui'; import { TreeNavSelection, KubernetesCollection } from '../../../types'; import { useStyles } from './styles'; +import { TreeViewIcon } from '../tree_view_icon'; +import { KUBERNETES_COLLECTION_ICONS_PROPS } from '../helpers'; interface BreadcrumbDeps { treeNavSelection: TreeNavSelection; @@ -21,9 +23,10 @@ export const Breadcrumb = ({ treeNavSelection, onSelect }: BreadcrumbDeps) => { (collectionType: string) => { const selectionCopy = { ...treeNavSelection }; switch (collectionType) { - case KubernetesCollection.cluster: { + case KubernetesCollection.clusterId: { onSelect({ - [KubernetesCollection.cluster]: treeNavSelection[KubernetesCollection.cluster], + [KubernetesCollection.clusterId]: treeNavSelection[KubernetesCollection.clusterId], + [KubernetesCollection.clusterName]: treeNavSelection[KubernetesCollection.clusterName], }); break; } @@ -54,13 +57,18 @@ export const Breadcrumb = ({ treeNavSelection, onSelect }: BreadcrumbDeps) => { <> {hasRightArrow && } {icon} - onBreadCrumbClick(collectionType)} - > - {treeNavSelection[collectionType]} - + + onBreadCrumbClick(collectionType)} + > + {collectionType === KubernetesCollection.clusterId + ? treeNavSelection[KubernetesCollection.clusterName] || + treeNavSelection[KubernetesCollection.clusterId] + : treeNavSelection[collectionType]} + + ), [ @@ -72,15 +80,15 @@ export const Breadcrumb = ({ treeNavSelection, onSelect }: BreadcrumbDeps) => { ] ); - if (!treeNavSelection[KubernetesCollection.cluster]) { + if (!treeNavSelection[KubernetesCollection.clusterId]) { return null; } return (
{renderBreadcrumbLink( - KubernetesCollection.cluster, - , + KubernetesCollection.clusterId, + , !( treeNavSelection[KubernetesCollection.namespace] || treeNavSelection[KubernetesCollection.node] @@ -90,25 +98,25 @@ export const Breadcrumb = ({ treeNavSelection, onSelect }: BreadcrumbDeps) => { {treeNavSelection[KubernetesCollection.namespace] && renderBreadcrumbLink( KubernetesCollection.namespace, - , + , !treeNavSelection[KubernetesCollection.pod] )} {treeNavSelection[KubernetesCollection.node] && renderBreadcrumbLink( KubernetesCollection.node, - , + , !treeNavSelection[KubernetesCollection.pod] )} {treeNavSelection[KubernetesCollection.pod] && renderBreadcrumbLink( KubernetesCollection.pod, - , + , !treeNavSelection[KubernetesCollection.containerImage] )} {treeNavSelection[KubernetesCollection.containerImage] && renderBreadcrumbLink( KubernetesCollection.containerImage, - , + , true )}
diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts index 7607b7253ada5..ef6c849ee9242 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts @@ -8,8 +8,15 @@ import { useInfiniteQuery } from 'react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QueryDslQueryContainerBool } from '../../../types'; -import { QUERY_KEY_PROCESS_EVENTS, AGGREGATE_ROUTE } from '../../../../common/constants'; +import { + QUERY_KEY_PROCESS_EVENTS, + AGGREGATE_ROUTE, + MULTI_TERMS_AGGREGATE_ROUTE, + ORCHESTRATOR_CLUSTER_NAME, +} from '../../../../common/constants'; import { AggregateBucketPaginationResult } from '../../../../common/types/aggregate'; +import { Bucket } from '../../../../common/types/multi_terms_aggregate'; +import { KUBERNETES_COLLECTION_FIELDS } from '../helpers'; export const useFetchDynamicTreeView = ( query: QueryDslQueryContainerBool, @@ -22,8 +29,36 @@ export const useFetchDynamicTreeView = ( return useInfiniteQuery( cachingKeys, - async ({ pageParam = 0 }) => - await http.get(AGGREGATE_ROUTE, { + async ({ pageParam = 0 }) => { + if (groupBy === KUBERNETES_COLLECTION_FIELDS.clusterId) { + const { buckets } = await http.get(MULTI_TERMS_AGGREGATE_ROUTE, { + query: { + query: JSON.stringify(query), + groupBys: JSON.stringify([ + { + field: groupBy, + }, + { + field: ORCHESTRATOR_CLUSTER_NAME, + missing: '', + }, + ]), + page: pageParam, + perPage: 50, + index, + }, + }); + + return { + buckets: buckets.map((bucket: Bucket) => ({ + ...bucket, + key_as_string: bucket.key[1], + key: bucket.key[0], + })), + }; + } + + return await http.get(AGGREGATE_ROUTE, { query: { query: JSON.stringify(query), groupBy, @@ -31,7 +66,8 @@ export const useFetchDynamicTreeView = ( perPage: 50, index, }, - }), + }); + }, { enabled, getNextPageParam: (lastPage, pages) => (lastPage.hasNextPage ? pages.length : undefined), diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx index 4ac51f05c1fc9..6a4595ddfb9a3 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx @@ -48,10 +48,10 @@ describe('DynamicTreeView component', () => { }} tree={[ { - key: 'cluster', - name: 'cluster', + key: 'clusterId', + name: 'clusterId', namePlural: 'clusters', - type: 'cluster', + type: 'clusterId', iconProps: { type: 'cluster', }, @@ -75,13 +75,13 @@ describe('DynamicTreeView component', () => { }); describe('DynamicTreeView parent level', () => { - const key = 'cluster-test'; + const key = 'orchestrator.cluster.id'; const tree = [ { key, name: 'cluster', namePlural: 'clusters', - type: 'cluster', + type: 'clusterId', iconProps: { type: 'cluster', }, @@ -94,15 +94,18 @@ describe('DynamicTreeView component', () => { }); await waitForApiCall(); - expect(mockedApi).toHaveBeenCalledWith('/internal/kubernetes_security/aggregate', { - query: { - groupBy: key, - index: '*-logs', - page: 0, - perPage: 50, - query: '{"bool":{"filter":[],"must":[],"must_not":[],"should":[]}}', - }, - }); + expect(mockedApi).toHaveBeenCalledWith( + '/internal/kubernetes_security/multi_terms_aggregate', + { + query: { + groupBys: `[{"field":"${key}"},{"field":"orchestrator.cluster.name","missing":""}]`, + index: '*-logs', + page: 0, + perPage: 50, + query: '{"bool":{"filter":[],"must":[],"must_not":[],"should":[]}}', + }, + } + ); }); it('should render the parent level based on api response', async () => { @@ -130,10 +133,10 @@ describe('DynamicTreeView component', () => { describe('DynamicTreeView children', () => { const tree = [ { - key: 'cluster', - name: 'cluster', + key: 'orchestrator.cluster.id', + name: 'clusterId', namePlural: 'clusters', - type: 'cluster', + type: 'clusterId', iconProps: { type: 'cluster', }, @@ -165,7 +168,7 @@ describe('DynamicTreeView component', () => { index: '*-logs', page: 0, perPage: 50, - query: `{"bool":{"filter":[{"term":{"cluster":"${parent}"}}],"must":[],"must_not":[],"should":[]}}`, + query: `{"bool":{"filter":[{"term":{"orchestrator.cluster.id":"${parent}"}}],"must":[],"must_not":[],"should":[]}}`, }, }); }); diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.tsx index 66195cb2615bc..df6c7b03d5cc2 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.tsx @@ -14,7 +14,9 @@ import { EuiBadge, keys, EuiLoadingSpinner, + EuiToolTip, } from '@elastic/eui'; +import { KubernetesCollection } from '../../../types'; import { TREE_NAVIGATION_LOADING, TREE_NAVIGATION_SHOW_MORE, @@ -23,6 +25,7 @@ import { useFetchDynamicTreeView } from './hooks'; import { useStyles } from './styles'; import { disableEventDefaults, focusNextElement } from './helpers'; import { useTreeViewContext } from '../contexts'; +import { TreeViewIcon } from '../tree_view_icon'; import type { DynamicTreeViewProps, DynamicTreeViewItemProps } from './types'; const BUTTON_TEST_ID = 'kubernetesSecurity:dynamicTreeViewButton'; @@ -101,7 +104,12 @@ export const DynamicTreeView = ({ useEffect(() => { if (!hasSelection && !depth && data && data.pages?.[0].buckets?.[0]?.key) { - onSelect({}, data.pages[0].buckets[0].key, tree[depth].type); + onSelect( + {}, + tree[depth].type, + data.pages[0].buckets[0].key, + data.pages[0].buckets[0].key_as_string + ); } }, [data, depth, hasSelection, onSelect, tree]); @@ -221,18 +229,26 @@ const DynamicTreeViewItem = ({ const styles = useStyles(depth); const buttonRef = useRef>({}); + const handleSelect = () => { + if (tree[depth].type === KubernetesCollection.clusterId) { + onSelect(selectionDepth, tree[depth].type, aggData.key, aggData.key_as_string); + } else { + onSelect(selectionDepth, tree[depth].type, aggData.key); + } + }; + const onKeyboardToggle = () => { if (!isLastNode) { onToggleExpand(); } - onSelect(selectionDepth, aggData.key, tree[depth].type); + handleSelect(); }; const onButtonToggle = () => { if (!isLastNode && !isExpanded) { onToggleExpand(); } - onSelect(selectionDepth, aggData.key, tree[depth].type); + handleSelect(); }; const onArrowToggle = (event: MouseEvent) => { @@ -308,8 +324,10 @@ const DynamicTreeViewItem = ({ onClick={onArrowToggle} /> )} - - {aggData.key} + + + {aggData.key_as_string || aggData.key} +
onChildrenKeydown(event, aggData.key.toString())} @@ -322,6 +340,9 @@ const DynamicTreeViewItem = ({ selectionDepth={{ ...selectionDepth, [tree[depth].type]: aggData.key, + ...(tree[depth].type === KubernetesCollection.clusterId && { + [KubernetesCollection.clusterName]: aggData.key_as_string, + }), }} tree={tree} onSelect={onSelect} diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/types.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/types.ts index dfddb8f1e38ba..1c3186e8e1dea 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/types.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/types.ts @@ -12,7 +12,12 @@ export type DynamicTreeViewProps = { depth?: number; selectionDepth?: TreeNavSelection; query: QueryDslQueryContainerBool; - onSelect: (selectionDepth: TreeNavSelection, key: string | number, type: string) => void; + onSelect: ( + selectionDepth: TreeNavSelection, + type: string, + key: string | number, + clusterName?: string + ) => void; hasSelection?: boolean; 'aria-label': string; selected?: string; diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/helpers.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/helpers.ts index 12a77a923c6c3..18d95dc0c9d9b 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/helpers.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/helpers.ts @@ -5,15 +5,37 @@ * 2.0. */ -import { DEFAULT_QUERY } from '../../../common/constants'; -import { KubernetesCollection, QueryDslQueryContainerBool, TreeNavSelection } from '../../types'; +import { + CLOUD_INSTANCE_NAME, + CONTAINER_IMAGE_NAME, + DEFAULT_QUERY, + ORCHESTRATOR_CLUSTER_ID, + ORCHESTRATOR_CLUSTER_NAME, + ORCHESTRATOR_NAMESPACE, + ORCHESTRATOR_RESOURCE_ID, +} from '../../../common/constants'; +import { + KubernetesCollection, + QueryDslQueryContainerBool, + TreeNavSelection, + TreeViewIconProps, +} from '../../types'; export const KUBERNETES_COLLECTION_FIELDS = { - [KubernetesCollection.cluster]: 'orchestrator.cluster.name', - [KubernetesCollection.namespace]: 'orchestrator.namespace', - [KubernetesCollection.node]: 'cloud.instance.name', - [KubernetesCollection.pod]: 'orchestrator.resource.name', - [KubernetesCollection.containerImage]: 'container.image.name', + [KubernetesCollection.clusterId]: ORCHESTRATOR_CLUSTER_ID, + [KubernetesCollection.clusterName]: ORCHESTRATOR_CLUSTER_NAME, + [KubernetesCollection.namespace]: ORCHESTRATOR_NAMESPACE, + [KubernetesCollection.node]: CLOUD_INSTANCE_NAME, + [KubernetesCollection.pod]: ORCHESTRATOR_RESOURCE_ID, + [KubernetesCollection.containerImage]: CONTAINER_IMAGE_NAME, +}; + +export const KUBERNETES_COLLECTION_ICONS_PROPS: { [key: string]: TreeViewIconProps } = { + [KubernetesCollection.clusterId]: { type: 'cluster', euiVarColor: 'euiColorVis0' }, + [KubernetesCollection.namespace]: { type: 'namespace', euiVarColor: 'euiColorVis1' }, + [KubernetesCollection.node]: { type: 'kubernetesNode', euiVarColor: 'euiColorVis3' }, + [KubernetesCollection.pod]: { type: 'kubernetesPod', euiVarColor: 'euiColorVis9' }, + [KubernetesCollection.containerImage]: { type: 'container', euiVarColor: 'euiColorVis8' }, }; export const addTreeNavSelectionToFilterQuery = ( @@ -28,20 +50,22 @@ export const addTreeNavSelectionToFilterQuery = ( throw new Error('Invalid filter query'); } parsedFilterQuery.bool.filter.push( - ...Object.keys(treeNavSelection).map((collectionKey) => { - const collection = collectionKey as KubernetesCollection; - return { - bool: { - should: [ - { - match: { - [KUBERNETES_COLLECTION_FIELDS[collection]]: treeNavSelection[collection], + ...Object.keys(treeNavSelection) + .filter((key) => key !== KubernetesCollection.clusterName) + .map((collectionKey) => { + const collection = collectionKey as KubernetesCollection; + return { + bool: { + should: [ + { + match: { + [KUBERNETES_COLLECTION_FIELDS[collection]]: treeNavSelection[collection], + }, }, - }, - ], - }, - }; - }) + ], + }, + }; + }) ); validFilterQuery = JSON.stringify(parsedFilterQuery); } catch { diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/hooks.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/hooks.tsx index d077d2c6908d6..7d322fadd8dcb 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/hooks.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/hooks.tsx @@ -48,7 +48,7 @@ export const useTreeView = ({ globalFilter, indexPattern }: UseTreeViewProps) => }, [filterQueryWithTimeRange]); useEffect(() => { - if (!!treeNavSelection[KubernetesCollection.cluster]) { + if (!!treeNavSelection[KubernetesCollection.clusterId]) { setHasSelection(true); setTreeNavSelection(treeNavSelection); } diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/mocks.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/mocks.ts index e14a1aa73e04b..7b1e098151e5e 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/mocks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/mocks.ts @@ -7,8 +7,16 @@ export const clusterResponseMock = { buckets: [ - { key: 'awp-demo-gke-main', doc_count: 58645 }, - { key: 'awp-demo-gke-test', doc_count: 23957 }, + { + key: ['awp-demo-gke-main', 'awp-demo-gke-main'], + key_as_string: 'awp-demo-gke-main|awp-demo-gke-main', + doc_count: 22279, + }, + { + key: ['awp-demo-gke-test', ''], + key_as_string: 'awp-demo-gke-test|', + doc_count: 1, + }, ], hasNextPage: false, }; diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/constants.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/constants.ts index b0dd7a66c0473..eb5e7ccd00657 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/constants.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/constants.ts @@ -6,34 +6,34 @@ */ import { KubernetesCollection, DynamicTree } from '../../../types'; -import { KUBERNETES_COLLECTION_FIELDS } from '../helpers'; +import { KUBERNETES_COLLECTION_FIELDS, KUBERNETES_COLLECTION_ICONS_PROPS } from '../helpers'; import { translations } from './translations'; const LOGICAL_TREE_VIEW: DynamicTree[] = [ { - key: KUBERNETES_COLLECTION_FIELDS.cluster, - iconProps: { type: 'heatmap', color: 'success' }, - type: KubernetesCollection.cluster, + key: KUBERNETES_COLLECTION_FIELDS.clusterId, + iconProps: KUBERNETES_COLLECTION_ICONS_PROPS.clusterId, + type: KubernetesCollection.clusterId, name: translations.cluster(), namePlural: translations.cluster(true), }, { key: KUBERNETES_COLLECTION_FIELDS.namespace, - iconProps: { type: 'nested', color: 'primary' }, + iconProps: KUBERNETES_COLLECTION_ICONS_PROPS.namespace, type: KubernetesCollection.namespace, name: translations.namespace(), namePlural: translations.namespace(true), }, { key: KUBERNETES_COLLECTION_FIELDS.pod, - iconProps: { type: 'package', color: 'warning' }, + iconProps: KUBERNETES_COLLECTION_ICONS_PROPS.pod, type: KubernetesCollection.pod, name: translations.pod(), namePlural: translations.pod(true), }, { key: KUBERNETES_COLLECTION_FIELDS.containerImage, - iconProps: { type: 'image', color: 'danger' }, + iconProps: KUBERNETES_COLLECTION_ICONS_PROPS.containerImage, type: KubernetesCollection.containerImage, name: translations.containerImage(), namePlural: translations.containerImage(true), @@ -44,7 +44,7 @@ const INFRASTRUCTURE_TREE_VIEW = LOGICAL_TREE_VIEW.map((tree, index) => { if (index === 1) { return { key: KUBERNETES_COLLECTION_FIELDS.node, - iconProps: { type: 'node', color: 'primary' }, + iconProps: KUBERNETES_COLLECTION_ICONS_PROPS.node, type: KubernetesCollection.node, name: translations.node(), namePlural: translations.node(true), diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.tsx index 0f10ccdcd41e1..1dc74fadbc4fd 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.tsx @@ -15,6 +15,7 @@ import { EuiFlexItem, EuiToolTip, } from '@elastic/eui'; +import { KubernetesCollection } from '../../../types'; import { TREE_VIEW_INFRASTRUCTURE_VIEW, TREE_VIEW_LOGICAL_VIEW, @@ -117,10 +118,13 @@ export const TreeNav = () => { tree={tree} aria-label={selectedLabel} selected={selected} - onSelect={(selectionDepth, key, type) => { + onSelect={(selectionDepth, type, key, clusterName) => { const newSelectionDepth = { ...selectionDepth, [type]: key, + ...(clusterName && { + [KubernetesCollection.clusterName]: clusterName, + }), }; setSelected( Object.entries(newSelectionDepth) diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_view_icon.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_view_icon.tsx new file mode 100644 index 0000000000000..8434caadf817d --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_view_icon.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiIcon } from '@elastic/eui'; +import { useEuiTheme } from '../../hooks'; +import { TreeViewIconProps } from '../../types'; + +export const TreeViewIcon = ({ euiVarColor, ...props }: TreeViewIconProps) => { + const { euiVars } = useEuiTheme(); + + const colorStyle = euiVars[euiVarColor] ? { style: { color: euiVars[euiVarColor] } } : {}; + + return ; +}; diff --git a/x-pack/plugins/kubernetes_security/public/hooks/use_eui_theme.ts b/x-pack/plugins/kubernetes_security/public/hooks/use_eui_theme.ts index 02f7dd479d2ac..67167f0c0edbc 100644 --- a/x-pack/plugins/kubernetes_security/public/hooks/use_eui_theme.ts +++ b/x-pack/plugins/kubernetes_security/public/hooks/use_eui_theme.ts @@ -18,6 +18,10 @@ type ExtraEuiVars = { type EuiVars = typeof euiLightVars & ExtraEuiVars; type EuiThemeReturn = ReturnType & { euiVars: EuiVars }; +export type EuiVarsColors = Pick< + ReturnType['euiVars'], + 'euiColorVis0' | 'euiColorVis1' | 'euiColorVis3' | 'euiColorVis8' | 'euiColorVis9' +>; // Not all Eui Tokens were fully migrated to @elastic/eui/useEuiTheme yet, so // this hook overrides the default useEuiTheme hook to provide a custom hook that // allows the use the euiVars tokens from the euiLightVars and euiDarkVars diff --git a/x-pack/plugins/kubernetes_security/public/types.ts b/x-pack/plugins/kubernetes_security/public/types.ts index 873c3783be781..dafeea490d7af 100644 --- a/x-pack/plugins/kubernetes_security/public/types.ts +++ b/x-pack/plugins/kubernetes_security/public/types.ts @@ -10,8 +10,9 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldSpec } from '@kbn/data-plugin/common'; import type { TimelinesUIStart } from '@kbn/timelines-plugin/public'; import type { SessionViewStart } from '@kbn/session-view-plugin/public'; -import { BoolQuery } from '@kbn/es-query'; import { EuiIconProps } from '@elastic/eui'; +import { BoolQuery } from '@kbn/es-query'; +import { EuiVarsColors } from './hooks/use_eui_theme'; export interface StartPlugins { data: DataPublicPluginStart; @@ -48,7 +49,8 @@ export type QueryDslQueryContainerBool = { }; export enum KubernetesCollection { - cluster = 'cluster', + clusterId = 'clusterId', + clusterName = 'clusterName', namespace = 'namespace', node = 'node', pod = 'pod', @@ -56,17 +58,22 @@ export enum KubernetesCollection { } export interface TreeNavSelection { - [KubernetesCollection.cluster]?: string; + [KubernetesCollection.clusterId]?: string; + [KubernetesCollection.clusterName]?: string; [KubernetesCollection.namespace]?: string; [KubernetesCollection.node]?: string; [KubernetesCollection.pod]?: string; [KubernetesCollection.containerImage]?: string; } +export type TreeViewIconProps = { + euiVarColor: keyof EuiVarsColors; +} & EuiIconProps; + export type DynamicTree = { key: string; type: KubernetesCollection; - iconProps: EuiIconProps; + iconProps: TreeViewIconProps; name: string; namePlural: string; }; diff --git a/x-pack/plugins/kubernetes_security/server/routes/index.ts b/x-pack/plugins/kubernetes_security/server/routes/index.ts index 6f7236ebb25d7..734215e54e275 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/index.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/index.ts @@ -8,8 +8,10 @@ import { IRouter } from '@kbn/core/server'; import { RuleRegistryPluginStartContract } from '@kbn/rule-registry-plugin/server'; import { registerAggregateRoute } from './aggregate'; import { registerCountRoute } from './count'; +import { registerMultiTermsAggregateRoute } from './multi_terms_aggregate'; export const registerRoutes = (router: IRouter, ruleRegistry: RuleRegistryPluginStartContract) => { registerAggregateRoute(router); registerCountRoute(router); + registerMultiTermsAggregateRoute(router); }; diff --git a/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts new file mode 100644 index 0000000000000..f3c22cf40e28c --- /dev/null +++ b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { schema } from '@kbn/config-schema'; +import type { ElasticsearchClient } from '@kbn/core/server'; +import { IRouter } from '@kbn/core/server'; +import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants'; +import { MULTI_TERMS_AGGREGATE_ROUTE, AGGREGATE_PAGE_SIZE } from '../../common/constants'; +import { + MultiTermsAggregateGroupBy, + MultiTermsAggregateBucketPaginationResult, +} from '../../common/types/multi_terms_aggregate'; + +export const registerMultiTermsAggregateRoute = (router: IRouter) => { + router.get( + { + path: MULTI_TERMS_AGGREGATE_ROUTE, + validate: { + query: schema.object({ + query: schema.string(), + countBy: schema.maybe(schema.string()), + groupBys: schema.arrayOf( + schema.object({ + field: schema.string(), + missing: schema.maybe(schema.string()), + }), + { defaultValue: [] } + ), + page: schema.number(), + perPage: schema.maybe(schema.number()), + index: schema.maybe(schema.string()), + }), + }, + }, + async (context, request, response) => { + const client = (await context.core).elasticsearch.client.asCurrentUser; + const { query, countBy, groupBys, page, perPage, index } = request.query; + + try { + const body = await doSearch(client, query, groupBys, page, perPage, index, countBy); + + return response.ok({ body }); + } catch (err) { + return response.badRequest(err.message); + } + } + ); +}; + +export const doSearch = async ( + client: ElasticsearchClient, + query: string, + groupBys: MultiTermsAggregateGroupBy[], + page: number, // zero based + perPage = AGGREGATE_PAGE_SIZE, + index?: string, + countBy?: string +): Promise => { + const queryDSL = JSON.parse(query); + + const countByAggs = countBy + ? { + count_by_aggs: { + cardinality: { + field: countBy, + }, + }, + } + : undefined; + + const search = await client.search({ + index: [index || PROCESS_EVENTS_INDEX], + body: { + query: queryDSL, + size: 0, + aggs: { + custom_agg: { + multi_terms: { + terms: groupBys, + }, + aggs: { + ...countByAggs, + bucket_sort: { + bucket_sort: { + size: perPage + 1, // check if there's a "next page" + from: perPage * page, + }, + }, + }, + }, + }, + }, + }); + + const agg: any = search.aggregations?.custom_agg; + const buckets = agg?.buckets || []; + + const hasNextPage = buckets.length > perPage; + + if (hasNextPage) { + buckets.pop(); + } + + return { + buckets, + hasNextPage, + }; +}; diff --git a/x-pack/plugins/lens/public/async_services.ts b/x-pack/plugins/lens/public/async_services.ts index cd2a9787c9cef..9025b738c2e1f 100644 --- a/x-pack/plugins/lens/public/async_services.ts +++ b/x-pack/plugins/lens/public/async_services.ts @@ -14,18 +14,18 @@ * This file causes all of them to be served in a single request. */ -export * from './datatable_visualization/datatable_visualization'; -export * from './datatable_visualization'; -export * from './metric_visualization/metric_visualization'; -export * from './metric_visualization'; +export * from './visualizations/datatable/datatable_visualization'; +export * from './visualizations/datatable'; +export * from './visualizations/legacy_metric/metric_visualization'; +export * from './visualizations/legacy_metric'; export * from './visualizations/metric/metric_visualization'; export * from './visualizations/metric'; -export * from './pie_visualization/pie_visualization'; -export * from './pie_visualization'; -export * from './xy_visualization/xy_visualization'; -export * from './xy_visualization'; -export * from './heatmap_visualization/heatmap_visualization'; -export * from './heatmap_visualization'; +export * from './visualizations/partition/pie_visualization'; +export * from './visualizations/partition'; +export * from './visualizations/xy/xy_visualization'; +export * from './visualizations/xy'; +export * from './visualizations/heatmap/heatmap_visualization'; +export * from './visualizations/heatmap'; export * from './visualizations/gauge/gauge_visualization'; export * from './visualizations/gauge'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index ecaf23bb34d41..238111cd8d947 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -554,7 +554,7 @@ export function LayerPanel( panelRef={(el) => (panelRef.current = el)} isOpen={isDimensionPanelOpen} isFullscreen={isFullscreen} - groupLabel={activeGroup?.groupLabel || ''} + groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')} handleClose={() => { if (layerDatasource) { if ( @@ -582,7 +582,7 @@ export function LayerPanel( return true; }} panel={ -
+ <> {activeGroup && activeId && layerDatasource && ( - -
+ <> +
+ +
+ {activeVisualization.renderDimensionEditorAdditionalSection && ( + + )} + )} -
+ } /> diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 6797ca674a06b..78aae56c5b9ad 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -23,10 +23,10 @@ import { import type { LensByReferenceInput, LensByValueInput } from './embeddable'; import type { Document } from '../persistence'; import type { IndexPatternPersistedState } from '../indexpattern_datasource/types'; -import type { XYState } from '../xy_visualization/types'; +import type { XYState } from '../visualizations/xy/types'; import type { PieVisualizationState, MetricState } from '../../common'; -import type { DatatableVisualizationState } from '../datatable_visualization/visualization'; -import type { HeatmapVisualizationState } from '../heatmap_visualization/types'; +import type { DatatableVisualizationState } from '../visualizations/datatable/visualization'; +import type { HeatmapVisualizationState } from '../visualizations/heatmap/types'; import type { GaugeVisualizationState } from '../visualizations/gauge/constants'; type LensAttributes = Omit< diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index f0dae2ca28409..82e510dbb6393 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -21,7 +21,7 @@ export type { YAxisMode, SeriesType, YConfig, -} from './xy_visualization/types'; +} from './visualizations/xy/types'; export type { DatasourcePublicAPI, DataType, @@ -39,8 +39,8 @@ export type { SharedPieLayerState, } from '../common/types'; -export type { DatatableVisualizationState } from './datatable_visualization/visualization'; -export type { HeatmapVisualizationState } from './heatmap_visualization/types'; +export type { DatatableVisualizationState } from './visualizations/datatable/visualization'; +export type { HeatmapVisualizationState } from './visualizations/heatmap/types'; export type { GaugeVisualizationState } from './visualizations/gauge/constants'; export type { IndexPatternPersistedState, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/advanced_options.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/advanced_options.tsx index 598bc5f4658b5..c8cbfb947a9e3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/advanced_options.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/advanced_options.tsx @@ -5,9 +5,10 @@ * 2.0. */ -import { EuiSpacer, EuiAccordion, EuiText, useEuiTheme } from '@elastic/eui'; +import { EuiSpacer, EuiAccordion, EuiTextColor, EuiTitle, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { css } from '@emotion/react'; import { AdvancedOption } from '../operations/definitions'; export function AdvancedOptions(props: { options: AdvancedOption[] }) { @@ -17,13 +18,21 @@ export function AdvancedOptions(props: { options: AdvancedOption[] }) { id="advancedOptionsAccordion" arrowProps={{ color: 'primary' }} data-test-subj="indexPattern-advanced-accordion" + className="lnsIndexPatternDimensionEditor-advancedOptions" buttonContent={ - - {i18n.translate('xpack.lens.indexPattern.advancedSettings', { - defaultMessage: 'Advanced', - })} - + +
+ + {i18n.translate('xpack.lens.indexPattern.advancedSettings', { + defaultMessage: 'Advanced', + })} + +
+
} + css={css` + padding: 0 ${euiTheme.size.base} ${euiTheme.size.base}; + `} > {props.options.map(({ dataTestSubj, inlineElement }) => (
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss index 99286ad34b380..1a7cf1fd787e6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss @@ -16,21 +16,19 @@ right: 0; top: 0; bottom: 0; - - .lnsIndexPatternDimensionEditor__section { - height: 100%; - } } -.lnsIndexPatternDimensionEditor__section--padded { +.lnsIndexPatternDimensionEditor--padded { padding: $euiSize; } -.lnsIndexPatternDimensionEditor__section--collapseNext { +.lnsIndexPatternDimensionEditor--collapseNext { margin-bottom: -$euiSizeL; + border-top: $euiBorderThin; + margin-top: 0 !important; } -.lnsIndexPatternDimensionEditor__section--shaded { +.lnsIndexPatternDimensionEditor--shaded { background-color: $euiColorLightestShade; border-bottom: $euiBorderThin; } @@ -48,3 +46,23 @@ padding-top: 0; padding-bottom: 0; } + +.lnsIndexPatternDimensionEditor__warning { + margin-bottom: $euiSize; + margin-top: $euiSizeS; +} + +.lnsIndexPatternDimensionEditor__droppable { + padding: $euiSizeXS; + border-radius: $euiBorderRadius; +} + +.lnsIndexPatternDimensionEditor__droppableItem { + margin-right: $euiSizeS; +} + +.lnsIndexPatternDimensionEditor-advancedOptions button { + &:hover, &:focus { + text-decoration-color: $euiColorPrimary; + } +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index e1884808910d1..487f559c6a161 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -8,13 +8,18 @@ import './dimension_editor.scss'; import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; import { EuiListGroup, + EuiFormRow, EuiSpacer, EuiListGroupItemProps, - EuiFormLabel, EuiToolTip, EuiText, + EuiIconTip, + useEuiTheme, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import ReactDOM from 'react-dom'; import type { IndexPatternDimensionEditorProps } from './dimension_panel'; @@ -41,6 +46,7 @@ import { FormatSelector } from './format_selector'; import { ReferenceEditor } from './reference_editor'; import { TimeScaling } from './time_scaling'; import { Filtering } from './filtering'; +import { Window } from './window'; import { AdvancedOptions } from './advanced_options'; import { TimeShift } from './time_shift'; import type { LayerType } from '../../../common'; @@ -51,9 +57,9 @@ import { isQuickFunction, getParamEditor, formulaOperationName, - DimensionEditorTabs, + DimensionEditorButtonGroups, CalloutWarning, - DimensionEditorTab, + DimensionEditorGroupsOptions, } from './dimensions_editor_helpers'; import type { TemporaryState } from './dimensions_editor_helpers'; import { FieldInput } from './field_input'; @@ -110,6 +116,7 @@ export function DimensionEditor(props: DimensionEditorProps) { const temporaryQuickFunction = Boolean(temporaryState === quickFunctionsName); const temporaryStaticValue = Boolean(temporaryState === staticValueOperationName); + const { euiTheme } = useEuiTheme(); const updateLayer = useCallback( (newLayer) => setState((prevState) => mergeLayer({ state: prevState, layerId, newLayer })), @@ -314,6 +321,10 @@ export function DimensionEditor(props: DimensionEditorProps) { [selectedColumn, currentIndexPattern] ); + const shouldDisplayDots = + temporaryState === 'none' || + (selectedColumn?.operationType != null && isQuickFunction(selectedColumn?.operationType)); + const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map( ({ operationType, compatibleWithCurrentField, disabledStatus }) => { const isActive = Boolean( @@ -321,13 +332,6 @@ export function DimensionEditor(props: DimensionEditorProps) { (!incompleteOperation && selectedColumn && selectedColumn.operationType === operationType) ); - let color: EuiListGroupItemProps['color'] = 'primary'; - if (isActive) { - color = 'text'; - } else if (!compatibleWithCurrentField) { - color = 'subdued'; - } - let label: EuiListGroupItemProps['label'] = operationDisplay[operationType].displayName; if (isActive && disabledStatus) { label = ( @@ -343,14 +347,33 @@ export function DimensionEditor(props: DimensionEditorProps) { {operationDisplay[operationType].displayName} ); - } else if (isActive) { - label = {operationDisplay[operationType].displayName}; + } else if (!compatibleWithCurrentField) { + label = ( + + + {label} + + {shouldDisplayDots && ( + + + + )} + + ); } return { id: operationType as string, label, - color, isActive, size: 's', isDisabled: !!disabledStatus, @@ -544,16 +567,16 @@ export function DimensionEditor(props: DimensionEditorProps) { const quickFunctions = ( <> -
- - {i18n.translate('xpack.lens.indexPattern.functionsLabel', { - defaultMessage: 'Functions', - })} - - + 3 ? 'lnsIndexPatternDimensionEditor__columns' : ''} gutterSize="none" + color="primary" listItems={ // add a padding item containing a non breakable space if the number of operations is not even // otherwise the column layout will break within an element @@ -561,136 +584,132 @@ export function DimensionEditor(props: DimensionEditorProps) { } maxWidth={false} /> -
+ -
- {shouldDisplayReferenceEditor ? ( - <> - {selectedColumn.references.map((referenceId, index) => { - const validation = selectedOperationDefinition.requiredReferences[index]; - const layer = state.layers[layerId]; - return ( - + {selectedColumn.references.map((referenceId, index) => { + const validation = selectedOperationDefinition.requiredReferences[index]; + const layer = state.layers[layerId]; + return ( + { + updateLayer( + deleteColumn({ + layer, + columnId: referenceId, + indexPattern: currentIndexPattern, + }) + ); + }} + onChooseFunction={(operationType: string, field?: IndexPatternField) => { + updateLayer( + insertOrReplaceColumn({ + layer, + columnId: referenceId, + op: operationType, + indexPattern: currentIndexPattern, + field, + visualizationGroups: dimensionGroups, + }) + ); + }} + onChooseField={(choice: FieldChoiceWithOperationType) => { + updateLayer( + insertOrReplaceColumn({ + layer, + columnId: referenceId, + indexPattern: currentIndexPattern, + op: choice.operationType, + field: currentIndexPattern.getFieldByName(choice.field), + visualizationGroups: dimensionGroups, + }) + ); + }} + paramEditorUpdater={( + setter: + | IndexPatternLayer + | ((prevLayer: IndexPatternLayer) => IndexPatternLayer) + | GenericIndexPatternColumn + ) => { + let newLayer: IndexPatternLayer; + if (typeof setter === 'function') { + newLayer = setter(layer); + } else if (isColumn(setter)) { + newLayer = { + ...layer, + columns: { + ...layer.columns, + [referenceId]: setter, + }, + }; + } else { + newLayer = setter; } - onDeleteColumn={() => { - updateLayer( - deleteColumn({ - layer, - columnId: referenceId, - indexPattern: currentIndexPattern, - }) - ); - }} - onChooseFunction={(operationType: string, field?: IndexPatternField) => { - updateLayer( - insertOrReplaceColumn({ - layer, - columnId: referenceId, - op: operationType, - indexPattern: currentIndexPattern, - field, - visualizationGroups: dimensionGroups, - }) - ); - }} - onChooseField={(choice: FieldChoiceWithOperationType) => { - updateLayer( - insertOrReplaceColumn({ - layer, - columnId: referenceId, - indexPattern: currentIndexPattern, - op: choice.operationType, - field: currentIndexPattern.getFieldByName(choice.field), - visualizationGroups: dimensionGroups, - }) - ); - }} - paramEditorUpdater={( - setter: - | IndexPatternLayer - | ((prevLayer: IndexPatternLayer) => IndexPatternLayer) - | GenericIndexPatternColumn - ) => { - let newLayer: IndexPatternLayer; - if (typeof setter === 'function') { - newLayer = setter(layer); - } else if (isColumn(setter)) { - newLayer = { - ...layer, - columns: { - ...layer.columns, - [referenceId]: setter, - }, - }; - } else { - newLayer = setter; - } - return updateLayer( - adjustColumnReferencesForChangedColumn(newLayer, referenceId) - ); - }} - validation={validation} - currentIndexPattern={currentIndexPattern} - existingFields={state.existingFields} - selectionStyle={selectedOperationDefinition.selectionStyle} - dateRange={dateRange} - labelAppend={selectedOperationDefinition?.getHelpMessage?.({ - data: props.data, - uiSettings: props.uiSettings, - currentColumn: layer.columns[columnId], - })} - isFullscreen={isFullscreen} - toggleFullscreen={toggleFullscreen} - setIsCloseable={setIsCloseable} - paramEditorCustomProps={paramEditorCustomProps} - {...services} - /> - ); - })} - {selectedOperationDefinition.selectionStyle !== 'field' ? : null} - - ) : null} - - {shouldDisplayFieldInput ? ( - { - if (temporaryQuickFunction) { - setTemporaryState('none'); - } - setStateWrapper(newLayer, { forceRender: temporaryQuickFunction }); - }} - incompleteField={incompleteField} - incompleteOperation={incompleteOperation} - incompleteParams={incompleteParams} - currentFieldIsInvalid={currentFieldIsInvalid} - helpMessage={selectedOperationDefinition?.getHelpMessage?.({ - data: props.data, - uiSettings: props.uiSettings, - currentColumn: state.layers[layerId].columns[columnId], - })} - dimensionGroups={dimensionGroups} - groupId={props.groupId} - operationDefinitionMap={operationDefinitionMap} - /> - ) : null} + return updateLayer(adjustColumnReferencesForChangedColumn(newLayer, referenceId)); + }} + validation={validation} + currentIndexPattern={currentIndexPattern} + existingFields={state.existingFields} + selectionStyle={selectedOperationDefinition.selectionStyle} + dateRange={dateRange} + labelAppend={selectedOperationDefinition?.getHelpMessage?.({ + data: props.data, + uiSettings: props.uiSettings, + currentColumn: layer.columns[columnId], + })} + isFullscreen={isFullscreen} + toggleFullscreen={toggleFullscreen} + setIsCloseable={setIsCloseable} + paramEditorCustomProps={paramEditorCustomProps} + {...services} + /> + ); + })} + {selectedOperationDefinition.selectionStyle !== 'field' ? : null} + + ) : null} - {shouldDisplayExtraOptions && } -
+ {shouldDisplayFieldInput ? ( + { + if (temporaryQuickFunction) { + setTemporaryState('none'); + } + setStateWrapper(newLayer, { forceRender: temporaryQuickFunction }); + }} + incompleteField={incompleteField} + incompleteOperation={incompleteOperation} + incompleteParams={incompleteParams} + currentFieldIsInvalid={currentFieldIsInvalid} + helpMessage={selectedOperationDefinition?.getHelpMessage?.({ + data: props.data, + uiSettings: props.uiSettings, + currentColumn: state.layers[layerId].columns[columnId], + })} + dimensionGroups={dimensionGroups} + groupId={props.groupId} + operationDefinitionMap={operationDefinitionMap} + /> + ) : null} + + {shouldDisplayExtraOptions && } ); @@ -719,7 +738,7 @@ export function DimensionEditor(props: DimensionEditorProps) { ) : null; - const TabContent = showQuickFunctions ? quickFunctions : customParamEditor; + const ButtonGroupContent = showQuickFunctions ? quickFunctions : customParamEditor; const onFormatChange = useCallback( (newFormat) => { @@ -738,9 +757,24 @@ export function DimensionEditor(props: DimensionEditorProps) { const hasFormula = !isFullscreen && operationSupportMatrix.operationWithoutField.has(formulaOperationName); - const hasTabs = !isFullscreen && (hasFormula || supportStaticValue); + const hasButtonGroups = !isFullscreen && (hasFormula || supportStaticValue); + const initialMethod = useMemo(() => { + let methodId = ''; + if (showStaticValueFunction) { + methodId = staticValueOperationName; + } else if (showQuickFunctions) { + methodId = quickFunctionsName; + } else if ( + temporaryState === 'none' && + selectedColumn?.operationType === formulaOperationName + ) { + methodId = formulaOperationName; + } + return methodId; + }, [selectedColumn?.operationType, showQuickFunctions, showStaticValueFunction, temporaryState]); + const [selectedMethod, setSelectedMethod] = useState(initialMethod); - const tabs: DimensionEditorTab[] = [ + const options: DimensionEditorGroupsOptions[] = [ { id: staticValueOperationName, enabled: Boolean(supportStaticValue), @@ -768,7 +802,7 @@ export function DimensionEditor(props: DimensionEditorProps) { } }, label: i18n.translate('xpack.lens.indexPattern.quickFunctionsLabel', { - defaultMessage: 'Quick functions', + defaultMessage: 'Quick function', }), }, { @@ -820,118 +854,169 @@ export function DimensionEditor(props: DimensionEditorProps) { return (
- {hasTabs ? : null} - - {TabContent} +
+ +

+ {paramEditorCustomProps?.headingLabel ?? + i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingData', { + defaultMessage: 'Data', + })} +

+
+ <> + {hasButtonGroups ? ( + { + setSelectedMethod(optionId); + }} + selectedMethod={selectedMethod} + /> + ) : null} + + {ButtonGroupContent} + +
{shouldDisplayAdvancedOptions && ( -
- - ) : null, - }, - { - dataTestSubj: 'indexPattern-filter-by-enable', - inlineElement: selectedOperationDefinition.filterable ? ( - - ) : null, - }, - { - dataTestSubj: 'indexPattern-time-shift-enable', - inlineElement: Boolean( - selectedOperationDefinition.shiftable && - (currentIndexPattern.timeFieldName || - Object.values(state.layers[layerId].columns).some( - (col) => col.operationType === 'date_histogram' - )) - ) ? ( - - ) : null, - }, - ...(operationDefinitionMap[selectedColumn.operationType].getAdvancedOptions?.( - paramEditorProps - ) || []), - ]} - /> -
+ + ) : null, + }, + { + dataTestSubj: 'indexPattern-filter-by-enable', + inlineElement: selectedOperationDefinition.filterable ? ( + + ) : null, + }, + { + dataTestSubj: 'indexPattern-window-enable', + inlineElement: selectedOperationDefinition.windowable ? ( + + ) : null, + }, + { + dataTestSubj: 'indexPattern-time-shift-enable', + inlineElement: Boolean( + selectedOperationDefinition.shiftable && + (currentIndexPattern.timeFieldName || + Object.values(state.layers[layerId].columns).some( + (col) => col.operationType === 'date_histogram' + )) + ) ? ( + + ) : null, + }, + ...(operationDefinitionMap[selectedColumn.operationType].getAdvancedOptions?.( + paramEditorProps + ) || []), + ]} + /> )} {!isFullscreen && !currentFieldIsInvalid && ( -
- {!incompleteInfo && selectedColumn && temporaryState === 'none' && ( - { - updateLayer({ - columns: { - ...state.layers[layerId].columns, - [columnId]: { - ...selectedColumn, - label: value, - customLabel: - operationDefinitionMap[selectedColumn.operationType].getDefaultLabel( - selectedColumn, - state.indexPatterns[state.layers[layerId].indexPatternId], - state.layers[layerId].columns - ) !== value, - }, - }, - }); - }} - /> +
+ {!incompleteInfo && temporaryState === 'none' && selectedColumn && ( + +

+ {i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingAppearance', { + defaultMessage: 'Appearance', + })} +

+
)} + <> + {!incompleteInfo && selectedColumn && temporaryState === 'none' && ( + { + updateLayer({ + columns: { + ...state.layers[layerId].columns, + [columnId]: { + ...selectedColumn, + label: value, + customLabel: + operationDefinitionMap[selectedColumn.operationType].getDefaultLabel( + selectedColumn, + state.indexPatterns[state.layers[layerId].indexPatternId], + state.layers[layerId].columns + ) !== value, + }, + }, + }); + }} + /> + )} - {!isFullscreen && !incompleteInfo && !hideGrouping && temporaryState === 'none' && ( - updateLayer({ columnOrder })} - getFieldByName={currentIndexPattern.getFieldByName} - /> - )} + {!isFullscreen && !incompleteInfo && !hideGrouping && temporaryState === 'none' && ( + updateLayer({ columnOrder })} + getFieldByName={currentIndexPattern.getFieldByName} + /> + )} - {supportFieldFormat && - !isFullscreen && - selectedColumn && - (selectedColumn.dataType === 'number' || selectedColumn.operationType === 'range') ? ( - - ) : null} + {supportFieldFormat && + !isFullscreen && + selectedColumn && + (selectedColumn.dataType === 'number' || selectedColumn.operationType === 'range') ? ( + + ) : null} +
)}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 0f5a5747bc7c0..236da5a4d8f1d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -8,6 +8,7 @@ import { ReactWrapper, ShallowWrapper } from 'enzyme'; import React, { ChangeEvent } from 'react'; import { act } from 'react-dom/test-utils'; +import { findTestSubject } from '@elastic/eui/lib/test'; import { EuiComboBox, EuiListGroupItemProps, @@ -44,6 +45,7 @@ import { DateHistogramIndexPatternColumn } from '../operations/definitions/date_ import { getFieldByNameFactory } from '../pure_helpers'; import { Filtering, setFilter } from './filtering'; import { TimeShift } from './time_shift'; +import { Window } from './window'; import { DimensionEditor } from './dimension_editor'; import { AdvancedOptions } from './advanced_options'; import { layerTypes } from '../../../common'; @@ -1108,7 +1110,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should default to None if time scaling is not set', () => { wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); expect(wrapper.find('[data-test-subj="indexPattern-time-scaling-enable"]')).toHaveLength(1); expect( wrapper @@ -1120,7 +1122,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should show current time scaling if set', () => { wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); expect( wrapper .find('[data-test-subj="indexPattern-time-scaling-unit"]') @@ -1132,7 +1134,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should allow to set time scaling initially', () => { const props = getProps({}); wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); wrapper .find('[data-test-subj="indexPattern-time-scaling-unit"]') .find(EuiSelect) @@ -1214,7 +1216,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should allow to change time scaling', () => { const props = getProps({ timeScale: 's', label: 'Count of records per second' }); wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); wrapper.find('[data-test-subj="indexPattern-time-scaling-unit"] select').simulate('change', { target: { value: 'h' }, @@ -1263,6 +1265,145 @@ describe('IndexPatternDimensionEditorPanel', () => { }); }); + describe('window', () => { + function getProps(colOverrides: Partial) { + return { + ...defaultProps, + state: getStateWithColumns({ + col2: { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + operationType: 'count', + sourceField: '___records___', + ...colOverrides, + } as GenericIndexPatternColumn, + }), + columnId: 'col2', + }; + } + + it('should not show the window component if window is not available', () => { + const props = { + ...defaultProps, + state: getStateWithColumns({ + datecolumn: { + dataType: 'date', + isBucketed: true, + label: '', + customLabel: true, + operationType: 'date_histogram', + sourceField: 'ts', + params: { + interval: '1d', + }, + } as DateHistogramIndexPatternColumn, + col2: { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + operationType: 'count', + sourceField: '___records___', + } as GenericIndexPatternColumn, + }), + columnId: 'col2', + }; + wrapper = mount(); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); + expect(wrapper.find('[data-test-subj="indexPattern-dimension-window-row"]')).toHaveLength(0); + }); + + it('should show current window if set', () => { + wrapper = mount(); + expect(wrapper.find(Window).find(EuiComboBox).prop('selectedOptions')[0].value).toEqual('5m'); + }); + + it('should allow to set window initially', () => { + const props = getProps({}); + wrapper = mount(); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); + wrapper.find(Window).find(EuiComboBox).prop('onChange')!([{ value: '1h', label: '' }]); + expect((props.setState as jest.Mock).mock.calls[0][0](props.state)).toEqual({ + ...props.state, + layers: { + first: { + ...props.state.layers.first, + columns: { + ...props.state.layers.first.columns, + col2: expect.objectContaining({ + window: '1h', + }), + }, + }, + }, + }); + }); + + it('should carry over window to other operation if possible', () => { + const props = getProps({ + window: '1d', + sourceField: 'bytes', + operationType: 'sum', + label: 'Sum of bytes per hour', + }); + wrapper = mount(); + wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); + expect((props.setState as jest.Mock).mock.calls[0][0](props.state)).toEqual({ + ...props.state, + layers: { + first: { + ...props.state.layers.first, + columns: { + ...props.state.layers.first.columns, + col2: expect.objectContaining({ + window: '1d', + }), + }, + }, + }, + }); + }); + + it('should allow to change window', () => { + const props = getProps({ + timeShift: '1d', + }); + wrapper = mount(); + wrapper.find(Window).find(EuiComboBox).prop('onCreateOption')!('7m', []); + expect((props.setState as jest.Mock).mock.calls[0][0](props.state)).toEqual({ + ...props.state, + layers: { + first: { + ...props.state.layers.first, + columns: { + ...props.state.layers.first.columns, + col2: expect.objectContaining({ + window: '7m', + }), + }, + }, + }, + }); + }); + + it('should report a generic error for invalid window string', () => { + const props = getProps({ + window: '5 months', + }); + wrapper = mount(); + + expect(wrapper.find(Window).find(EuiComboBox).prop('isInvalid')).toBeTruthy(); + + expect( + wrapper + .find(Window) + .find('[data-test-subj="indexPattern-dimension-window-row"]') + .first() + .prop('error') + ).toBe('Time range value is not valid.'); + }); + }); + describe('time shift', () => { function getProps(colOverrides: Partial) { return { @@ -1320,7 +1461,7 @@ describe('IndexPatternDimensionEditorPanel', () => { }} /> ); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); expect(wrapper.find('[data-test-subj="indexPattern-time-shift-enable"]')).toHaveLength(1); expect(wrapper.find(TimeShift)).toHaveLength(0); }); @@ -1347,7 +1488,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should allow to set time shift initially', () => { const props = getProps({}); wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); wrapper.find(TimeShift).find(EuiComboBox).prop('onChange')!([{ value: '1h', label: '' }]); expect((props.setState as jest.Mock).mock.calls[0][0](props.state)).toEqual({ ...props.state, @@ -1480,7 +1621,7 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should show custom options if filtering is available', () => { wrapper = mount(); - wrapper.find('[data-test-subj="indexPattern-advanced-accordion"]').first().simulate('click'); + findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); expect( wrapper.find('[data-test-subj="indexPattern-filter-by-enable"]').hostNodes() ).toHaveLength(1); @@ -1778,23 +1919,23 @@ describe('IndexPatternDimensionEditorPanel', () => { const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; - expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([ - 'Average', - 'Count', - 'Counter rate', - 'Cumulative sum', - 'Differences', - 'Last value', - 'Maximum', - 'Median', - 'Minimum', - 'Moving average', - 'Percentile', - 'Percentile rank', - 'Standard deviation', - 'Sum', - 'Unique count', - ' ', + expect(items.map(({ id }) => id)).toEqual([ + 'average', + 'count', + 'counter_rate', + 'cumulative_sum', + 'differences', + 'last_value', + 'max', + 'median', + 'min', + 'moving_average', + 'percentile', + 'percentile_rank', + 'standard_deviation', + 'sum', + 'unique_count', + undefined, ]); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx index f68bf30a32b69..f4471640a6520 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx @@ -15,7 +15,7 @@ import './dimension_editor.scss'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiTabs, EuiTab, EuiCallOut } from '@elastic/eui'; +import { EuiCallOut, EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { operationDefinitionMap } from '../operations'; export const formulaOperationName = 'formula'; @@ -112,7 +112,7 @@ export const CalloutWarning = ({ ); }; -export interface DimensionEditorTab { +export interface DimensionEditorGroupsOptions { enabled: boolean; state: boolean; onClick: () => void; @@ -120,25 +120,47 @@ export interface DimensionEditorTab { label: string; } -export const DimensionEditorTabs = ({ tabs }: { tabs: DimensionEditorTab[] }) => { +export const DimensionEditorButtonGroups = ({ + options, + onMethodChange, + selectedMethod, +}: { + options: DimensionEditorGroupsOptions[]; + onMethodChange: (id: string) => void; + selectedMethod: string; +}) => { + const enabledGroups = options.filter(({ enabled }) => enabled); + const groups = enabledGroups.map(({ id, label }) => { + return { + id, + label, + 'data-test-subj': `lens-dimensionTabs-${id}`, + }; + }); + + const onChange = (optionId: string) => { + onMethodChange(optionId); + const selectedOption = options.find(({ id }) => id === optionId); + selectedOption?.onClick(); + }; + return ( - - {tabs.map(({ id, enabled, state, onClick, label }) => { - return enabled ? ( - - {label} - - ) : null; + + fullWidth + > + onChange(id)} + /> + ); }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx index 0bc9b26dc1a5a..4383f7a7c68de 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx @@ -38,7 +38,9 @@ export function setTimeScaling( currentColumn.timeScale, timeScale, currentColumn.timeShift, - currentColumn.timeShift + currentColumn.timeShift, + currentColumn.window, + currentColumn.window ); return { ...layer, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx index 6620a2259f3ac..35d2bd55ff10e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx @@ -45,7 +45,9 @@ export function setTimeShift( currentColumn.timeScale, currentColumn.timeScale, currentColumn.timeShift, - trimmedTimeShift + trimmedTimeShift, + currentColumn.window, + currentColumn.window ); return { ...layer, @@ -108,13 +110,16 @@ export function TimeShift({ const localValueNotMultiple = parsedLocalValue && isValueNotMultiple(parsedLocalValue); function getSelectedOption() { - if (!localValue) return []; const goodPick = timeShiftOptions.filter(({ value }) => value === localValue); if (goodPick.length > 0) return goodPick; return [ { - value: localValue, - label: localValue, + value: localValue ?? '', + label: + localValue ?? + i18n.translate('xpack.lens.timeShift.none', { + defaultMessage: 'None', + }), }, ]; } @@ -180,7 +185,7 @@ export function TimeShift({ } }} onChange={(choices) => { - if (choices.length === 0) { + if (choices.length === 0 || (choices.length && choices[0].value === '')) { updateLayer(setTimeShift(columnId, layer, '')); setLocalValue(''); return; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/window.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/window.tsx new file mode 100644 index 0000000000000..c0d561d694274 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/window.tsx @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFormRow, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { EuiComboBox } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect, useState } from 'react'; + +import { parseTimeShift } from '@kbn/data-plugin/common'; +import { Duration } from 'moment'; +import { + adjustTimeScaleLabelSuffix, + GenericIndexPatternColumn, + operationDefinitionMap, +} from '../operations'; +import { IndexPattern, IndexPatternLayer } from '../types'; +import { windowOptions } from '../window_utils'; + +export function setWindow(columnId: string, layer: IndexPatternLayer, window: string | undefined) { + const trimmedWindow = window?.trim(); + const currentColumn = layer.columns[columnId]; + const label = currentColumn.customLabel + ? currentColumn.label + : adjustTimeScaleLabelSuffix( + currentColumn.label, + currentColumn.timeScale, + currentColumn.timeScale, + currentColumn.timeShift, + currentColumn.timeShift, + currentColumn.window, + trimmedWindow + ); + return { + ...layer, + columns: { + ...layer.columns, + [columnId]: { + ...layer.columns[columnId], + window: trimmedWindow, + label, + }, + }, + }; +} + +function isInvalid(value: Duration | 'previous' | 'invalid') { + return value === 'previous' || value === 'invalid'; +} + +export function Window({ + selectedColumn, + columnId, + layer, + updateLayer, + indexPattern, +}: { + selectedColumn: GenericIndexPatternColumn; + columnId: string; + layer: IndexPatternLayer; + updateLayer: (newLayer: IndexPatternLayer) => void; + indexPattern: IndexPattern; +}) { + const [localValue, setLocalValue] = useState(selectedColumn.window); + useEffect(() => { + setLocalValue(selectedColumn.window); + }, [selectedColumn.window]); + const selectedOperation = operationDefinitionMap[selectedColumn.operationType]; + const hasDateHistogram = Object.values(layer.columns).some( + (c) => c.operationType === 'date_histogram' + ); + if ( + !selectedOperation.windowable || + (!selectedColumn.window && (hasDateHistogram || !indexPattern.timeFieldName)) + ) { + return null; + } + + const parsedLocalValue = localValue && parseTimeShift(localValue); + const isLocalValueInvalid = Boolean(parsedLocalValue && isInvalid(parsedLocalValue)); + const shouldNotUseWindow = Boolean(hasDateHistogram || !indexPattern.timeFieldName); + + function getSelectedOption() { + if (!localValue) return []; + const goodPick = windowOptions.filter(({ value }) => value === localValue); + if (goodPick.length > 0) return goodPick; + return [ + { + value: localValue, + label: localValue, + }, + ]; + } + + return ( +
+ + + + { + const parsedVal = parseTimeShift(val); + if (!isInvalid(parsedVal)) { + updateLayer(setWindow(columnId, layer, val)); + } else { + setLocalValue(val); + } + }} + onChange={(choices) => { + if (choices.length === 0) { + updateLayer(setWindow(columnId, layer, '')); + setLocalValue(''); + return; + } + + const choice = choices[0].value as string; + const parsedVal = parseTimeShift(choice); + if (!isInvalid(parsedVal)) { + updateLayer(setWindow(columnId, layer, choice)); + } else { + setLocalValue(choice); + } + }} + /> + + + +
+ ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index 5bae2f5a1865f..b9998e943c515 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -20,7 +20,15 @@ export const buildLabelFunction = (ofName: (name?: string) => string) => (name?: string, timeScale?: TimeScaleUnit, timeShift?: string) => { const rawLabel = ofName(name); - return adjustTimeScaleLabelSuffix(rawLabel, undefined, timeScale, undefined, timeShift); + return adjustTimeScaleLabelSuffix( + rawLabel, + undefined, + timeScale, + undefined, + timeShift, + undefined, + undefined + ); }; export function checkForDataLayerType(layerType: LayerType, name: string) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index 017a3580824f8..dbbbb2da80e08 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { EuiSwitch } from '@elastic/eui'; +import { EuiSwitch, EuiText } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import { AggFunctionsMapping } from '@kbn/data-plugin/public'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; @@ -25,6 +25,7 @@ import { import { adjustTimeScaleLabelSuffix } from '../time_scale_utils'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { updateColumnParam } from '../layer_helpers'; +import { getColumnWindowError } from '../../window_utils'; const supportedTypes = new Set([ 'string', @@ -42,7 +43,7 @@ const SCALE = 'ratio'; const OPERATION_TYPE = 'unique_count'; const IS_BUCKETED = false; -function ofName(name: string, timeShift: string | undefined) { +function ofName(name: string, timeShift: string | undefined, window: string | undefined) { return adjustTimeScaleLabelSuffix( i18n.translate('xpack.lens.indexPattern.cardinalityOf', { defaultMessage: 'Unique count of {name}', @@ -53,7 +54,9 @@ function ofName(name: string, timeShift: string | undefined) { undefined, undefined, undefined, - timeShift + timeShift, + undefined, + window ); } @@ -90,6 +93,7 @@ export const cardinalityOperation: OperationDefinition< combineErrorMessages([ getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), getDisallowedPreviousShiftMessage(layer, columnId), + getColumnWindowError(layer, columnId, indexPattern), ]), isTransferable: (column, newIndexPattern) => { const newField = newIndexPattern.getFieldByName(column.sourceField); @@ -103,11 +107,12 @@ export const cardinalityOperation: OperationDefinition< }, filterable: true, shiftable: true, + windowable: true, getDefaultLabel: (column, indexPattern) => - ofName(getSafeName(column.sourceField, indexPattern), column.timeShift), + ofName(getSafeName(column.sourceField, indexPattern), column.timeShift, column.window), buildColumn({ field, previousColumn }, columnParams) { return { - label: ofName(field.displayName, previousColumn?.timeShift), + label: ofName(field.displayName, previousColumn?.timeShift, previousColumn?.window), dataType: 'number', operationType: OPERATION_TYPE, scale: SCALE, @@ -115,6 +120,7 @@ export const cardinalityOperation: OperationDefinition< isBucketed: IS_BUCKETED, filter: getFilter(previousColumn, columnParams), timeShift: columnParams?.shift || previousColumn?.timeShift, + window: columnParams?.window || previousColumn?.window, params: { ...getFormatFromPreviousColumn(previousColumn), emptyAsNull: @@ -136,9 +142,13 @@ export const cardinalityOperation: OperationDefinition< dataTestSubj: 'hide-zero-values', inlineElement: ( + {i18n.translate('xpack.lens.indexPattern.hideZero', { + defaultMessage: 'Hide zero values', + })} + + } labelProps={{ style: { fontWeight: euiThemeVars.euiFontWeightMedium, @@ -175,7 +185,7 @@ export const cardinalityOperation: OperationDefinition< onFieldChange: (oldColumn, field) => { return { ...oldColumn, - label: ofName(field.displayName, oldColumn.timeShift), + label: ofName(field.displayName, oldColumn.timeShift, oldColumn.window), sourceField: field.name, }; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts index 47222b51fc012..e8058cd9ecda9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts @@ -16,6 +16,7 @@ export interface BaseIndexPatternColumn extends Operation { customLabel?: boolean; timeScale?: TimeScaleUnit; filter?: Query; + window?: string; timeShift?: string; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index b7d806807b6b7..bf61755e79f4c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { EuiSwitch } from '@elastic/eui'; +import { EuiSwitch, EuiText } from '@elastic/eui'; import { AggFunctionsMapping } from '@kbn/data-plugin/public'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; import { TimeScaleUnit } from '../../../../common/expressions'; @@ -28,6 +28,7 @@ import { } from '../time_scale_utils'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { updateColumnParam } from '../layer_helpers'; +import { getColumnWindowError } from '../../window_utils'; const countLabel = i18n.translate('xpack.lens.indexPattern.countOf', { defaultMessage: 'Count of records', @@ -48,7 +49,8 @@ const supportedTypes = new Set([ function ofName( field: IndexPatternField | undefined, timeShift: string | undefined, - timeScale: string | undefined + timeScale: string | undefined, + window: string | undefined ) { return adjustTimeScaleLabelSuffix( field?.type !== 'document' @@ -62,7 +64,9 @@ function ofName( undefined, timeScale as TimeScaleUnit, undefined, - timeShift + timeShift, + undefined, + window ); } @@ -87,12 +91,13 @@ export const countOperation: OperationDefinition { return { ...oldColumn, - label: ofName(field, oldColumn.timeShift, oldColumn.timeShift), + label: ofName(field, oldColumn.timeShift, oldColumn.timeShift, oldColumn.window), sourceField: field.name, }; }, @@ -108,11 +113,16 @@ export const countOperation: OperationDefinition { const field = indexPattern.getFieldByName(column.sourceField); - return ofName(field, column.timeShift, column.timeScale); + return ofName(field, column.timeShift, column.timeScale, column.window); }, buildColumn({ field, previousColumn }, columnParams) { return { - label: ofName(field, previousColumn?.timeShift, previousColumn?.timeScale), + label: ofName( + field, + previousColumn?.timeShift, + previousColumn?.timeScale, + previousColumn?.window + ), dataType: 'number', operationType: 'count', isBucketed: false, @@ -121,6 +131,7 @@ export const countOperation: OperationDefinition + {i18n.translate('xpack.lens.indexPattern.hideZero', { + defaultMessage: 'Hide zero values', + })} + + } labelProps={{ style: { fontWeight: euiThemeVars.euiFontWeightMedium, @@ -204,6 +219,7 @@ export const countOperation: OperationDefinition { paramEditorUpdater={updateLayerSpy} columnId="col1" currentColumn={thirdLayer.columns.col1 as DateHistogramIndexPatternColumn} + indexPattern={{ ...indexPattern1, timeFieldName: '@timestamp' }} /> ); instance @@ -558,7 +559,9 @@ describe('date_histogram', () => { indexPattern={{ ...indexPattern1, timeFieldName: 'other_timestamp' }} /> ); - expect(instance.find(EuiSwitch).first().prop('disabled')).toBeTruthy(); + expect( + instance.find('[data-test-subj="lensDropPartialIntervals"]').prop('disabled') + ).toBeTruthy(); }); it('should force calendar values to 1', () => { @@ -754,12 +757,9 @@ describe('date_histogram', () => { currentColumn={thirdLayer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - instance - .find(EuiSwitch) - .first() - .simulate('change', { - target: { checked: true }, - }); + instance.find('[data-test-subj="lensDropPartialIntervals"]').simulate('change', { + target: { checked: true }, + }); expect(updateLayerSpy).toHaveBeenCalled(); const newLayer = updateLayerSpy.mock.calls[0][0](layer); expect(newLayer).toHaveProperty('columns.col1.params.dropPartials', true); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index 5ee246f09c2e5..50eb6df5723f7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -17,6 +17,8 @@ import { EuiIconTip, EuiSwitch, EuiSwitchEvent, + EuiSpacer, + EuiText, } from '@elastic/eui'; import { AggFunctionsMapping, @@ -264,38 +266,115 @@ export const dateHistogramOperation: OperationDefinition< return ( <> + - - - + + {i18n.translate('xpack.lens.indexPattern.dateHistogram.includeEmptyRows', { + defaultMessage: 'Include empty rows', + })} + + } + checked={Boolean(currentColumn.params.includeEmptyRows)} + data-test-subj="indexPattern-include-empty-rows" + onChange={() => { + paramEditorUpdater( + updateColumnParam({ + layer, + columnId, + paramName: 'includeEmptyRows', + value: !currentColumn.params.includeEmptyRows, + }) + ); + }} + compressed + /> + {indexPattern.timeFieldName !== field?.name && ( + <> + + + + {i18n.translate( + 'xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker', + { + defaultMessage: 'Bind to global time picker', + } + )}{' '} + + + } + disabled={indexPattern.timeFieldName === field?.name} + checked={bindToGlobalTimePickerValue} + onChange={() => { + let newLayer = updateColumnParam({ + layer, + columnId, + paramName: 'ignoreTimeRange', + value: !currentColumn.params.ignoreTimeRange, + }); + if ( + !currentColumn.params.ignoreTimeRange && + currentColumn.params.interval === autoInterval + ) { + const newFixedInterval = + data.search.aggs.calculateAutoTimeExpression({ + from: dateRange.fromDate, + to: dateRange.toDate, + }) || '1h'; + newLayer = updateColumnParam({ + layer: newLayer, + columnId, + paramName: 'interval', + value: newFixedInterval, + }); + setIntervalInput(newFixedInterval); + } + paramEditorUpdater(newLayer); + }} + compressed + /> + + + )} + {i18n.translate('xpack.lens.indexPattern.dateHistogram.selectOptionHelpText', { + defaultMessage: `Select an option or create a custom value.`, + })} +
+ {i18n.translate( + 'xpack.lens.indexPattern.dateHistogram.selectOptionExamplesHelpText', + { + defaultMessage: `Examples: 30s, 20m, 24h, 2d, 1w, 1M`, + } + )} + + } isInvalid={!isValid} error={ !isValid && @@ -346,81 +425,33 @@ export const dateHistogramOperation: OperationDefinition< /> )}
+ - - {i18n.translate('xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker', { - defaultMessage: 'Bind to global time picker', - })}{' '} - - - } - disabled={indexPattern.timeFieldName === field?.name} - checked={bindToGlobalTimePickerValue} - onChange={() => { - let newLayer = updateColumnParam({ - layer, - columnId, - paramName: 'ignoreTimeRange', - value: !currentColumn.params.ignoreTimeRange, - }); - if ( - !currentColumn.params.ignoreTimeRange && - currentColumn.params.interval === autoInterval - ) { - const newFixedInterval = - data.search.aggs.calculateAutoTimeExpression({ - from: dateRange.fromDate, - to: dateRange.toDate, - }) || '1h'; - newLayer = updateColumnParam({ - layer: newLayer, - columnId, - paramName: 'interval', - value: newFixedInterval, - }); - setIntervalInput(newFixedInterval); + - - - { - paramEditorUpdater( - updateColumnParam({ - layer, - columnId, - paramName: 'includeEmptyRows', - value: !currentColumn.params.includeEmptyRows, - }) - ); - }} - compressed - /> + )} + condition={!bindToGlobalTimePickerValue} + > + + {i18n.translate('xpack.lens.indexPattern.dateHistogram.dropPartialBuckets', { + defaultMessage: 'Drop partial intervals', + })} + + } + data-test-subj="lensDropPartialIntervals" + checked={Boolean(currentColumn.params.dropPartials)} + onChange={onChangeDropPartialBuckets} + compressed + disabled={!bindToGlobalTimePickerValue} + /> + ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss index 92a778ebfb803..5ce35d945dd07 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss @@ -30,13 +30,15 @@ .lnsFormula__editorHeader, .lnsFormula__editorFooter { - padding: $euiSizeS $euiSize; + padding: $euiSizeS; } .lnsFormula__editorFooter { // make sure docs are rendered in front of monaco z-index: 1; background-color: $euiColorLightestShade; + border-bottom-right-radius: $euiBorderRadius; + border-bottom-left-radius: $euiBorderRadius; } .lnsFormula__editorHeaderGroup, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx index de3987287e902..f0aebbf3bfa69 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx @@ -7,9 +7,11 @@ import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; import { EuiButtonIcon, EuiButtonEmpty, + EuiFormLabel, EuiFlexGroup, EuiFlexItem, EuiIcon, @@ -18,6 +20,7 @@ import { EuiText, EuiToolTip, EuiSpacer, + useEuiTheme, } from '@elastic/eui'; import useUnmount from 'react-use/lib/useUnmount'; import { monaco } from '@kbn/monaco'; @@ -112,6 +115,8 @@ export function FormulaEditor({ const disposables = React.useRef([]); const editor1 = React.useRef(); + const { euiTheme } = useEuiTheme(); + const visibleOperationsMap = useMemo( () => filterByVisibleOperation(operationDefinitionMap), [operationDefinitionMap] @@ -534,7 +539,8 @@ export function FormulaEditor({ tokenInfo.ast.type !== 'namedArgument' || (tokenInfo.ast.name !== 'kql' && tokenInfo.ast.name !== 'lucene' && - tokenInfo.ast.name !== 'shift') || + tokenInfo.ast.name !== 'shift' && + tokenInfo.ast.name !== 'timeRange') || (tokenInfo.ast.value !== 'LENS_MATH_MARKER' && !isSingleQuoteCase.test(tokenInfo.ast.value)) ) { @@ -554,7 +560,11 @@ export function FormulaEditor({ text: `''`, }; } - if (char === "'" && tokenInfo.ast.name !== 'shift') { + if ( + char === "'" && + tokenInfo.ast.name !== 'shift' && + tokenInfo.ast.name !== 'timeRange' + ) { editOperation = { range: { ...currentPosition, @@ -651,7 +661,26 @@ export function FormulaEditor({ 'lnsIndexPatternDimensionEditor-isFullscreen': isFullscreen, })} > -
+ {!isFullscreen && ( + + {i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingFormula', { + defaultMessage: 'Formula', + })} + + )} +
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx index bdc774f9d15f3..7a6d4c051c2a2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx @@ -146,6 +146,32 @@ sum(products.base_price) / overall_sum(sum(products.base_price)) \`\`\` `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate('xpack.lens.formulaDocumentation.recentChange', { + defaultMessage: 'Recent change', + }), + description: ( + arg.name === 'timeRange')) { + list.push('timeRange'); + } + } if ('operationParams' in operation) { // Exclude any previously used named args list.push( @@ -362,6 +369,12 @@ export async function getNamedArgumentSuggestions({ type: SUGGESTION_TYPE.SHIFTS, }; } + if (ast.name === 'timeRange') { + return { + list: windowOptions.map(({ value }) => value), + type: SUGGESTION_TYPE.WINDOWS, + }; + } if (ast.name !== 'kql' && ast.name !== 'lucene') { return { list: [], type: SUGGESTION_TYPE.KQL }; } @@ -416,6 +429,9 @@ export function getSuggestion( case SUGGESTION_TYPE.SHIFTS: sortText = String(timeShiftOptionOrder[label]).padStart(4, '0'); break; + case SUGGESTION_TYPE.WINDOWS: + sortText = String(windowOptionOrder[label]).padStart(4, '0'); + break; case SUGGESTION_TYPE.FIELD: kind = monaco.languages.CompletionItemKind.Value; // Look for unsafe characters @@ -444,7 +460,7 @@ export function getSuggestion( break; case SUGGESTION_TYPE.NAMED_ARGUMENT: kind = monaco.languages.CompletionItemKind.Keyword; - if (label === 'kql' || label === 'lucene' || label === 'shift') { + if (label === 'kql' || label === 'lucene' || label === 'shift' || label === 'timeRange') { command = TRIGGER_SUGGESTION_COMMAND; insertText = `${label}='$0'`; insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts index 01bae7d7511b0..fc8357db87b7c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts @@ -87,6 +87,16 @@ export function generateFormula( } previousFormula += `shift='${previousColumn.timeShift}'`; } + if (previousColumn.window) { + if ( + previousColumn.operationType !== 'count' || + previousColumn.filter || + previousColumn.timeShift + ) { + previousFormula += ', '; + } + previousFormula += `timeRange='${previousColumn.window}'`; + } if (previousFormula) { // close the formula at the end previousFormula += ')'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts index ca6e5cd971349..dff2f91b5bd93 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts @@ -95,6 +95,9 @@ export function getOperationParams( if (operation.shiftable && name === 'shift') { args[name] = value; } + if (operation.windowable && name === 'timeRange') { + args.window = value; + } return args; }, {}); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts index 28e015e4fc0b5..edb95ad8d633c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts @@ -500,6 +500,19 @@ function getQueryValidationErrors( }); } } + + if (arg.name === 'timeRange') { + const parsedWindow = parseTimeShift(arg.value || ''); + if (parsedWindow === 'invalid' || parsedWindow === 'previous') { + errors.push({ + message: i18n.translate('xpack.lens.indexPattern.invalidWindow', { + defaultMessage: + 'Invalid window interval. Enter positive integer amount followed by one of the units s, m, h, d, w, M, y. For example 3h for 3 hours', + }), + locations: [arg.location], + }); + } + } }); return errors; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 2cbe6f5e0c827..931b753d5a94b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -344,6 +344,10 @@ interface BaseOperationDefinitionProps< * autocomplete. */ filterable?: boolean | { helpMessage: string }; + /** + * Windowable operations can have a time window defined at the dimension level - under the hood this will be translated into a filter on the defined time field + */ + windowable?: boolean; shiftable?: boolean; getHelpMessage?: (props: HelpProps) => React.ReactNode; @@ -492,6 +496,7 @@ interface FieldBasedOperationDefinition C; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 0af5ed4428ef7..52e827a54f755 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -14,6 +14,7 @@ import { EuiComboBoxOptionOption, EuiSwitch, EuiToolTip, + EuiText, } from '@elastic/eui'; import { AggFunctionsMapping } from '@kbn/data-plugin/public'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; @@ -31,8 +32,9 @@ import { adjustTimeScaleLabelSuffix } from '../time_scale_utils'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { isScriptedField } from './terms/helpers'; import { FormRow } from './shared_components/form_row'; +import { getColumnWindowError } from '../../window_utils'; -function ofName(name: string, timeShift: string | undefined) { +function ofName(name: string, timeShift: string | undefined, window: string | undefined) { return adjustTimeScaleLabelSuffix( i18n.translate('xpack.lens.indexPattern.lastValueOf', { defaultMessage: 'Last value of {name}', @@ -43,7 +45,9 @@ function ofName(name: string, timeShift: string | undefined) { undefined, undefined, undefined, - timeShift + timeShift, + undefined, + window ); } @@ -130,7 +134,7 @@ export const lastValueOperation: OperationDefinition< defaultMessage: 'Last value', }), getDefaultLabel: (column, indexPattern) => - ofName(getSafeName(column.sourceField, indexPattern), column.timeShift), + ofName(getSafeName(column.sourceField, indexPattern), column.timeShift, column.window), input: 'field', onFieldChange: (oldColumn, field) => { const newParams = { ...oldColumn.params }; @@ -143,7 +147,7 @@ export const lastValueOperation: OperationDefinition< return { ...oldColumn, dataType: field.type as DataType, - label: ofName(field.displayName, oldColumn.timeShift), + label: ofName(field.displayName, oldColumn.timeShift, oldColumn.window), sourceField: field.name, params: newParams, scale: field.type === 'string' ? 'ordinal' : 'ratio', @@ -185,6 +189,7 @@ export const lastValueOperation: OperationDefinition< errorMessages = [invalidSortFieldMessage]; } errorMessages.push(...(getDisallowedPreviousShiftMessage(layer, columnId) || [])); + errorMessages.push(...(getColumnWindowError(layer, columnId, indexPattern) || [])); return errorMessages.length ? errorMessages : undefined; }, buildColumn({ field, previousColumn, indexPattern }, columnParams) { @@ -204,7 +209,7 @@ export const lastValueOperation: OperationDefinition< const showArrayValues = isScriptedField(field) || lastValueParams?.showArrayValues; return { - label: ofName(field.displayName, previousColumn?.timeShift), + label: ofName(field.displayName, previousColumn?.timeShift, previousColumn?.window), dataType: field.type as DataType, operationType: 'last_value', isBucketed: false, @@ -212,6 +217,7 @@ export const lastValueOperation: OperationDefinition< sourceField: field.name, filter: getFilter(previousColumn, columnParams) || getExistsFilter(field.name), timeShift: columnParams?.shift || previousColumn?.timeShift, + window: columnParams?.window || previousColumn?.window, params: { showArrayValues, sortField: lastValueParams?.sortField || sortField, @@ -220,6 +226,7 @@ export const lastValueOperation: OperationDefinition< }; }, filterable: true, + windowable: true, shiftable: true, toEsAggsFn: (column, columnId, indexPattern) => { const initialArgs = { @@ -296,6 +303,46 @@ export const lastValueOperation: OperationDefinition< return ( <> + {!isReferenced && ( + + + + {i18n.translate('xpack.lens.indexPattern.lastValue.showArrayValues', { + defaultMessage: 'Show array values', + })} + + } + compressed={true} + checked={Boolean(currentColumn.params.showArrayValues)} + disabled={isScriptedField(currentColumn.sourceField, indexPattern)} + onChange={() => setShowArrayValues(!currentColumn.params.showArrayValues)} + /> + + + )} - {!isReferenced && ( - - - setShowArrayValues(!currentColumn.params.showArrayValues)} - /> - - - )} ); }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index 5f3973b84e8fd..1942095ffa7ce 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { EuiSwitch } from '@elastic/eui'; +import { EuiSwitch, EuiText } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; import { OperationDefinition, ParamEditorProps } from '.'; @@ -30,6 +30,7 @@ import { } from '../time_scale_utils'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { updateColumnParam } from '../layer_helpers'; +import { getColumnWindowError } from '../../window_utils'; type MetricColumn = FieldBasedIndexPatternColumn & { operationType: T; @@ -80,7 +81,9 @@ function buildMetricOperation>({ undefined, optionalTimeScaling ? column?.timeScale : undefined, undefined, - column?.timeShift + column?.timeShift, + undefined, + column?.window ); }; @@ -131,6 +134,7 @@ function buildMetricOperation>({ timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined, filter: getFilter(previousColumn, columnParams), timeShift: columnParams?.shift || previousColumn?.timeShift, + window: columnParams?.window || previousColumn?.window, params: { ...getFormatFromPreviousColumn(previousColumn), emptyAsNull: @@ -160,9 +164,13 @@ function buildMetricOperation>({ dataTestSubj: 'hide-zero-values', inlineElement: ( + {i18n.translate('xpack.lens.indexPattern.hideZero', { + defaultMessage: 'Hide zero values', + })} + + } labelProps={{ style: { fontWeight: euiThemeVars.euiFontWeightMedium, @@ -204,8 +212,10 @@ function buildMetricOperation>({ indexPattern ), getDisallowedPreviousShiftMessage(layer, columnId), + getColumnWindowError(layer, columnId, indexPattern), ]), filterable: true, + windowable: true, documentation: { section: 'elasticsearch', signature: i18n.translate('xpack.lens.indexPattern.metric.signature', { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx index c7e028dfaea7e..a0eb542449813 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx @@ -30,6 +30,7 @@ import { adjustTimeScaleLabelSuffix } from '../time_scale_utils'; import { useDebouncedValue } from '../../../shared_components'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { FormRow } from './shared_components'; +import { getColumnWindowError } from '../../window_utils'; export interface PercentileIndexPatternColumn extends FieldBasedIndexPatternColumn { operationType: 'percentile'; @@ -44,7 +45,12 @@ export interface PercentileIndexPatternColumn extends FieldBasedIndexPatternColu }; } -function ofName(name: string, percentile: number, timeShift: string | undefined) { +function ofName( + name: string, + percentile: number, + timeShift: string | undefined, + window: string | undefined +) { return adjustTimeScaleLabelSuffix( i18n.translate('xpack.lens.indexPattern.percentileOf', { defaultMessage: @@ -54,7 +60,9 @@ function ofName(name: string, percentile: number, timeShift: string | undefined) undefined, undefined, undefined, - timeShift + timeShift, + undefined, + window ); } @@ -79,6 +87,7 @@ export const percentileOperation: OperationDefinition< ], filterable: true, shiftable: true, + windowable: true, getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { if (supportedFieldTypes.includes(fieldType) && aggregatable && !aggregationRestrictions) { return { @@ -102,7 +111,8 @@ export const percentileOperation: OperationDefinition< ofName( getSafeName(column.sourceField, indexPattern), column.params.percentile, - column.timeShift + column.timeShift, + column.window ), buildColumn: ({ field, previousColumn, indexPattern }, columnParams) => { const existingPercentileParam = @@ -115,7 +125,8 @@ export const percentileOperation: OperationDefinition< label: ofName( getSafeName(field.name, indexPattern), newPercentileParam, - previousColumn?.timeShift + previousColumn?.timeShift, + previousColumn?.window ), dataType: 'number', operationType: 'percentile', @@ -124,6 +135,7 @@ export const percentileOperation: OperationDefinition< scale: 'ratio', filter: getFilter(previousColumn, columnParams), timeShift: columnParams?.shift || previousColumn?.timeShift, + window: columnParams?.window || previousColumn?.window, params: { percentile: newPercentileParam, ...getFormatFromPreviousColumn(previousColumn), @@ -133,7 +145,12 @@ export const percentileOperation: OperationDefinition< onFieldChange: (oldColumn, field) => { return { ...oldColumn, - label: ofName(field.displayName, oldColumn.params.percentile, oldColumn.timeShift), + label: ofName( + field.displayName, + oldColumn.params.percentile, + oldColumn.timeShift, + oldColumn.window + ), sourceField: field.name, }; }, @@ -269,6 +286,7 @@ export const percentileOperation: OperationDefinition< combineErrorMessages([ getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), getDisallowedPreviousShiftMessage(layer, columnId), + getColumnWindowError(layer, columnId, indexPattern), ]), paramEditor: function PercentileParamEditor({ paramEditorUpdater, @@ -298,7 +316,8 @@ export const percentileOperation: OperationDefinition< indexPattern.getFieldByName(currentColumn.sourceField)?.displayName || currentColumn.sourceField, Number(value), - currentColumn.timeShift + currentColumn.timeShift, + currentColumn.window ), params: { ...currentColumn.params, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx index 61a7a33cdbd73..9541665195689 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx @@ -25,6 +25,7 @@ import { adjustTimeScaleLabelSuffix } from '../time_scale_utils'; import { useDebouncedValue } from '../../../shared_components'; import { getDisallowedPreviousShiftMessage } from '../../time_shift_utils'; import { FormRow } from './shared_components'; +import { getColumnWindowError } from '../../window_utils'; export interface PercentileRanksIndexPatternColumn extends FieldBasedIndexPatternColumn { operationType: 'percentile_rank'; @@ -33,7 +34,12 @@ export interface PercentileRanksIndexPatternColumn extends FieldBasedIndexPatter }; } -function ofName(name: string, value: number, timeShift: string | undefined) { +function ofName( + name: string, + value: number, + timeShift: string | undefined, + window: string | undefined +) { return adjustTimeScaleLabelSuffix( i18n.translate('xpack.lens.indexPattern.percentileRanksOf', { defaultMessage: 'Percentile rank ({value}) of {name}', @@ -42,7 +48,9 @@ function ofName(name: string, value: number, timeShift: string | undefined) { undefined, undefined, undefined, - timeShift + timeShift, + undefined, + window ); } @@ -72,6 +80,7 @@ export const percentileRanksOperation: OperationDefinition< ], filterable: true, shiftable: true, + windowable: true, getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { if (supportedFieldTypes.includes(fieldType) && aggregatable && !aggregationRestrictions) { return { @@ -92,7 +101,12 @@ export const percentileRanksOperation: OperationDefinition< ); }, getDefaultLabel: (column, indexPattern, columns) => - ofName(getSafeName(column.sourceField, indexPattern), column.params.value, column.timeShift), + ofName( + getSafeName(column.sourceField, indexPattern), + column.params.value, + column.timeShift, + column.window + ), buildColumn: ({ field, previousColumn, indexPattern }, columnParams) => { const existingPercentileRanksParam = previousColumn && @@ -104,7 +118,8 @@ export const percentileRanksOperation: OperationDefinition< label: ofName( getSafeName(field.name, indexPattern), newPercentileRanksParam, - previousColumn?.timeShift + previousColumn?.timeShift, + previousColumn?.window ), dataType: 'number', operationType: 'percentile_rank', @@ -113,6 +128,7 @@ export const percentileRanksOperation: OperationDefinition< scale: 'ratio', filter: getFilter(previousColumn, columnParams), timeShift: columnParams?.shift || previousColumn?.timeShift, + window: columnParams?.window || previousColumn?.window, params: { value: newPercentileRanksParam, ...getFormatFromPreviousColumn(previousColumn), @@ -122,7 +138,12 @@ export const percentileRanksOperation: OperationDefinition< onFieldChange: (oldColumn, field) => { return { ...oldColumn, - label: ofName(field.displayName, oldColumn.params.value, oldColumn.timeShift), + label: ofName( + field.displayName, + oldColumn.params.value, + oldColumn.timeShift, + oldColumn.window + ), sourceField: field.name, }; }, @@ -144,6 +165,7 @@ export const percentileRanksOperation: OperationDefinition< combineErrorMessages([ getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), getDisallowedPreviousShiftMessage(layer, columnId), + getColumnWindowError(layer, columnId, indexPattern), ]), paramEditor: function PercentileParamEditor({ paramEditorUpdater, @@ -170,7 +192,8 @@ export const percentileRanksOperation: OperationDefinition< indexPattern.getFieldByName(currentColumn.sourceField)?.displayName || currentColumn.sourceField, Number(value), - currentColumn.timeShift + currentColumn.timeShift, + currentColumn.window ), params: { ...currentColumn.params, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx index e7cf46e1e3e81..92f0196b9cbb5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx @@ -19,6 +19,7 @@ import { EuiToolTip, EuiSwitch, EuiSpacer, + EuiText, } from '@elastic/eui'; import type { IFieldFormat } from '@kbn/field-formats-plugin/common'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; @@ -179,9 +180,13 @@ const BaseRangeEditor = ({ + {i18n.translate('xpack.lens.indexPattern.ranges.includeEmptyRows', { + defaultMessage: 'Include empty rows', + })} + + } checked={Boolean(includeEmptyRows)} onChange={() => { onChangeIncludeEmptyRows(!includeEmptyRows); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx index 0f4ba342348cf..4939c03fb7a19 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx @@ -37,6 +37,7 @@ export const NewBucketButton = ({ iconType="plusInCircle" onClick={onClick} isDisabled={isDisabled} + flush="left" > {label} @@ -131,12 +132,14 @@ export const DragDropBuckets = ({ onDragEnd, droppableId, children, + className, }: { items: any; // eslint-disable-line @typescript-eslint/no-explicit-any onDragStart: () => void; onDragEnd: (items: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any droppableId: string; children: React.ReactElement[]; + className?: string; }) => { const handleDragEnd = ({ source, @@ -152,7 +155,7 @@ export const DragDropBuckets = ({ }; return ( - + {children} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx index bb9b36a3d097b..f2359d567052f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFieldNumber, EuiFormLabel, EuiSpacer } from '@elastic/eui'; +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; import { OperationDefinition } from '.'; import { ReferenceBasedIndexPatternColumn, @@ -215,9 +215,7 @@ export const staticValueOperation: OperationDefinition< ); return ( -
- {paramEditorCustomProps?.labels?.[0] || defaultLabel} - + -
+
); }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx index 6381b2843cf2c..7fba5b65c9ac3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiButtonIcon, EuiDraggable, @@ -13,6 +13,8 @@ import { EuiFlexItem, EuiIcon, htmlIdGenerator, + EuiPanel, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DragDropBuckets, NewBucketButton } from '../shared_components/buckets'; @@ -55,6 +57,9 @@ export function FieldInputs({ operationSupportMatrix, invalidFields, }: FieldInputsProps) { + const { euiTheme } = useEuiTheme(); + const [isDragging, setIsDragging] = useState(false); + const onChangeWrapped = useCallback( (values: WrappedValue[]) => onChange(values.filter(removeNewEmptyField).map(({ value }) => value)), @@ -87,152 +92,155 @@ export function FieldInputs({ [localValues, indexPattern, handleInputChange] ); - // diminish attention to adding fields alternative - if (localValues.length === 1) { - const [{ value }] = localValues; - return ( - <> - - { - handleInputChange([ - ...localValues, - { id: generateId(), value: undefined, isNew: true }, - ]); - }} - label={i18n.translate('xpack.lens.indexPattern.terms.addField', { - defaultMessage: 'Add field', - })} - isDisabled={column.params.orderBy.type === 'rare'} - /> - - ); - } - const disableActions = localValues.length === 2 && localValues.some(({ isNew }) => isNew); + const disableActions = + (localValues.length === 2 && localValues.some(({ isNew }) => isNew)) || + localValues.length === 1; const localValuesFilled = localValues.filter(({ isNew }) => !isNew); return ( <> - { - handleInputChange(updatedValues); +
{}} - droppableId="TOP_TERMS_DROPPABLE_AREA" - items={localValues} > - {localValues.map(({ id, value, isNew }, index) => { - // need to filter the available fields for multiple terms - // * a scripted field should be removed - // * a field of unsupported type should be removed - // * a field that has been used - // * a scripted field was used in a singular term, should be marked as invalid for multi-terms - const filteredOperationByField = Object.keys(operationSupportMatrix.operationByField) - .filter((key) => { - if (key === value) { - return true; - } - const field = indexPattern.getFieldByName(key); - return ( - !rawValuesLookup.has(key) && - field && - !field.scripted && - supportedTypes.has(field.type) - ); - }) - .reduce((memo, key) => { - memo[key] = operationSupportMatrix.operationByField[key]; - return memo; - }, {}); + { + handleInputChange(updatedValues); + setIsDragging(false); + }} + className="lnsIndexPatternDimensionEditor__droppable" + onDragStart={() => { + setIsDragging(true); + }} + droppableId="TOP_TERMS_DROPPABLE_AREA" + items={localValues} + > + {localValues.map(({ id, value, isNew }, index) => { + // need to filter the available fields for multiple terms + // * a scripted field should be removed + // * a field of unsupported type should be removed + // * a field that has been used + // * a scripted field was used in a singular term, should be marked as invalid for multi-terms + const filteredOperationByField = Object.keys(operationSupportMatrix.operationByField) + .filter((key) => { + if (key === value) { + return true; + } + const field = indexPattern.getFieldByName(key); + if (index === 0) { + return !rawValuesLookup.has(key) && field && supportedTypes.has(field.type); + } else { + return ( + !rawValuesLookup.has(key) && + field && + !field.scripted && + supportedTypes.has(field.type) + ); + } + }) + .reduce((memo, key) => { + memo[key] = operationSupportMatrix.operationByField[key]; + return memo; + }, {}); - const shouldShowError = Boolean( - value && - ((indexPattern.getFieldByName(value)?.scripted && localValuesFilled.length > 1) || - invalidFields?.includes(value)) - ); - return ( - - {(provided) => ( - - {/* Empty for spacing */} - - - - - { - onFieldSelectChange(choice, index); - }} - isInvalid={shouldShowError} - data-test-subj={`indexPattern-dimension-field-${index}`} - /> - - - - 1) || + invalidFields?.includes(value)) + ); + return ( + + {(provided) => ( + + + + + + + { + onFieldSelectChange(choice, index); + }} + isInvalid={shouldShowError} + data-test-subj={ + localValues.length !== 1 + ? `indexPattern-dimension-field-${index}` + : undefined } - )} - title={i18n.translate('xpack.lens.indexPattern.terms.deleteButtonLabel', { - defaultMessage: 'Delete', - })} - onClick={() => { - handleInputChange(localValues.filter((_, i) => i !== index)); - }} - data-test-subj={`indexPattern-terms-removeField-${index}`} - isDisabled={disableActions && !isNew} - /> - - - - )} - - ); - })} - + /> + + + + { + handleInputChange(localValues.filter((_, i) => i !== index)); + }} + data-test-subj={`indexPattern-terms-removeField-${index}`} + isDisabled={disableActions && !isNew} + /> + + + + + )} + + ); + })} + +
{ handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); @@ -241,7 +249,9 @@ export function FieldInputs({ label={i18n.translate('xpack.lens.indexPattern.terms.addaFilter', { defaultMessage: 'Add field', })} - isDisabled={localValues.length > MAX_MULTI_FIELDS_SIZE} + isDisabled={ + column.params.orderBy.type === 'rare' || localValues.length > MAX_MULTI_FIELDS_SIZE + } /> ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts index 3998836074f6b..3cd46a10151d0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts @@ -15,9 +15,11 @@ import { getDisallowedTermsMessage, getMultiTermsScriptedFieldErrorMessage, isSortableByColumn, + computeOrderForMultiplePercentiles, } from './helpers'; import { ReferenceBasedIndexPatternColumn } from '../column_types'; import type { PercentileRanksIndexPatternColumn } from '../percentile_ranks'; +import type { PercentileIndexPatternColumn } from '../percentile'; import { MULTI_KEY_VISUAL_SEPARATOR } from './constants'; const indexPattern = createMockedIndexPattern(); @@ -381,6 +383,138 @@ describe('getDisallowedTermsMessage()', () => { }); }); +describe('computeOrderForMultiplePercentiles()', () => { + it('should return null for no percentile orderColumn', () => { + expect( + computeOrderForMultiplePercentiles( + { + label: 'Percentile rank (1024.5) of bytes', + dataType: 'number', + operationType: 'percentile_rank', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { value: 1024.5 }, + } as PercentileRanksIndexPatternColumn, + getLayer(getStringBasedOperationColumn(), [ + { + label: 'Percentile rank (1024.5) of bytes', + dataType: 'number', + operationType: 'percentile_rank', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { value: 1024.5 }, + } as PercentileRanksIndexPatternColumn, + ]), + ['col1', 'col2'] + ) + ).toBeNull(); + }); + + it('should return null for single percentile', () => { + expect( + computeOrderForMultiplePercentiles( + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + getLayer(getStringBasedOperationColumn(), [ + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + ]), + ['col1', 'col2'] + ) + ).toBeNull(); + }); + + it('should return correct orderBy for multiple percentile on the same field', () => { + expect( + computeOrderForMultiplePercentiles( + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + getLayer(getStringBasedOperationColumn(), [ + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + { + label: 'Percentile 65 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 65 }, + } as PercentileIndexPatternColumn, + ]), + ['col1', 'col2', 'col3'] + ) + ).toBe('1.95'); + }); + + it('should return null for multiple percentile on different field', () => { + expect( + computeOrderForMultiplePercentiles( + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + getLayer(getStringBasedOperationColumn(), [ + { + label: 'Percentile 95 of bytes', + dataType: 'number', + operationType: 'percentile', + sourceField: 'bytes', + isBucketed: false, + scale: 'ratio', + params: { percentile: 95 }, + } as PercentileIndexPatternColumn, + { + label: 'Percentile 65 of geo', + dataType: 'number', + operationType: 'percentile', + sourceField: 'geo', + isBucketed: false, + scale: 'ratio', + params: { percentile: 65 }, + } as PercentileIndexPatternColumn, + ]), + ['col1', 'col2', 'col3'] + ) + ).toBeNull(); + }); +}); + describe('isSortableByColumn()', () => { it('should sort by the given column', () => { expect( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts index a3d749f4308cf..b1331b0128284 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts @@ -20,6 +20,7 @@ import type { FiltersIndexPatternColumn } from '..'; import type { TermsIndexPatternColumn } from './types'; import { LastValueIndexPatternColumn } from '../last_value'; import type { PercentileRanksIndexPatternColumn } from '../percentile_ranks'; +import type { PercentileIndexPatternColumn } from '../percentile'; import type { IndexPatternLayer, IndexPattern, IndexPatternField } from '../../../types'; import { MULTI_KEY_VISUAL_SEPARATOR, supportedTypes } from './constants'; @@ -224,6 +225,31 @@ export function isPercentileRankSortable(column: GenericIndexPatternColumn) { ); } +export function computeOrderForMultiplePercentiles( + column: GenericIndexPatternColumn, + layer: IndexPatternLayer, + orderedColumnIds: string[] +) { + // compute the percentiles orderBy correctly for multiple percentiles + if (column.operationType === 'percentile') { + const percentileColumns = []; + for (const [key, value] of Object.entries(layer.columns)) { + if ( + value.operationType === 'percentile' && + (value as PercentileIndexPatternColumn).sourceField === + (column as PercentileIndexPatternColumn).sourceField + ) { + percentileColumns.push(key); + } + } + if (percentileColumns.length > 1) { + const parentColumn = String(orderedColumnIds.indexOf(percentileColumns[0])); + return `${parentColumn}.${(column as PercentileIndexPatternColumn).params?.percentile}`; + } + } + return null; +} + export function isSortableByColumn(layer: IndexPatternLayer, columnId: string) { const column = layer.columns[columnId]; return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index cade55e9c2f7b..ce84265cecd4f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -19,6 +19,8 @@ import { EuiButtonGroup, EuiText, useEuiTheme, + EuiTitle, + EuiTextColor, } from '@elastic/eui'; import { uniq } from 'lodash'; import { AggFunctionsMapping } from '@kbn/data-plugin/public'; @@ -47,6 +49,7 @@ import { getFieldsByValidationState, isSortableByColumn, isPercentileRankSortable, + computeOrderForMultiplePercentiles, } from './helpers'; import { DEFAULT_MAX_DOC_COUNT, @@ -260,6 +263,14 @@ export const termsOperation: OperationDefinition {!hasRestrictions && ( <> - + - {i18n.translate('xpack.lens.indexPattern.terms.advancedSettings', { - defaultMessage: 'Advanced', - })} - + +
+ + {i18n.translate('xpack.lens.indexPattern.terms.advancedSettings', { + defaultMessage: 'Advanced', + })} + +
+
} data-test-subj="indexPattern-terms-advanced" + className="lnsIndexPatternDimensionEditor-advancedOptions" > - + + {i18n.translate('xpack.lens.indexPattern.terms.missingBucketDescription', { + defaultMessage: 'Include documents without the selected field', + })} + + } compressed - data-test-subj="indexPattern-terms-other-bucket" - checked={Boolean(currentColumn.params.otherBucket)} - disabled={currentColumn.params.orderBy.type === 'rare'} + disabled={ + !currentColumn.params.otherBucket || + indexPattern.getFieldByName(currentColumn.sourceField)?.type !== 'string' || + currentColumn.params.orderBy.type === 'rare' + } + data-test-subj="indexPattern-terms-missing-bucket" + checked={Boolean(currentColumn.params.missingBucket)} onChange={(e: EuiSwitchEvent) => paramEditorUpdater( updateColumnParam({ layer, columnId, - paramName: 'otherBucket', + paramName: 'missingBucket', value: e.target.checked, }) ) } /> - + + {i18n.translate('xpack.lens.indexPattern.terms.otherBucketDescription', { + defaultMessage: 'Group remaining values as "Other"', + })} + } - data-test-subj="indexPattern-terms-missing-bucket" - checked={Boolean(currentColumn.params.missingBucket)} + compressed + data-test-subj="indexPattern-terms-other-bucket" + checked={Boolean(currentColumn.params.otherBucket)} + disabled={currentColumn.params.orderBy.type === 'rare'} onChange={(e: EuiSwitchEvent) => paramEditorUpdater( updateColumnParam({ layer, columnId, - paramName: 'missingBucket', + paramName: 'otherBucket', value: e.target.checked, }) ) } /> - + + {i18n.translate('xpack.lens.indexPattern.terms.accuracyModeDescription', { defaultMessage: 'Enable accuracy mode', })}{' '} @@ -992,7 +1016,7 @@ export const termsOperation: OperationDefinition - + } compressed disabled={currentColumn.params.orderBy.type === 'rare'} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index 424bdfd002522..f67dac072b341 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -336,6 +336,49 @@ describe('terms', () => { ); }); + it('should reflect correct orderBy for multiple percentiles', () => { + const newLayer = { + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + operationType: 'percentile', + params: { + percentile: 95, + }, + }, + col3: { + ...layer.columns.col2, + operationType: 'percentile', + params: { + percentile: 65, + }, + }, + }, + }; + const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; + const esAggsFn = termsOperation.toEsAggsFn( + { + ...termsColumn, + params: { ...termsColumn.params, orderBy: { type: 'column', columnId: 'col3' } }, + }, + 'col1', + {} as IndexPattern, + newLayer, + uiSettingsMock, + ['col1', 'col2', 'col3'] + ); + expect(esAggsFn).toEqual( + expect.objectContaining({ + function: 'aggTerms', + arguments: expect.objectContaining({ + orderBy: ['1.65'], + }), + }) + ); + }); + it('should not enable missing bucket if other bucket is not set', () => { const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; const esAggsFn = termsOperation.toEsAggsFn( @@ -1346,7 +1389,7 @@ describe('terms', () => { ).toBe('Invalid field: "timestamp". Check your data view or pick another field.'); }); - it('should render the an add button for single layer, but no other hints', () => { + it('should render the an add button for single layer and disabled the remove button', () => { const updateLayerSpy = jest.fn(); const existingFields = getExistingFields(); const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); @@ -1366,7 +1409,15 @@ describe('terms', () => { instance.find('[data-test-subj="indexPattern-terms-add-field"]').exists() ).toBeTruthy(); - expect(instance.find('[data-test-subj^="indexPattern-terms-removeField-"]').length).toBe(0); + expect(instance.find('[data-test-subj^="indexPattern-terms-removeField-"]').length).not.toBe( + 0 + ); + expect( + instance + .find('[data-test-subj^="indexPattern-terms-removeField-"]') + .first() + .prop('isDisabled') + ).toBeTruthy(); }); it('should switch to the first supported operation when in single term mode and the picked field is not supported', () => { @@ -1583,7 +1634,7 @@ describe('terms', () => { ); expect( - instance.find('[data-test-subj="indexPattern-dimension-field"]').first().prop('options') + instance.find('[data-test-subj="indexPattern-dimension-field"]').at(1).prop('options') ).toEqual( expect.arrayContaining([ expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts index fd8952ad9e077..7992a1d840947 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts @@ -16,82 +16,258 @@ describe('time scale utils', () => { describe('adjustTimeScaleLabelSuffix', () => { it('should should remove existing suffix', () => { expect( - adjustTimeScaleLabelSuffix('abc per second', 's', undefined, undefined, undefined) + adjustTimeScaleLabelSuffix( + 'abc per second', + 's', + undefined, + undefined, + undefined, + undefined, + undefined + ) ).toEqual('abc'); expect( - adjustTimeScaleLabelSuffix('abc per hour', 'h', undefined, undefined, undefined) + adjustTimeScaleLabelSuffix( + 'abc per hour', + 'h', + undefined, + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc'); + expect( + adjustTimeScaleLabelSuffix( + 'abc -3d', + undefined, + undefined, + '3d', + undefined, + undefined, + undefined + ) + ).toEqual('abc'); + expect( + adjustTimeScaleLabelSuffix( + 'abc per hour -3d', + 'h', + undefined, + '3d', + undefined, + undefined, + undefined + ) ).toEqual('abc'); - expect(adjustTimeScaleLabelSuffix('abc -3d', undefined, undefined, '3d', undefined)).toEqual( - 'abc' - ); expect( - adjustTimeScaleLabelSuffix('abc per hour -3d', 'h', undefined, '3d', undefined) + adjustTimeScaleLabelSuffix( + 'abc last 5m', + undefined, + undefined, + undefined, + undefined, + '5m', + undefined + ) + ).toEqual('abc'); + expect( + adjustTimeScaleLabelSuffix( + 'abc per hour -3d last 5m', + 'h', + undefined, + '3d', + undefined, + '5m', + undefined + ) ).toEqual('abc'); }); it('should add suffix', () => { - expect(adjustTimeScaleLabelSuffix('abc', undefined, 's', undefined, undefined)).toEqual( - 'abc per second' - ); - expect(adjustTimeScaleLabelSuffix('abc', undefined, 'd', undefined, undefined)).toEqual( - 'abc per day' - ); - expect(adjustTimeScaleLabelSuffix('abc', undefined, undefined, undefined, '12h')).toEqual( - 'abc -12h' - ); - expect(adjustTimeScaleLabelSuffix('abc', undefined, 'h', undefined, '12h')).toEqual( - 'abc per hour -12h' - ); + expect( + adjustTimeScaleLabelSuffix( + 'abc', + undefined, + 's', + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc per second'); + expect( + adjustTimeScaleLabelSuffix( + 'abc', + undefined, + 'd', + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc per day'); + expect( + adjustTimeScaleLabelSuffix( + 'abc', + undefined, + undefined, + undefined, + undefined, + undefined, + '5m' + ) + ).toEqual('abc last 5m'); + expect( + adjustTimeScaleLabelSuffix( + 'abc', + undefined, + undefined, + undefined, + '12h', + undefined, + undefined + ) + ).toEqual('abc -12h'); + expect( + adjustTimeScaleLabelSuffix('abc', undefined, 'h', undefined, '12h', undefined, undefined) + ).toEqual('abc per hour -12h'); + expect( + adjustTimeScaleLabelSuffix('abc', undefined, 'h', undefined, '12h', undefined, '5m') + ).toEqual('abc per hour -12h last 5m'); }); it('should add and remove at the same time', () => { - expect(adjustTimeScaleLabelSuffix('abc per hour', 'h', undefined, undefined, '1d')).toEqual( - 'abc -1d' - ); - expect(adjustTimeScaleLabelSuffix('abc -1d', undefined, 'h', '1d', undefined)).toEqual( - 'abc per hour' - ); + expect( + adjustTimeScaleLabelSuffix( + 'abc per hour', + 'h', + undefined, + undefined, + '1d', + undefined, + undefined + ) + ).toEqual('abc -1d'); + expect( + adjustTimeScaleLabelSuffix('abc -1d', undefined, 'h', '1d', undefined, undefined, undefined) + ).toEqual('abc per hour'); + expect( + adjustTimeScaleLabelSuffix('abc -1d', undefined, 'h', '1d', undefined, undefined, '12m') + ).toEqual('abc per hour last 12m'); }); it('should change suffix', () => { - expect(adjustTimeScaleLabelSuffix('abc per second', 's', 'd', undefined, undefined)).toEqual( - 'abc per day' - ); - expect(adjustTimeScaleLabelSuffix('abc per day', 'd', 's', undefined, undefined)).toEqual( - 'abc per second' - ); - expect(adjustTimeScaleLabelSuffix('abc per day -3h', 'd', 's', '3h', '3h')).toEqual( - 'abc per second -3h' - ); - expect(adjustTimeScaleLabelSuffix('abc per day -3h', 'd', 'd', '3h', '4h')).toEqual( - 'abc per day -4h' - ); + expect( + adjustTimeScaleLabelSuffix( + 'abc per second', + 's', + 'd', + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc per day'); + expect( + adjustTimeScaleLabelSuffix( + 'abc per day', + 'd', + 's', + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc per second'); + expect( + adjustTimeScaleLabelSuffix('abc per day -3h', 'd', 's', '3h', '3h', undefined, undefined) + ).toEqual('abc per second -3h'); + expect( + adjustTimeScaleLabelSuffix('abc per day -3h', 'd', 'd', '3h', '4h', undefined, undefined) + ).toEqual('abc per day -4h'); + }); + + it('should change window', () => { + expect( + adjustTimeScaleLabelSuffix( + 'abc per second last 5m', + 's', + 's', + undefined, + undefined, + undefined, + '2h' + ) + ).toEqual('abc per second last 2h'); }); it('should keep current state', () => { - expect(adjustTimeScaleLabelSuffix('abc', undefined, undefined, undefined, undefined)).toEqual( - 'abc' - ); - expect(adjustTimeScaleLabelSuffix('abc per day', 'd', 'd', undefined, undefined)).toEqual( - 'abc per day' - ); - expect(adjustTimeScaleLabelSuffix('abc -1h', undefined, undefined, '1h', '1h')).toEqual( - 'abc -1h' - ); - expect(adjustTimeScaleLabelSuffix('abc per day -1h', 'd', 'd', '1h', '1h')).toEqual( - 'abc per day -1h' - ); + expect( + adjustTimeScaleLabelSuffix( + 'abc', + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc'); + expect( + adjustTimeScaleLabelSuffix( + 'abc per day', + 'd', + 'd', + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc per day'); + expect( + adjustTimeScaleLabelSuffix( + 'abc -1h', + undefined, + undefined, + '1h', + '1h', + undefined, + undefined + ) + ).toEqual('abc -1h'); + expect( + adjustTimeScaleLabelSuffix('abc per day -1h', 'd', 'd', '1h', '1h', undefined, undefined) + ).toEqual('abc per day -1h'); + expect( + adjustTimeScaleLabelSuffix('abc per day -1h last 78s', 'd', 'd', '1h', '1h', '78s', '78s') + ).toEqual('abc per day -1h last 78s'); }); it('should not fail on inconsistent input', () => { - expect(adjustTimeScaleLabelSuffix('abc', 's', undefined, undefined, undefined)).toEqual( - 'abc' - ); - expect(adjustTimeScaleLabelSuffix('abc', 's', 'd', undefined, undefined)).toEqual( - 'abc per day' - ); expect( - adjustTimeScaleLabelSuffix('abc per day', 's', undefined, undefined, undefined) + adjustTimeScaleLabelSuffix( + 'abc', + 's', + undefined, + undefined, + undefined, + undefined, + undefined + ) + ).toEqual('abc'); + expect( + adjustTimeScaleLabelSuffix('abc', 's', 'd', undefined, undefined, undefined, undefined) + ).toEqual('abc per day'); + expect( + adjustTimeScaleLabelSuffix( + 'abc per day', + 's', + undefined, + undefined, + undefined, + undefined, + undefined + ) ).toEqual('abc per day'); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts index c6cd343504253..c8d7cca2ec6a6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import { unitSuffixesLong } from '../../../common/suffix_formatter'; import type { TimeScaleUnit } from '../../../common/expressions'; import type { IndexPatternLayer } from '../types'; @@ -12,12 +13,23 @@ import type { GenericIndexPatternColumn } from './definitions'; export const DEFAULT_TIME_SCALE = 's' as TimeScaleUnit; -function getSuffix(scale: TimeScaleUnit | undefined, shift: string | undefined) { +function getSuffix( + scale: TimeScaleUnit | undefined, + shift: string | undefined, + window: string | undefined +) { return ( (shift || scale ? ' ' : '') + (scale ? unitSuffixesLong[scale] : '') + (shift && scale ? ' ' : '') + - (shift ? `-${shift}` : '') + (shift ? `-${shift}` : '') + + (window ? ' ' : '') + + (window + ? i18n.translate('xpack.lens.windowSuffix', { + defaultMessage: 'last {window}', + values: { window }, + }) + : '') ); } @@ -26,22 +38,24 @@ export function adjustTimeScaleLabelSuffix( previousTimeScale: TimeScaleUnit | undefined, newTimeScale: TimeScaleUnit | undefined, previousShift: string | undefined, - newShift: string | undefined + newShift: string | undefined, + previousWindow: string | undefined, + newWindow: string | undefined ) { let cleanedLabel = oldLabel; // remove added suffix if column had a time scale previously - if (previousTimeScale || previousShift) { - const suffix = getSuffix(previousTimeScale, previousShift); + if (previousTimeScale || previousShift || previousWindow) { + const suffix = getSuffix(previousTimeScale, previousShift, previousWindow); const suffixPosition = oldLabel.lastIndexOf(suffix); if (suffixPosition !== -1) { cleanedLabel = oldLabel.substring(0, suffixPosition); } } - if (!newTimeScale && !newShift) { + if (!newTimeScale && !newShift && !newWindow) { return cleanedLabel; } // add new suffix if column has a time scale now - return `${cleanedLabel}${getSuffix(newTimeScale, newShift)}`; + return `${cleanedLabel}${getSuffix(newTimeScale, newShift, newWindow)}`; } export function adjustTimeScaleOnOtherColumnChange( @@ -70,7 +84,9 @@ export function adjustTimeScaleOnOtherColumnChange c.operationType === 'date_histogram'); if (referenceEntries.length || esAggEntries.length) { let aggs: ExpressionAstExpressionBuilder[] = []; @@ -124,6 +125,8 @@ function getExpressionForLayer( const aggId = String(index); const wrapInFilter = Boolean(def.filterable && col.filter); + const wrapInTimeFilter = + def.windowable && !hasDateHistogram && col.window && indexPattern.timeFieldName; let aggAst = def.toEsAggsFn( col, wrapInFilter ? `${aggId}-metric` : aggId, @@ -133,7 +136,7 @@ function getExpressionForLayer( orderedColumnIds, operationDefinitionMap ); - if (wrapInFilter) { + if (wrapInFilter || wrapInTimeFilter) { aggAst = buildExpressionFunction( 'aggFilteredMetric', { @@ -146,6 +149,8 @@ function getExpressionForLayer( enabled: true, schema: 'bucket', filter: col.filter && queryToAst(col.filter), + timeWindow: wrapInTimeFilter ? col.window : undefined, + timeShift: col.timeShift, }), ]), customMetric: buildExpression({ type: 'expression', chain: [aggAst] }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/window_utils.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/window_utils.tsx new file mode 100644 index 0000000000000..14cc09fcdec8b --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/window_utils.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { IndexPattern, IndexPatternLayer } from './types'; + +export const windowOptions = [ + { + label: i18n.translate('xpack.lens.indexPattern.window.30s', { + defaultMessage: '30 seconds (30s)', + }), + value: '30s', + }, + { + label: i18n.translate('xpack.lens.indexPattern.window.1m', { + defaultMessage: '1 minute (1m)', + }), + value: '1m', + }, + { + label: i18n.translate('xpack.lens.indexPattern.window.5m', { + defaultMessage: '5 minutes (5m)', + }), + value: '5m', + }, + { + label: i18n.translate('xpack.lens.indexPattern.window.15m', { + defaultMessage: '15 minutes (15m)', + }), + value: '15m', + }, + { + label: i18n.translate('xpack.lens.indexPattern.window.1h', { + defaultMessage: '1 hour (1h)', + }), + value: '1h', + }, +]; + +export const windowOptionOrder = windowOptions.reduce<{ [key: string]: number }>( + (optionMap, { value }, index) => ({ + ...optionMap, + [value]: index, + }), + {} +); + +export function getColumnWindowError( + layer: IndexPatternLayer, + columnId: string, + indexPattern: IndexPattern +): string[] | undefined { + const currentColumn = layer.columns[columnId]; + if (!currentColumn.window) { + return; + } + const hasDateHistogram = Object.values(layer.columns).some( + (column) => column.operationType === 'date_histogram' + ); + const hasTimeField = Boolean(indexPattern.timeFieldName); + return [ + hasDateHistogram && + i18n.translate('xpack.lens.indexPattern.windowWithDateHistogram', { + defaultMessage: + 'Reduced time range can only be used without a date histogram. Either remove the date histogram dimension or remove the reduced time range from {column}.', + values: { + column: currentColumn.label, + }, + }), + !hasTimeField && + i18n.translate('xpack.lens.indexPattern.windowWithoutTimefield', { + defaultMessage: + 'Reduced time range can only be used with a specified default time field on the data view. Either use a different data view with default time field or remove the reduced time range from {column}.', + values: { + column: currentColumn.label, + }, + }), + ].filter(Boolean) as string[]; +} diff --git a/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx b/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx index 09bf2f065c60c..cbf310cb2f50a 100644 --- a/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { createFormulaPublicApi } from '../async_services'; import { LensPublicStart } from '..'; -import { visualizationTypes } from '../xy_visualization/types'; +import { visualizationTypes } from '../visualizations/xy/types'; type Start = jest.Mocked; diff --git a/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts b/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts index aadc35068da0e..0d117220b895b 100644 --- a/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts +++ b/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts @@ -6,7 +6,8 @@ */ import { findObjectByTitle } from './find_object_by_title'; -import { SimpleSavedObjectImpl, SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { SavedObjectsClientContract, SavedObject } from '@kbn/core/public'; +import { simpleSavedObjectMock } from '@kbn/core/public/mocks'; describe('findObjectByTitle', () => { const savedObjectsClient: SavedObjectsClientContract = {} as SavedObjectsClientContract; @@ -21,7 +22,7 @@ describe('findObjectByTitle', () => { }); it('matches any case', async () => { - const indexPattern = new SimpleSavedObjectImpl(savedObjectsClient, { + const indexPattern = simpleSavedObjectMock.create(savedObjectsClient, { attributes: { title: 'foo' }, } as SavedObject); savedObjectsClient.find = jest.fn().mockImplementation(() => diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index c66d538ed0511..99585a0fd9a77 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -54,21 +54,21 @@ import type { import type { XyVisualization as XyVisualizationType, XyVisualizationPluginSetupPlugins, -} from './xy_visualization'; +} from './visualizations/xy'; import type { LegacyMetricVisualization as LegacyMetricVisualizationType, LegacyMetricVisualizationPluginSetupPlugins, -} from './metric_visualization'; +} from './visualizations/legacy_metric'; import type { MetricVisualization as MetricVisualizationType } from './visualizations/metric'; import type { DatatableVisualization as DatatableVisualizationType, DatatableVisualizationPluginSetupPlugins, -} from './datatable_visualization'; +} from './visualizations/datatable'; import type { PieVisualization as PieVisualizationType, PieVisualizationPluginSetupPlugins, -} from './pie_visualization'; -import type { HeatmapVisualization as HeatmapVisualizationType } from './heatmap_visualization'; +} from './visualizations/partition'; +import type { HeatmapVisualization as HeatmapVisualizationType } from './visualizations/heatmap'; import type { GaugeVisualization as GaugeVisualizationType } from './visualizations/gauge'; import { APP_ID, getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common/constants'; @@ -504,7 +504,7 @@ export class LensPlugin { return Boolean(core.application.capabilities.visualize?.show); }, getXyVisTypes: async () => { - const { visualizationTypes } = await import('./xy_visualization/types'); + const { visualizationTypes } = await import('./visualizations/xy/types'); return visualizationTypes; }, diff --git a/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx b/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx index 5c1cc704671be..5378859fdc94c 100644 --- a/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiSpacer, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AxesSettingsConfig } from '../xy_visualization/types'; +import { AxesSettingsConfig } from '../visualizations/xy/types'; import { LabelMode, useDebouncedValue, VisLabel } from '.'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index cf01b60b1c42c..b910354f1f68d 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -82,7 +82,7 @@ export function PalettePanelContainer({ > {i18n.translate('xpack.lens.table.palettePanelTitle', { - defaultMessage: 'Edit color', + defaultMessage: 'Color', })}

diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 215c0ef273cbb..78f104ce943fb 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -32,14 +32,14 @@ import type { LensResizeActionData, LensToggleActionData, LensPagesizeActionData, -} from './datatable_visualization/components/types'; +} from './visualizations/datatable/components/types'; import { LENS_EDIT_SORT_ACTION, LENS_EDIT_RESIZE_ACTION, LENS_TOGGLE_ACTION, LENS_EDIT_PAGESIZE_ACTION, -} from './datatable_visualization/components/constants'; +} from './visualizations/datatable/components/constants'; import type { LensInspector } from './lens_inspector_service'; export type ErrorCallback = (e: { message: string }) => void; @@ -444,6 +444,7 @@ export type DatasourceDimensionProps = SharedDimensionProps & { export type ParamEditorCustomProps = Record & { labels?: string[]; isInline?: boolean; + headingLabel?: string; }; // The only way a visualization has to restrict the query building export type DatasourceDimensionEditorProps = DatasourceDimensionProps & { @@ -586,6 +587,7 @@ export interface AccessorConfig { export type VisualizationDimensionGroupConfig = SharedDimensionProps & { groupLabel: string; + dimensionEditorGroupLabel?: string; groupTooltip?: string; /** ID is passed back to visualization. For example, `x` */ @@ -907,6 +909,14 @@ export interface Visualization { domElement: Element, props: VisualizationDimensionEditorProps ) => ((cleanupElement: Element) => void) | void; + /** + * Additional editor that gets rendered inside the dimension popover. + * This can be used to configure dimension-specific options + */ + renderDimensionEditorAdditionalSection?: ( + domElement: Element, + props: VisualizationDimensionEditorProps + ) => ((cleanupElement: Element) => void) | void; /** * Renders dimension trigger. Used only for noDatasource layers */ diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/datatable/components/__snapshots__/table_basic.test.tsx.snap similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap rename to x-pack/plugins/lens/public/visualizations/datatable/components/__snapshots__/table_basic.test.tsx.snap diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.test.tsx index 94a55a393814c..f6092e3dc299a 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.test.tsx @@ -14,7 +14,7 @@ import { Datatable } from '@kbn/expressions-plugin/public'; import { IUiSettingsClient } from '@kbn/core/public'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { DatatableArgs, ColumnConfigArg } from '../../../common/expressions'; +import { DatatableArgs, ColumnConfigArg } from '../../../../common/expressions'; import { DataContextType } from './types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.tsx similarity index 91% rename from x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.tsx index e43c08aec6375..4db92a65f9c4d 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/cell_value.tsx @@ -9,11 +9,11 @@ import React, { useContext, useEffect } from 'react'; import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; import type { IUiSettingsClient } from '@kbn/core/public'; import classNames from 'classnames'; -import type { FormatFactory } from '../../../common'; -import { getOriginalId } from '../../../common/expressions'; -import type { ColumnConfig } from '../../../common/expressions'; +import type { FormatFactory } from '../../../../common'; +import { getOriginalId } from '../../../../common/expressions'; +import type { ColumnConfig } from '../../../../common/expressions'; import type { DataContextType } from './types'; -import { getContrastColor, getNumericValue } from '../../shared_components/coloring/utils'; +import { getContrastColor, getNumericValue } from '../../../shared_components/coloring/utils'; export const createGridCell = ( formatters: Record>, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx index fd3a0acb2b0fd..81b73b8f267b7 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/columns.tsx @@ -18,8 +18,8 @@ import type { DatatableColumn, DatatableColumnMeta, } from '@kbn/expressions-plugin/common'; -import type { FormatFactory } from '../../../common'; -import type { ColumnConfig } from '../../../common/expressions'; +import type { FormatFactory } from '../../../../common'; +import type { ColumnConfig } from '../../../../common/expressions'; export const createGridColumns = ( bucketColumns: string[], diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/constants.ts b/x-pack/plugins/lens/public/visualizations/datatable/components/constants.ts similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/components/constants.ts rename to x-pack/plugins/lens/public/visualizations/datatable/components/constants.ts diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.scss b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.scss similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.scss rename to x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.scss diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.test.tsx similarity index 78% rename from x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.test.tsx index 64517c92dc77e..d182a9151d51c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.test.tsx @@ -6,21 +6,21 @@ */ import React from 'react'; -import { EuiButtonGroup, EuiComboBox, EuiFieldText } from '@elastic/eui'; +import { EuiButtonGroup } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import { FramePublicAPI, OperationDescriptor, VisualizationDimensionEditorProps, -} from '../../types'; +} from '../../../types'; import { DatatableVisualizationState } from '../visualization'; -import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { TableDimensionEditor } from './dimension_editor'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { act } from 'react-dom/test-utils'; -import { PalettePanelContainer } from '../../shared_components'; -import { layerTypes } from '../../../common'; +import { PalettePanelContainer } from '../../../shared_components'; +import { layerTypes } from '../../../../common'; describe('data table dimension editor', () => { let frame: FramePublicAPI; @@ -242,56 +242,4 @@ describe('data table dimension editor', () => { ); expect(instance.find('[data-test-subj="lnsDatatable_summaryrow_label"]').exists()).toBe(false); }); - - it('should set the summary row function default to "none"', () => { - frame.activeData!.first.columns[0].meta.type = 'number'; - const instance = mountWithIntl(); - expect( - instance - .find('[data-test-subj="lnsDatatable_summaryrow_function"]') - .find(EuiComboBox) - .prop('selectedOptions') - ).toEqual([{ value: 'none', label: 'None' }]); - - expect(instance.find('[data-test-subj="lnsDatatable_summaryrow_label"]').exists()).toBe(false); - }); - - it('should show the summary row label input ony when summary row is different from "none"', () => { - frame.activeData!.first.columns[0].meta.type = 'number'; - state.columns[0].summaryRow = 'sum'; - const instance = mountWithIntl(); - expect( - instance - .find('[data-test-subj="lnsDatatable_summaryrow_function"]') - .find(EuiComboBox) - .prop('selectedOptions') - ).toEqual([{ value: 'sum', label: 'Sum' }]); - - expect( - instance - .find('[data-test-subj="lnsDatatable_summaryrow_label"]') - .find(EuiFieldText) - .prop('value') - ).toBe('Sum'); - }); - - it("should show the correct summary row name when user's changes summary label", () => { - frame.activeData!.first.columns[0].meta.type = 'number'; - state.columns[0].summaryRow = 'sum'; - state.columns[0].summaryLabel = 'MySum'; - const instance = mountWithIntl(); - expect( - instance - .find('[data-test-subj="lnsDatatable_summaryrow_function"]') - .find(EuiComboBox) - .prop('selectedOptions') - ).toEqual([{ value: 'sum', label: 'Sum' }]); - - expect( - instance - .find('[data-test-subj="lnsDatatable_summaryrow_label"]') - .find(EuiFieldText) - .prop('value') - ).toBe('MySum'); - }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx similarity index 77% rename from x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx index 28537e7934555..da1f28ba579f2 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, @@ -16,36 +16,25 @@ import { EuiFlexItem, EuiFlexGroup, EuiButtonEmpty, - EuiFieldText, - EuiComboBox, } from '@elastic/eui'; import { CustomizablePalette, PaletteRegistry, FIXED_PROGRESSION } from '@kbn/coloring'; -import { VisualizationDimensionEditorProps } from '../../types'; +import { VisualizationDimensionEditorProps } from '../../../types'; import { DatatableVisualizationState } from '../visualization'; import { applyPaletteParams, defaultPaletteParams, - useDebouncedValue, PalettePanelContainer, findMinMaxByColumnId, -} from '../../shared_components'; -import type { ColumnState } from '../../../common/expressions'; -import { - isNumericFieldForDatatable, - getDefaultSummaryLabel, - getFinalSummaryConfiguration, - getSummaryRowOptions, - getOriginalId, -} from '../../../common/expressions'; +} from '../../../shared_components'; +import { isNumericFieldForDatatable, getOriginalId } from '../../../../common/expressions'; import './dimension_editor.scss'; -import { CollapseSetting } from '../../shared_components/collapse_setting'; +import { CollapseSetting } from '../../../shared_components/collapse_setting'; const idPrefix = htmlIdGenerator()(); type ColumnType = DatatableVisualizationState['columns'][number]; -type SummaryRowType = Extract; function updateColumnWith( state: DatatableVisualizationState, @@ -69,24 +58,6 @@ export function TableDimensionEditor( const { state, setState, frame, accessor } = props; const column = state.columns.find(({ columnId }) => accessor === columnId); const [isPaletteOpen, setIsPaletteOpen] = useState(false); - const onSummaryLabelChangeToDebounce = useCallback( - (newSummaryLabel: string | undefined) => { - setState({ - ...state, - columns: updateColumnWith(state, accessor, { summaryLabel: newSummaryLabel }), - }); - }, - [accessor, setState, state] - ); - const { inputValue: summaryLabel, handleInputChange: onSummaryLabelChange } = useDebouncedValue< - string | undefined - >( - { - onChange: onSummaryLabelChangeToDebounce, - value: column?.summaryLabel, - }, - { allowFalsyValue: true } // falsy values are valid for this feature - ); if (!column) return null; if (column.isTransposed) return null; @@ -98,12 +69,6 @@ export function TableDimensionEditor( const currentAlignment = column?.alignment || (isNumeric ? 'right' : 'left'); const currentColorMode = column?.colorMode || 'none'; const hasDynamicColoring = currentColorMode !== 'none'; - // when switching from one operation to another, make sure to keep the configuration consistent - const { summaryRow, summaryLabel: fallbackSummaryLabel } = getFinalSummaryConfiguration( - accessor, - column, - currentData - ); const datasource = frame.datasourceLayers[state.layerId]; const showDynamicColoringFeature = Boolean( @@ -188,97 +153,6 @@ export function TableDimensionEditor( }} /> - {!column.isTransposed && ( - - { - const newState = { - ...state, - columns: state.columns.map((currentColumn) => { - if (currentColumn.columnId === accessor) { - return { - ...currentColumn, - hidden: !column.hidden, - }; - } else { - return currentColumn; - } - }), - }; - setState(newState); - }} - /> - - )} - {isNumeric && ( - <> - - { - const newValue = choices[0].value as SummaryRowType; - setState({ - ...state, - columns: updateColumnWith(state, accessor, { summaryRow: newValue }), - }); - }} - /> - - {summaryRow !== 'none' && ( - - { - onSummaryLabelChange(e.target.value); - }} - /> - - )} - - )} {showDynamicColoringFeature && ( <> )} + {!column.isTransposed && ( + + { + const newState = { + ...state, + columns: state.columns.map((currentColumn) => { + if (currentColumn.columnId === accessor) { + return { + ...currentColumn, + hidden: !column.hidden, + }; + } else { + return currentColumn; + } + }), + }; + setState(newState); + }} + /> + + )} ); } diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_additional_section.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_additional_section.test.tsx new file mode 100644 index 0000000000000..859c3867b0cef --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_additional_section.test.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiComboBox, EuiFieldText } from '@elastic/eui'; +import type { PaletteRegistry } from '@kbn/coloring'; +import { FramePublicAPI, VisualizationDimensionEditorProps } from '../../../types'; +import { DatatableVisualizationState } from '../visualization'; +import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { TableDimensionEditorAdditionalSection } from './dimension_editor_addtional_section'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +import { layerTypes } from '../../../../common'; + +describe('data table dimension editor additional section', () => { + let frame: FramePublicAPI; + let state: DatatableVisualizationState; + let setState: (newState: DatatableVisualizationState) => void; + let props: VisualizationDimensionEditorProps & { + paletteService: PaletteRegistry; + }; + + function testState(): DatatableVisualizationState { + return { + layerId: 'first', + layerType: layerTypes.DATA, + columns: [ + { + columnId: 'foo', + }, + ], + }; + } + + beforeEach(() => { + state = testState(); + frame = createMockFramePublicAPI(); + frame.datasourceLayers = { + first: createMockDatasource('test').publicAPIMock, + }; + frame.activeData = { + first: { + type: 'datatable', + columns: [ + { + id: 'foo', + name: 'foo', + meta: { + type: 'string', + }, + }, + ], + rows: [], + }, + }; + setState = jest.fn(); + props = { + accessor: 'foo', + frame, + groupId: 'columns', + layerId: 'first', + state, + setState, + paletteService: chartPluginMock.createPaletteRegistry(), + panelRef: React.createRef(), + }; + }); + + it('should set the summary row function default to "none"', () => { + frame.activeData!.first.columns[0].meta.type = 'number'; + const instance = mountWithIntl(); + expect( + instance + .find('[data-test-subj="lnsDatatable_summaryrow_function"]') + .find(EuiComboBox) + .prop('selectedOptions') + ).toEqual([{ value: 'none', label: 'None' }]); + + expect(instance.find('[data-test-subj="lnsDatatable_summaryrow_label"]').exists()).toBe(false); + }); + + it('should show the summary row label input ony when summary row is different from "none"', () => { + frame.activeData!.first.columns[0].meta.type = 'number'; + state.columns[0].summaryRow = 'sum'; + const instance = mountWithIntl(); + expect( + instance + .find('[data-test-subj="lnsDatatable_summaryrow_function"]') + .find(EuiComboBox) + .prop('selectedOptions') + ).toEqual([{ value: 'sum', label: 'Sum' }]); + + expect( + instance + .find('[data-test-subj="lnsDatatable_summaryrow_label"]') + .find(EuiFieldText) + .prop('value') + ).toBe('Sum'); + }); + + it("should show the correct summary row name when user's changes summary label", () => { + frame.activeData!.first.columns[0].meta.type = 'number'; + state.columns[0].summaryRow = 'sum'; + state.columns[0].summaryLabel = 'MySum'; + const instance = mountWithIntl(); + expect( + instance + .find('[data-test-subj="lnsDatatable_summaryrow_function"]') + .find(EuiComboBox) + .prop('selectedOptions') + ).toEqual([{ value: 'sum', label: 'Sum' }]); + + expect( + instance + .find('[data-test-subj="lnsDatatable_summaryrow_label"]') + .find(EuiFieldText) + .prop('value') + ).toBe('MySum'); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx new file mode 100644 index 0000000000000..c61a512991b62 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { EuiFormRow, EuiFieldText, EuiText, useEuiTheme, EuiComboBox } from '@elastic/eui'; +import { PaletteRegistry } from '@kbn/coloring'; +import { VisualizationDimensionEditorProps } from '../../../types'; +import { DatatableVisualizationState } from '../visualization'; + +import { useDebouncedValue } from '../../../shared_components'; +import type { ColumnState } from '../../../../common/expressions'; +import { + isNumericFieldForDatatable, + getDefaultSummaryLabel, + getFinalSummaryConfiguration, + getSummaryRowOptions, +} from '../../../../common/expressions'; + +import './dimension_editor.scss'; + +type ColumnType = DatatableVisualizationState['columns'][number]; +type SummaryRowType = Extract; + +function updateColumnWith( + state: DatatableVisualizationState, + columnId: string, + newColumnProps: Partial +) { + return state.columns.map((currentColumn) => { + if (currentColumn.columnId === columnId) { + return { ...currentColumn, ...newColumnProps }; + } else { + return currentColumn; + } + }); +} + +export function TableDimensionEditorAdditionalSection( + props: VisualizationDimensionEditorProps & { + paletteService: PaletteRegistry; + } +) { + const { state, setState, frame, accessor } = props; + const column = state.columns.find(({ columnId }) => accessor === columnId); + const onSummaryLabelChangeToDebounce = useCallback( + (newSummaryLabel: string | undefined) => { + setState({ + ...state, + columns: updateColumnWith(state, accessor, { summaryLabel: newSummaryLabel }), + }); + }, + [accessor, setState, state] + ); + const { inputValue: summaryLabel, handleInputChange: onSummaryLabelChange } = useDebouncedValue< + string | undefined + >( + { + onChange: onSummaryLabelChangeToDebounce, + value: column?.summaryLabel, + }, + { allowFalsyValue: true } // falsy values are valid for this feature + ); + + const { euiTheme } = useEuiTheme(); + + if (!column) return null; + if (column.isTransposed) return null; + + const currentData = frame.activeData?.[state.layerId]; + + // either read config state or use same logic as chart itself + const isNumeric = isNumericFieldForDatatable(currentData, accessor); + // when switching from one operation to another, make sure to keep the configuration consistent + const { summaryRow, summaryLabel: fallbackSummaryLabel } = getFinalSummaryConfiguration( + accessor, + column, + currentData + ); + + return ( + <> + {isNumeric && ( +
+ +

+ {i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingSummary', { + defaultMessage: 'Summary', + })} +

+
+ + <> + + { + const newValue = choices[0].value as SummaryRowType; + setState({ + ...state, + columns: updateColumnWith(state, accessor, { summaryRow: newValue }), + }); + }} + /> + + {summaryRow !== 'none' && ( + + { + onSummaryLabelChange(e.target.value); + }} + /> + + )} + +
+ )} + + ); +} diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/row_height_settings.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/row_height_settings.tsx similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/components/row_height_settings.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/row_height_settings.tsx diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts b/x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.test.ts similarity index 99% rename from x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts rename to x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.test.ts index 3d3352309c102..760145e06c33a 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.test.ts @@ -17,7 +17,7 @@ import { createGridHideHandler, createTransposeColumnFilterHandler, } from './table_actions'; -import { LensGridDirection, ColumnConfig } from '../../../common/expressions'; +import { LensGridDirection, ColumnConfig } from '../../../../common/expressions'; function getDefaultConfig(): ColumnConfig { return { diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts b/x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.ts similarity index 96% rename from x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts rename to x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.ts index 5d06584003ae1..b4075ef848989 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/table_actions.ts @@ -9,8 +9,8 @@ import type { EuiDataGridSorting } from '@elastic/eui'; import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; import type { LensResizeAction, LensSortAction, LensToggleAction } from './types'; -import type { ColumnConfig, LensGridDirection } from '../../../common/expressions'; -import { getOriginalId } from '../../../common/expressions'; +import type { ColumnConfig, LensGridDirection } from '../../../../common/expressions'; +import { getOriginalId } from '../../../../common/expressions'; export const createGridResizeHandler = ( diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss b/x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.scss similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss rename to x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.scss diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.test.tsx similarity index 99% rename from x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.test.tsx index 44600b2c03b94..e40b7b0c079ec 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.test.tsx @@ -12,11 +12,11 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { EuiDataGrid } from '@elastic/eui'; import { IAggType } from '@kbn/data-plugin/public'; import { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; -import { VisualizationContainer } from '../../visualization_container'; +import { VisualizationContainer } from '../../../visualization_container'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; -import { LensIconChartDatatable } from '../../assets/chart_datatable'; +import { LensIconChartDatatable } from '../../../assets/chart_datatable'; import { DataContext, DatatableComponent } from './table_basic'; -import { DatatableProps } from '../../../common/expressions'; +import { DatatableProps } from '../../../../common/expressions'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { IUiSettingsClient } from '@kbn/core/public'; import { Datatable, RenderMode } from '@kbn/expressions-plugin/common'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.tsx similarity index 96% rename from x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.tsx index 97d14bed8d06c..0262d3d7cab40 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/table_basic.tsx @@ -29,12 +29,12 @@ import { } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; -import type { LensTableRowContextMenuEvent } from '../../types'; -import type { FormatFactory } from '../../../common'; -import type { LensGridDirection } from '../../../common/expressions'; -import { VisualizationContainer } from '../../visualization_container'; -import { findMinMaxByColumnId } from '../../shared_components'; -import { LensIconChartDatatable } from '../../assets/chart_datatable'; +import type { LensTableRowContextMenuEvent } from '../../../types'; +import type { FormatFactory } from '../../../../common'; +import type { LensGridDirection } from '../../../../common/expressions'; +import { VisualizationContainer } from '../../../visualization_container'; +import { findMinMaxByColumnId } from '../../../shared_components'; +import { LensIconChartDatatable } from '../../../assets/chart_datatable'; import type { DataContextType, DatatableRenderProps, @@ -52,7 +52,7 @@ import { createGridSortingConfig, createTransposeColumnFilterHandler, } from './table_actions'; -import { getOriginalId, getFinalSummaryConfiguration } from '../../../common/expressions'; +import { getOriginalId, getFinalSummaryConfiguration } from '../../../../common/expressions'; export const DataContext = React.createContext({}); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/components/toolbar.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.test.tsx index 6c69b855dd65a..281c4ef9c86a5 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.test.tsx @@ -9,10 +9,10 @@ import React, { ChangeEvent, FormEvent } from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { DataTableToolbar } from './toolbar'; import { DatatableVisualizationState } from '../visualization'; -import { FramePublicAPI, VisualizationToolbarProps } from '../../types'; +import { FramePublicAPI, VisualizationToolbarProps } from '../../../types'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; import { ReactWrapper } from 'enzyme'; -import { PagingState } from '../../../common/expressions'; +import { PagingState } from '../../../../common/expressions'; import { EuiButtonGroup, EuiRange } from '@elastic/eui'; // mocking random id generator function diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx similarity index 96% rename from x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx index 656f8efcc09ee..fee76d99593fd 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx @@ -8,8 +8,8 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFormRow, EuiSwitch, EuiToolTip } from '@elastic/eui'; -import { ToolbarPopover } from '../../shared_components'; -import type { VisualizationToolbarProps } from '../../types'; +import { ToolbarPopover } from '../../../shared_components'; +import type { VisualizationToolbarProps } from '../../../types'; import type { DatatableVisualizationState } from '../visualization'; import { RowHeightSettings } from './row_height_settings'; import { DEFAULT_PAGE_SIZE } from './table_basic'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts b/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts similarity index 95% rename from x-pack/plugins/lens/public/datatable_visualization/components/types.ts rename to x-pack/plugins/lens/public/visualizations/datatable/components/types.ts index c6a1560ce4675..3338e9da8f4ae 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts @@ -10,15 +10,15 @@ import type { PaletteRegistry } from '@kbn/coloring'; import { CustomPaletteState } from '@kbn/charts-plugin/public'; import type { IAggType } from '@kbn/data-plugin/public'; import type { Datatable, RenderMode } from '@kbn/expressions-plugin/common'; -import type { ILensInterpreterRenderHandlers, LensEditEvent } from '../../types'; +import type { ILensInterpreterRenderHandlers, LensEditEvent } from '../../../types'; import { LENS_EDIT_SORT_ACTION, LENS_EDIT_RESIZE_ACTION, LENS_TOGGLE_ACTION, LENS_EDIT_PAGESIZE_ACTION, } from './constants'; -import type { FormatFactory } from '../../../common'; -import type { DatatableProps, LensGridDirection } from '../../../common/expressions'; +import type { FormatFactory } from '../../../../common'; +import type { DatatableProps, LensGridDirection } from '../../../../common/expressions'; export interface LensSortActionData { columnId: string | undefined; diff --git a/x-pack/plugins/lens/public/datatable_visualization/datatable_visualization.ts b/x-pack/plugins/lens/public/visualizations/datatable/datatable_visualization.ts similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/datatable_visualization.ts rename to x-pack/plugins/lens/public/visualizations/datatable/datatable_visualization.ts diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/expression.test.tsx similarity index 92% rename from x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/expression.test.tsx index 6bf6d27f35edd..7bd3bac4e9502 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/expression.test.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import type { DatatableProps } from '../../common/expressions'; +import type { DatatableProps } from '../../../common/expressions'; import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks'; -import type { FormatFactory } from '../../common'; -import { getDatatable } from '../../common/expressions'; +import type { FormatFactory } from '../../../common'; +import { getDatatable } from '../../../common/expressions'; import { Datatable } from '@kbn/expressions-plugin/common'; function sampleArgs() { diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx similarity index 92% rename from x-pack/plugins/lens/public/datatable_visualization/expression.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/expression.tsx index a7ce6b2a9f67c..93a41f9baf532 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx @@ -14,12 +14,12 @@ import type { IAggType } from '@kbn/data-plugin/public'; import { IUiSettingsClient, ThemeServiceStart } from '@kbn/core/public'; import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { trackUiCounterEvents } from '../lens_ui_telemetry'; +import { trackUiCounterEvents } from '../../lens_ui_telemetry'; import { DatatableComponent } from './components/table_basic'; -import type { ILensInterpreterRenderHandlers } from '../types'; -import type { FormatFactory } from '../../common'; -import type { DatatableProps } from '../../common/expressions'; +import type { ILensInterpreterRenderHandlers } from '../../types'; +import type { FormatFactory } from '../../../common'; +import type { DatatableProps } from '../../../common/expressions'; export const getDatatableRenderer = (dependencies: { formatFactory: FormatFactory; diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/visualizations/datatable/index.ts similarity index 91% rename from x-pack/plugins/lens/public/datatable_visualization/index.ts rename to x-pack/plugins/lens/public/visualizations/datatable/index.ts index 1d2821cb49c0e..7999e7246a594 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/visualizations/datatable/index.ts @@ -9,8 +9,8 @@ import type { CoreSetup } from '@kbn/core/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { EditorFrameSetup } from '../types'; -import type { FormatFactory } from '../../common'; +import type { EditorFrameSetup } from '../../types'; +import type { FormatFactory } from '../../../common'; interface DatatableVisualizationPluginStartPlugins { data: DataPublicPluginStart; @@ -28,7 +28,9 @@ export class DatatableVisualization { { expressions, formatFactory, editorFrame, charts }: DatatableVisualizationPluginSetupPlugins ) { editorFrame.registerVisualization(async () => { - const { getDatatableRenderer, getDatatableVisualization } = await import('../async_services'); + const { getDatatableRenderer, getDatatableVisualization } = await import( + '../../async_services' + ); const palettes = await charts.palettes.getPalettes(); expressions.registerRenderer(() => getDatatableRenderer({ diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx similarity index 99% rename from x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx index 87dc93b71f871..494be445670ae 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx @@ -7,7 +7,7 @@ import { Ast } from '@kbn/interpreter'; import { buildExpression } from '@kbn/expressions-plugin/public'; -import { createMockDatasource, createMockFramePublicAPI, DatasourceMock } from '../mocks'; +import { createMockDatasource, createMockFramePublicAPI, DatasourceMock } from '../../mocks'; import { DatatableVisualizationState, getDatatableVisualization } from './visualization'; import { Operation, @@ -15,9 +15,9 @@ import { FramePublicAPI, TableSuggestionColumn, VisualizationDimensionGroupConfig, -} from '../types'; +} from '../../types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { themeServiceMock } from '@kbn/core/public/mocks'; function mockFrame(): FramePublicAPI { diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx similarity index 93% rename from x-pack/plugins/lens/public/datatable_visualization/visualization.tsx rename to x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx index d30a65e92661a..2ccef5a89b1ba 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx @@ -19,12 +19,13 @@ import type { Visualization, VisualizationSuggestion, DatasourceLayers, -} from '../types'; -import { LensIconChartDatatable } from '../assets/chart_datatable'; +} from '../../types'; +import { LensIconChartDatatable } from '../../assets/chart_datatable'; import { TableDimensionEditor } from './components/dimension_editor'; -import { LayerType, layerTypes } from '../../common'; -import { getDefaultSummaryLabel, PagingState } from '../../common/expressions'; -import type { ColumnState, SortingState } from '../../common/expressions'; +import { TableDimensionEditorAdditionalSection } from './components/dimension_editor_addtional_section'; +import { LayerType, layerTypes } from '../../../common'; +import { getDefaultSummaryLabel, PagingState } from '../../../common/expressions'; +import type { ColumnState, SortingState } from '../../../common/expressions'; import { DataTableToolbar } from './components/toolbar'; export interface DatatableVisualizationState { @@ -190,6 +191,9 @@ export const getDatatableVisualization = ({ groupLabel: i18n.translate('xpack.lens.datatable.breakdownRows', { defaultMessage: 'Rows', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.datatable.breakdownRow', { + defaultMessage: 'Row', + }), groupTooltip: i18n.translate('xpack.lens.datatable.breakdownRows.description', { defaultMessage: 'Split table rows by field. This is recommended for high cardinality breakdowns.', @@ -221,6 +225,9 @@ export const getDatatableVisualization = ({ groupLabel: i18n.translate('xpack.lens.datatable.breakdownColumns', { defaultMessage: 'Columns', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.datatable.breakdownColumn', { + defaultMessage: 'Column', + }), groupTooltip: i18n.translate('xpack.lens.datatable.breakdownColumns.description', { defaultMessage: "Split metric columns by field. It's recommended to keep the number of columns low to avoid horizontal scrolling.", @@ -245,6 +252,14 @@ export const getDatatableVisualization = ({ groupLabel: i18n.translate('xpack.lens.datatable.metrics', { defaultMessage: 'Metrics', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.datatable.metric', { + defaultMessage: 'Metric', + }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.datatable.headingLabel', { + defaultMessage: 'Value', + }), + }, layerId: state.layerId, accessors: sortedColumns .filter((c) => !datasource!.getOperationForColumnId(c)?.isBucketed) @@ -313,6 +328,17 @@ export const getDatatableVisualization = ({ ); }, + renderDimensionEditorAdditionalSection(domElement, props) { + render( + + + + + , + domElement + ); + }, + getSupportedLayers() { return [ { diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts index 7b10565e3d092..7282259108a92 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts @@ -97,6 +97,9 @@ describe('gauge', () => { groups: [ { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.METRIC, groupLabel: 'Metric', accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], @@ -109,6 +112,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Minimum value'], + }, groupId: GROUP_ID.MIN, groupLabel: 'Minimum value', accessors: [{ columnId: 'min-accessor' }], @@ -122,6 +129,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Maximum value'], + }, groupId: GROUP_ID.MAX, groupLabel: 'Maximum value', accessors: [{ columnId: 'max-accessor' }], @@ -135,6 +146,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Goal value'], + }, groupId: GROUP_ID.GOAL, groupLabel: 'Goal value', accessors: [{ columnId: 'goal-accessor' }], @@ -164,6 +179,9 @@ describe('gauge', () => { groups: [ { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.METRIC, groupLabel: 'Metric', accessors: [], @@ -176,6 +194,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Minimum value'], + }, groupId: GROUP_ID.MIN, groupLabel: 'Minimum value', accessors: [{ columnId: 'min-accessor' }], @@ -189,6 +211,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Maximum value'], + }, groupId: GROUP_ID.MAX, groupLabel: 'Maximum value', accessors: [], @@ -202,6 +228,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Goal value'], + }, groupId: GROUP_ID.GOAL, groupLabel: 'Goal value', accessors: [], @@ -237,6 +267,9 @@ describe('gauge', () => { groups: [ { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.METRIC, groupLabel: 'Metric', accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], @@ -249,6 +282,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Minimum value'], + }, groupId: GROUP_ID.MIN, groupLabel: 'Minimum value', accessors: [{ columnId: 'min-accessor' }], @@ -262,6 +299,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Maximum value'], + }, groupId: GROUP_ID.MAX, groupLabel: 'Maximum value', accessors: [{ columnId: 'max-accessor' }], @@ -275,6 +316,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Goal value'], + }, groupId: GROUP_ID.GOAL, groupLabel: 'Goal value', accessors: [{ columnId: 'goal-accessor' }], @@ -315,6 +360,9 @@ describe('gauge', () => { groups: [ { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.METRIC, groupLabel: 'Metric', accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], @@ -327,6 +375,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Minimum value'], + }, groupId: GROUP_ID.MIN, groupLabel: 'Minimum value', accessors: [{ columnId: 'min-accessor' }], @@ -342,6 +394,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Maximum value'], + }, groupId: GROUP_ID.MAX, groupLabel: 'Maximum value', accessors: [{ columnId: 'max-accessor' }], @@ -357,6 +413,10 @@ describe('gauge', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + labels: ['Goal value'], + }, groupId: GROUP_ID.GOAL, groupLabel: 'Goal value', accessors: [{ columnId: 'goal-accessor' }], diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index f4f8fd41c7f83..95512e535790b 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -255,6 +255,11 @@ export const getGaugeVisualization = ({ groupLabel: i18n.translate('xpack.lens.gauge.metricLabel', { defaultMessage: 'Metric', }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.gauge.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: metricAccessor ? [ palette @@ -283,6 +288,16 @@ export const getGaugeVisualization = ({ groupLabel: i18n.translate('xpack.lens.gauge.minValueLabel', { defaultMessage: 'Minimum value', }), + paramEditorCustomProps: { + labels: [ + i18n.translate('xpack.lens.gauge.minValueLabel', { + defaultMessage: 'Minimum value', + }), + ], + headingLabel: i18n.translate('xpack.lens.gauge.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: state.minAccessor ? [{ columnId: state.minAccessor }] : [], filterOperations: isNumericMetric, supportsMoreColumns: !state.minAccessor, @@ -299,6 +314,16 @@ export const getGaugeVisualization = ({ groupLabel: i18n.translate('xpack.lens.gauge.maxValueLabel', { defaultMessage: 'Maximum value', }), + paramEditorCustomProps: { + labels: [ + i18n.translate('xpack.lens.gauge.maxValueLabel', { + defaultMessage: 'Maximum value', + }), + ], + headingLabel: i18n.translate('xpack.lens.gauge.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: state.maxAccessor ? [{ columnId: state.maxAccessor }] : [], filterOperations: isNumericMetric, supportsMoreColumns: !state.maxAccessor, @@ -315,6 +340,16 @@ export const getGaugeVisualization = ({ groupLabel: i18n.translate('xpack.lens.gauge.goalValueLabel', { defaultMessage: 'Goal value', }), + paramEditorCustomProps: { + labels: [ + i18n.translate('xpack.lens.gauge.goalValueLabel', { + defaultMessage: 'Goal value', + }), + ], + headingLabel: i18n.translate('xpack.lens.gauge.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: state.goalAccessor ? [{ columnId: state.goalAccessor }] : [], filterOperations: isNumericMetric, supportsMoreColumns: !state.goalAccessor, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/constants.ts b/x-pack/plugins/lens/public/visualizations/heatmap/constants.ts similarity index 100% rename from x-pack/plugins/lens/public/heatmap_visualization/constants.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/constants.ts diff --git a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.scss b/x-pack/plugins/lens/public/visualizations/heatmap/dimension_editor.scss similarity index 100% rename from x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.scss rename to x-pack/plugins/lens/public/visualizations/heatmap/dimension_editor.scss diff --git a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/dimension_editor.tsx similarity index 96% rename from x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx rename to x-pack/plugins/lens/public/visualizations/heatmap/dimension_editor.tsx index ae7f01a7cebd3..ef0700b7ca9fe 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/dimension_editor.tsx @@ -15,8 +15,8 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { CustomizablePalette, FIXED_PROGRESSION, PaletteRegistry } from '@kbn/coloring'; -import type { VisualizationDimensionEditorProps } from '../types'; -import { PalettePanelContainer } from '../shared_components'; +import type { VisualizationDimensionEditorProps } from '../../types'; +import { PalettePanelContainer } from '../../shared_components'; import './dimension_editor.scss'; import type { HeatmapVisualizationState } from './types'; import { getSafePaletteParams } from './utils'; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/heatmap_visualization.ts b/x-pack/plugins/lens/public/visualizations/heatmap/heatmap_visualization.ts similarity index 100% rename from x-pack/plugins/lens/public/heatmap_visualization/heatmap_visualization.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/heatmap_visualization.ts diff --git a/x-pack/plugins/lens/public/heatmap_visualization/index.ts b/x-pack/plugins/lens/public/visualizations/heatmap/index.ts similarity index 86% rename from x-pack/plugins/lens/public/heatmap_visualization/index.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/index.ts index e6bebd087c5b3..dd132c371d715 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/index.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/index.ts @@ -7,7 +7,7 @@ import type { CoreSetup } from '@kbn/core/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { EditorFrameSetup } from '../types'; +import type { EditorFrameSetup } from '../../types'; export interface HeatmapVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; @@ -17,7 +17,7 @@ export interface HeatmapVisualizationPluginSetupPlugins { export class HeatmapVisualization { setup(core: CoreSetup, { editorFrame, charts }: HeatmapVisualizationPluginSetupPlugins) { editorFrame.registerVisualization(async () => { - const { getHeatmapVisualization } = await import('../async_services'); + const { getHeatmapVisualization } = await import('../../async_services'); const palettes = await charts.palettes.getPalettes(); return getHeatmapVisualization({ paletteService: palettes, theme: core.theme }); diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts similarity index 99% rename from x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts index dbe2d9a7c9771..70db8e0d4165a 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts @@ -9,7 +9,7 @@ import { Position } from '@elastic/charts'; import { getSuggestions } from './suggestions'; import type { HeatmapVisualizationState } from './types'; import { HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; describe('heatmap suggestions', () => { describe('rejects suggestions', () => { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts similarity index 97% rename from x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts index c13b6eb655e3b..a302be4cfceb6 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts @@ -8,10 +8,10 @@ import { partition } from 'lodash'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import type { Visualization } from '../types'; +import type { Visualization } from '../../types'; import type { HeatmapVisualizationState } from './types'; import { CHART_SHAPES, HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; export const getSuggestions: Visualization['getSuggestions'] = ({ table, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.scss b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.scss similarity index 100% rename from x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.scss rename to x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.scss diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx similarity index 96% rename from x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx rename to x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx index d653649dc1048..b0a80ca297a0a 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx @@ -10,18 +10,18 @@ import { EuiFlexGroup, EuiFlexItem, IconType } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { LegendSize } from '@kbn/visualizations-plugin/public'; -import type { VisualizationToolbarProps } from '../types'; +import type { VisualizationToolbarProps } from '../../types'; import { LegendSettingsPopover, ToolbarPopover, ValueLabelsSettings, AxisTitleSettings, TooltipWrapper, -} from '../shared_components'; -import { EuiIconAxisLeft } from '../assets/axis_left'; -import { EuiIconAxisBottom } from '../assets/axis_bottom'; +} from '../../shared_components'; +import { EuiIconAxisLeft } from '../../assets/axis_left'; +import { EuiIconAxisBottom } from '../../assets/axis_bottom'; import type { HeatmapVisualizationState } from './types'; -import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; +import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import './toolbar_component.scss'; const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [ diff --git a/x-pack/plugins/lens/public/heatmap_visualization/types.ts b/x-pack/plugins/lens/public/visualizations/heatmap/types.ts similarity index 94% rename from x-pack/plugins/lens/public/heatmap_visualization/types.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/types.ts index 1b92dca12ac25..08913ad25a7d3 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/types.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/types.ts @@ -7,7 +7,7 @@ import type { PaletteOutput, CustomPaletteParams } from '@kbn/coloring'; import type { HeatmapArguments } from '@kbn/expression-heatmap-plugin/common'; -import type { LayerType } from '../../common'; +import type { LayerType } from '../../../common'; export type ChartShapes = 'heatmap'; export type HeatmapLayerState = HeatmapArguments & { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/utils.ts b/x-pack/plugins/lens/public/visualizations/heatmap/utils.ts similarity index 93% rename from x-pack/plugins/lens/public/heatmap_visualization/utils.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/utils.ts index 66c70381a560d..5e09ce2987bae 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/utils.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/utils.ts @@ -7,7 +7,7 @@ import type { PaletteRegistry } from '@kbn/coloring'; import type { Datatable } from '@kbn/expressions-plugin/common'; -import { applyPaletteParams, findMinMaxByColumnId } from '../shared_components'; +import { applyPaletteParams, findMinMaxByColumnId } from '../../shared_components'; import { DEFAULT_PALETTE_NAME } from './constants'; import type { HeatmapVisualizationState, Palette } from './types'; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts similarity index 97% rename from x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts rename to x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts index afec1743d47f5..ee6a7030a0c9d 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts @@ -10,7 +10,7 @@ import { getHeatmapVisualization, isCellValueSupported, } from './visualization'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { CHART_SHAPES, FUNCTION_NAME, @@ -20,9 +20,9 @@ import { } from './constants'; import { Position } from '@elastic/charts'; import type { HeatmapVisualizationState } from './types'; -import type { DatasourceLayers, OperationDescriptor } from '../types'; +import type { DatasourceLayers, OperationDescriptor } from '../../types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { themeServiceMock } from '@kbn/core/public/mocks'; function exampleState(): HeatmapVisualizationState { @@ -151,6 +151,9 @@ describe('heatmap', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.CELL, groupLabel: 'Cell value', accessors: [ @@ -206,6 +209,9 @@ describe('heatmap', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.CELL, groupLabel: 'Cell value', accessors: [], @@ -259,6 +265,9 @@ describe('heatmap', () => { }, { layerId: 'first', + paramEditorCustomProps: { + headingLabel: 'Value', + }, groupId: GROUP_ID.CELL, groupLabel: 'Cell value', accessors: [ diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx similarity index 98% rename from x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx rename to x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx index afda41dd8f7b8..9c97aa4dab605 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx @@ -16,7 +16,7 @@ import { ThemeServiceStart } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { HeatmapIcon } from '@kbn/expression-heatmap-plugin/public'; -import type { OperationMetadata, Visualization } from '../types'; +import type { OperationMetadata, Visualization } from '../../types'; import type { HeatmapVisualizationState } from './types'; import { getSuggestions } from './suggestions'; import { @@ -32,7 +32,7 @@ import { import { HeatmapToolbar } from './toolbar_component'; import { HeatmapDimensionEditor } from './dimension_editor'; import { getSafePaletteParams } from './utils'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; const groupLabelForHeatmap = i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', { defaultMessage: 'Magnitude', @@ -200,6 +200,11 @@ export const getHeatmapVisualization = ({ groupLabel: i18n.translate('xpack.lens.heatmap.cellValueLabel', { defaultMessage: 'Cell value', }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.heatmap.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: state.valueAccessor ? [ // When data is not available and the range type is numeric, return a placeholder while refreshing diff --git a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.scss b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.scss similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/dimension_editor.scss rename to x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.scss diff --git a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx similarity index 97% rename from x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx index c83e83aad7b1b..0deb214979786 100644 --- a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { EuiButtonGroup } from '@elastic/eui'; -import { FramePublicAPI, VisualizationDimensionEditorProps } from '../types'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; +import { FramePublicAPI, VisualizationDimensionEditorProps } from '../../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { MetricDimensionEditor } from './dimension_editor'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -21,9 +21,9 @@ import { } from '@kbn/coloring'; import { act } from 'react-dom/test-utils'; -import { PalettePanelContainer } from '../shared_components'; -import { layerTypes } from '../../common'; -import type { MetricState } from '../../common/types'; +import { PalettePanelContainer } from '../../shared_components'; +import { layerTypes } from '../../../common'; +import type { MetricState } from '../../../common/types'; // mocking random id generator function jest.mock('@elastic/eui', () => { diff --git a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx similarity index 96% rename from x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx index b3a2e7207e77d..5acfe97c5b3ef 100644 --- a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx @@ -22,10 +22,10 @@ import { import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; import { ColorMode } from '@kbn/charts-plugin/common'; -import type { MetricState } from '../../common/types'; -import { isNumericFieldForDatatable } from '../../common/expressions'; -import { applyPaletteParams, PalettePanelContainer } from '../shared_components'; -import type { VisualizationDimensionEditorProps } from '../types'; +import type { MetricState } from '../../../common/types'; +import { isNumericFieldForDatatable } from '../../../common/expressions'; +import { applyPaletteParams, PalettePanelContainer } from '../../shared_components'; +import type { VisualizationDimensionEditorProps } from '../../types'; import { defaultPaletteParams } from './palette_config'; import './dimension_editor.scss'; diff --git a/x-pack/plugins/lens/public/metric_visualization/index.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/index.ts similarity index 91% rename from x-pack/plugins/lens/public/metric_visualization/index.ts rename to x-pack/plugins/lens/public/visualizations/legacy_metric/index.ts index ce733a033b6da..933bf4dab6ae8 100644 --- a/x-pack/plugins/lens/public/metric_visualization/index.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/index.ts @@ -7,7 +7,7 @@ import type { CoreSetup } from '@kbn/core/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { EditorFrameSetup } from '../types'; +import type { EditorFrameSetup } from '../../types'; export interface LegacyMetricVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; @@ -18,7 +18,7 @@ export class LegacyMetricVisualization { setup(core: CoreSetup, { editorFrame, charts }: LegacyMetricVisualizationPluginSetupPlugins) { editorFrame.registerVisualization(async () => { const { getLegacyMetricVisualization: getMetricVisualization } = await import( - '../async_services' + '../../async_services' ); const palettes = await charts.palettes.getPalettes(); diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/align_options.tsx similarity index 96% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/align_options.tsx index cf077e6eaa77f..0da058db3d5b5 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/align_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup } from '@elastic/eui'; -import { MetricState } from '../../../common/types'; +import { MetricState } from '../../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx similarity index 87% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx index 280a036ab5daf..14cf0860db2f1 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx @@ -7,10 +7,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ToolbarPopover, TooltipWrapper } from '../../shared_components'; +import { ToolbarPopover, TooltipWrapper } from '../../../shared_components'; import { TitlePositionOptions } from './title_position_option'; -import { FramePublicAPI } from '../../types'; -import type { MetricState } from '../../../common/types'; +import { FramePublicAPI } from '../../../types'; +import type { MetricState } from '../../../../common/types'; import { TextFormattingOptions } from './text_formatting_options'; export interface VisualOptionsPopoverProps { diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx similarity index 89% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx index 947115fcee5db..0988aefa24492 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx @@ -7,8 +7,8 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, htmlIdGenerator } from '@elastic/eui'; -import type { VisualizationToolbarProps } from '../../types'; -import type { MetricState } from '../../../common/types'; +import type { VisualizationToolbarProps } from '../../../types'; +import type { MetricState } from '../../../../common/types'; import { AppearanceOptionsPopover } from './appearance_options_popover'; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/size_options.tsx similarity index 97% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/size_options.tsx index 40af2d9a0e717..94efe0e00231e 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/size_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiSuperSelect } from '@elastic/eui'; -import type { MetricState } from '../../../common/types'; +import type { MetricState } from '../../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/text_formatting_options.tsx similarity index 95% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/text_formatting_options.tsx index 149d66a2db3cb..8a1f97cd8251e 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/text_formatting_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { MetricState } from '../../../common/types'; +import type { MetricState } from '../../../../common/types'; import { SizeOptions } from './size_options'; import { AlignOptions } from './align_options'; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/title_position_option.tsx similarity index 96% rename from x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/title_position_option.tsx index 63abd195d6e5e..233a2e382669f 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/title_position_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; -import type { MetricState } from '../../../common/types'; +import type { MetricState } from '../../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts similarity index 98% rename from x-pack/plugins/lens/public/metric_visualization/metric_suggestions.test.ts rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts index 791d282bfe9d5..e1803e106f2eb 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts @@ -6,7 +6,7 @@ */ import { getSuggestions } from './metric_suggestions'; -import { TableSuggestionColumn, TableSuggestion } from '../types'; +import { TableSuggestionColumn, TableSuggestion } from '../../types'; describe('metric_suggestions', () => { function numCol(columnId: string): TableSuggestionColumn { diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts similarity index 89% rename from x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts index 4485571ffdb8d..32a5831618251 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types'; -import type { MetricState } from '../../common/types'; -import { layerTypes } from '../../common'; -import { LensIconChartMetric } from '../assets/chart_metric'; +import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../../types'; +import type { MetricState } from '../../../common/types'; +import { layerTypes } from '../../../common'; +import { LensIconChartMetric } from '../../assets/chart_metric'; import { legacyMetricSupportedTypes } from './visualization'; /** diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_visualization.ts similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_visualization.ts diff --git a/x-pack/plugins/lens/public/metric_visualization/palette_config.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/palette_config.tsx similarity index 96% rename from x-pack/plugins/lens/public/metric_visualization/palette_config.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/palette_config.tsx index b44e94d84e0a9..e34533c058e7f 100644 --- a/x-pack/plugins/lens/public/metric_visualization/palette_config.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/palette_config.tsx @@ -6,7 +6,7 @@ */ import { RequiredPaletteParamTypes } from '@kbn/coloring'; -import { defaultPaletteParams as sharedDefaultParams } from '../shared_components'; +import { defaultPaletteParams as sharedDefaultParams } from '../../shared_components'; export const DEFAULT_PALETTE_NAME = 'status'; export const DEFAULT_COLOR_STEPS = 3; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts similarity index 97% rename from x-pack/plugins/lens/public/metric_visualization/visualization.test.ts rename to x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts index d6b14e5e574c8..399c29797ff83 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.test.ts @@ -6,16 +6,16 @@ */ import { getLegacyMetricVisualization } from './visualization'; -import type { MetricState } from '../../common/types'; -import { layerTypes } from '../../common'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; -import { generateId } from '../id_generator'; -import { DatasourcePublicAPI, FramePublicAPI } from '../types'; +import type { MetricState } from '../../../common/types'; +import { layerTypes } from '../../../common'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { generateId } from '../../id_generator'; +import { DatasourcePublicAPI, FramePublicAPI } from '../../types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { ColorMode } from '@kbn/charts-plugin/common'; import { themeServiceMock } from '@kbn/core/public/mocks'; -jest.mock('../id_generator'); +jest.mock('../../id_generator'); function exampleState(): MetricState { return { diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx similarity index 96% rename from x-pack/plugins/lens/public/metric_visualization/visualization.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx index 51087e9e44010..5550341a81e8b 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx @@ -15,10 +15,10 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { ColorMode, CustomPaletteState } from '@kbn/charts-plugin/common'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { getSuggestions } from './metric_suggestions'; -import { LensIconChartMetric } from '../assets/chart_metric'; -import { Visualization, OperationMetadata, DatasourceLayers } from '../types'; -import type { MetricState } from '../../common/types'; -import { layerTypes } from '../../common'; +import { LensIconChartMetric } from '../../assets/chart_metric'; +import { Visualization, OperationMetadata, DatasourceLayers } from '../../types'; +import type { MetricState } from '../../../common/types'; +import { layerTypes } from '../../../common'; import { MetricDimensionEditor } from './dimension_editor'; import { MetricToolbar } from './metric_config_panel'; import { DEFAULT_TITLE_POSITION } from './metric_config_panel/title_position_option'; @@ -235,6 +235,11 @@ export const getLegacyMetricVisualization = ({ groups: [ { groupId: 'metric', + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.metric.headingLabel', { + defaultMessage: 'Value', + }), + }, groupLabel: i18n.translate('xpack.lens.legacyMetric.label', { defaultMessage: 'Legacy Metric', }), diff --git a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap index 62bb2fa3e3f67..ffcbcafc405c6 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap @@ -16,6 +16,9 @@ Object { "groupId": "metric", "groupLabel": "Primary metric", "layerId": "first", + "paramEditorCustomProps": Object { + "headingLabel": "Value", + }, "required": true, "supportFieldFormat": false, "supportsMoreColumns": false, @@ -31,6 +34,9 @@ Object { "groupId": "secondaryMetric", "groupLabel": "Secondary metric", "layerId": "first", + "paramEditorCustomProps": Object { + "headingLabel": "Value", + }, "required": false, "supportFieldFormat": false, "supportsMoreColumns": false, @@ -47,6 +53,9 @@ Object { "groupLabel": "Maximum value", "groupTooltip": "If the maximum value is specified, the minimum value is fixed at zero.", "layerId": "first", + "paramEditorCustomProps": Object { + "headingLabel": "Value", + }, "required": false, "supportFieldFormat": false, "supportStaticValue": true, diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index 8a64d4e5bf705..127d3da0e2c9c 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -233,6 +233,11 @@ export const getMetricVisualization = ({ groupLabel: i18n.translate('xpack.lens.primaryMetric.label', { defaultMessage: 'Primary metric', }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.primaryMetric.headingLabel', { + defaultMessage: 'Value', + }), + }, layerId: props.state.layerId, accessors: props.state.metricAccessor ? [ @@ -254,6 +259,11 @@ export const getMetricVisualization = ({ groupLabel: i18n.translate('xpack.lens.metric.secondaryMetric', { defaultMessage: 'Secondary metric', }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.primaryMetric.headingLabel', { + defaultMessage: 'Value', + }), + }, layerId: props.state.layerId, accessors: props.state.secondaryMetricAccessor ? [ @@ -271,6 +281,11 @@ export const getMetricVisualization = ({ { groupId: GROUP_ID.MAX, groupLabel: i18n.translate('xpack.lens.metric.max', { defaultMessage: 'Maximum value' }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.primaryMetric.headingLabel', { + defaultMessage: 'Value', + }), + }, layerId: props.state.layerId, accessors: props.state.maxAccessor ? [ diff --git a/x-pack/plugins/lens/public/pie_visualization/constants.ts b/x-pack/plugins/lens/public/visualizations/partition/constants.ts similarity index 100% rename from x-pack/plugins/lens/public/pie_visualization/constants.ts rename to x-pack/plugins/lens/public/visualizations/partition/constants.ts diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/visualizations/partition/index.ts similarity index 88% rename from x-pack/plugins/lens/public/pie_visualization/index.ts rename to x-pack/plugins/lens/public/visualizations/partition/index.ts index 6b7a9d68115d6..09e98c6c6c806 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/index.ts @@ -8,7 +8,7 @@ import type { CoreSetup } from '@kbn/core/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { EditorFrameSetup } from '../types'; +import type { EditorFrameSetup } from '../../types'; export interface PieVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; @@ -22,7 +22,7 @@ export interface PieVisualizationPluginStartPlugins { export class PieVisualization { setup(core: CoreSetup, { editorFrame, charts }: PieVisualizationPluginSetupPlugins) { editorFrame.registerVisualization(async () => { - const { getPieVisualization } = await import('../async_services'); + const { getPieVisualization } = await import('../../async_services'); const palettes = await charts.palettes.getPalettes(); return getPieVisualization({ paletteService: palettes, kibanaTheme: core.theme }); diff --git a/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts b/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts similarity index 91% rename from x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts rename to x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts index be2ddda9372b9..6d948ce4332fc 100644 --- a/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts @@ -9,13 +9,18 @@ import { i18n } from '@kbn/i18n'; import type { EuiIconProps } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { LensIconChartDonut } from '../assets/chart_donut'; -import { LensIconChartPie } from '../assets/chart_pie'; -import { LensIconChartTreemap } from '../assets/chart_treemap'; -import { LensIconChartMosaic } from '../assets/chart_mosaic'; -import { LensIconChartWaffle } from '../assets/chart_waffle'; -import { CategoryDisplay, NumberDisplay, SharedPieLayerState, EmptySizeRatios } from '../../common'; -import type { PieChartType } from '../../common/types'; +import { LensIconChartDonut } from '../../assets/chart_donut'; +import { LensIconChartPie } from '../../assets/chart_pie'; +import { LensIconChartTreemap } from '../../assets/chart_treemap'; +import { LensIconChartMosaic } from '../../assets/chart_mosaic'; +import { LensIconChartWaffle } from '../../assets/chart_waffle'; +import { + CategoryDisplay, + NumberDisplay, + SharedPieLayerState, + EmptySizeRatios, +} from '../../../common'; +import type { PieChartType } from '../../../common/types'; interface PartitionChartMeta { icon: ({ title, titleId, ...props }: Omit) => JSX.Element; diff --git a/x-pack/plugins/lens/public/pie_visualization/pie_visualization.ts b/x-pack/plugins/lens/public/visualizations/partition/pie_visualization.ts similarity index 100% rename from x-pack/plugins/lens/public/pie_visualization/pie_visualization.ts rename to x-pack/plugins/lens/public/visualizations/partition/pie_visualization.ts diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts b/x-pack/plugins/lens/public/visualizations/partition/render_helpers.test.ts similarity index 97% rename from x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts rename to x-pack/plugins/lens/public/visualizations/partition/render_helpers.test.ts index 6fe4d9b60420e..b7137c3f8e067 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/render_helpers.test.ts @@ -8,7 +8,7 @@ import type { Datatable } from '@kbn/expressions-plugin/public'; import { checkTableForContainsSmallValues, shouldShowValuesInLegend } from './render_helpers'; -import { PieLayerState, PieChartTypes } from '../../common'; +import { PieLayerState, PieChartTypes } from '../../../common'; describe('render helpers', () => { describe('#checkTableForContainsSmallValues', () => { diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/visualizations/partition/render_helpers.ts similarity index 93% rename from x-pack/plugins/lens/public/pie_visualization/render_helpers.ts rename to x-pack/plugins/lens/public/visualizations/partition/render_helpers.ts index b34385d55b44f..bce1031d22133 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/render_helpers.ts @@ -6,7 +6,7 @@ */ import type { Datatable } from '@kbn/expressions-plugin/public'; -import type { PieChartType, PieLayerState } from '../../common/types'; +import type { PieChartType, PieLayerState } from '../../../common/types'; import { PartitionChartsMeta } from './partition_charts_meta'; export const isPartitionShape = (shape: PieChartType | string) => diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts similarity index 99% rename from x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts rename to x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index 44fbaebf91fc0..fc449113adfa2 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -7,7 +7,7 @@ import type { PaletteOutput } from '@kbn/coloring'; import { suggestions } from './suggestions'; -import type { DataType, SuggestionRequest } from '../types'; +import type { DataType, SuggestionRequest } from '../../types'; import { CategoryDisplay, LegendDisplay, @@ -15,8 +15,8 @@ import { PieChartTypes, PieLayerState, PieVisualizationState, -} from '../../common'; -import { layerTypes } from '../../common'; +} from '../../../common'; +import { layerTypes } from '../../../common'; describe('suggestions', () => { describe('pie', () => { diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts similarity index 98% rename from x-pack/plugins/lens/public/pie_visualization/suggestions.ts rename to x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index 176fa2d5026ee..f2541493be339 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -7,7 +7,11 @@ import { partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import type { SuggestionRequest, TableSuggestionColumn, VisualizationSuggestion } from '../types'; +import type { + SuggestionRequest, + TableSuggestionColumn, + VisualizationSuggestion, +} from '../../types'; import { CategoryDisplay, layerTypes, @@ -15,8 +19,8 @@ import { NumberDisplay, PieChartTypes, PieVisualizationState, -} from '../../common'; -import type { PieChartType } from '../../common/types'; +} from '../../../common'; +import type { PieChartType } from '../../../common/types'; import { PartitionChartsMeta } from './partition_charts_meta'; import { isPartitionShape } from './render_helpers'; diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts similarity index 98% rename from x-pack/plugins/lens/public/pie_visualization/to_expression.ts rename to x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 20b7199bbc4cd..6fa209a26a829 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -10,7 +10,7 @@ import { Position } from '@elastic/charts'; import type { PaletteOutput, PaletteRegistry } from '@kbn/coloring'; import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/public'; -import type { Operation, DatasourcePublicAPI, DatasourceLayers } from '../types'; +import type { Operation, DatasourcePublicAPI, DatasourceLayers } from '../../types'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { shouldShowValuesInLegend } from './render_helpers'; import { @@ -21,8 +21,8 @@ import { PieVisualizationState, EmptySizeRatios, LegendDisplay, -} from '../../common'; -import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; +} from '../../../common'; +import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; interface Attributes { isPreview: boolean; diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.scss b/x-pack/plugins/lens/public/visualizations/partition/toolbar.scss similarity index 100% rename from x-pack/plugins/lens/public/pie_visualization/toolbar.scss rename to x-pack/plugins/lens/public/visualizations/partition/toolbar.scss diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx similarity index 98% rename from x-pack/plugins/lens/public/pie_visualization/toolbar.tsx rename to x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx index aa17098cfad5d..e72bc705ae237 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx @@ -21,15 +21,15 @@ import type { PaletteRegistry } from '@kbn/coloring'; import { LegendSize } from '@kbn/visualizations-plugin/public'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { PartitionChartsMeta } from './partition_charts_meta'; -import { LegendDisplay, PieVisualizationState, SharedPieLayerState } from '../../common'; -import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types'; +import { LegendDisplay, PieVisualizationState, SharedPieLayerState } from '../../../common'; +import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../../types'; import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue, PalettePicker, -} from '../shared_components'; -import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; +} from '../../shared_components'; +import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { shouldShowValuesInLegend } from './render_helpers'; const legendOptions: Array<{ diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts similarity index 95% rename from x-pack/plugins/lens/public/pie_visualization/visualization.test.ts rename to x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index a911b5f0e1ba3..1b46c6eefea58 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -12,14 +12,14 @@ import { CategoryDisplay, NumberDisplay, LegendDisplay, -} from '../../common'; -import { layerTypes } from '../../common'; +} from '../../../common'; +import { layerTypes } from '../../../common'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; -import { FramePublicAPI } from '../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { FramePublicAPI } from '../../types'; import { themeServiceMock } from '@kbn/core/public/mocks'; -jest.mock('../id_generator'); +jest.mock('../../id_generator'); const LAYER_ID = 'l1'; diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx similarity index 93% rename from x-pack/plugins/lens/public/pie_visualization/visualization.tsx rename to x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index f5c24c563ba37..5e453838aef51 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -18,14 +18,14 @@ import type { OperationMetadata, AccessorConfig, VisualizationDimensionGroupConfig, -} from '../types'; +} from '../../types'; import { getSortedGroups, toExpression, toPreviewExpression } from './to_expression'; -import { CategoryDisplay, layerTypes, LegendDisplay, NumberDisplay } from '../../common'; +import { CategoryDisplay, layerTypes, LegendDisplay, NumberDisplay } from '../../../common'; import { suggestions } from './suggestions'; import { PartitionChartsMeta } from './partition_charts_meta'; import { DimensionEditor, PieToolbar } from './toolbar'; import { checkTableForContainsSmallValues } from './render_helpers'; -import { PieChartTypes, PieLayerState, PieVisualizationState } from '../../common'; +import { PieChartTypes, PieLayerState, PieVisualizationState } from '../../../common'; function newLayerState(layerId: string): PieLayerState { return { @@ -149,6 +149,9 @@ export const getPieVisualization = ({ groupLabel: i18n.translate('xpack.lens.pie.sliceGroupLabel', { defaultMessage: 'Slice by', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.sliceDimensionGroupLabel', { + defaultMessage: 'Slice', + }), supportsMoreColumns: sortedColumns.length < PartitionChartsMeta.pie.maxBuckets, dataTestSubj: 'lnsPie_sliceByDimensionPanel', }; @@ -158,6 +161,9 @@ export const getPieVisualization = ({ groupLabel: i18n.translate('xpack.lens.pie.treemapGroupLabel', { defaultMessage: 'Group by', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.treemapDimensionGroupLabel', { + defaultMessage: 'Group', + }), supportsMoreColumns: sortedColumns.length < PartitionChartsMeta[state.shape].maxBuckets, dataTestSubj: 'lnsPie_groupByDimensionPanel', requiredMinDimensionCount: PartitionChartsMeta[state.shape].requiredMinDimensionCount, @@ -170,6 +176,14 @@ export const getPieVisualization = ({ groupLabel: i18n.translate('xpack.lens.pie.groupsizeLabel', { defaultMessage: 'Size by', }), + dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupSizeLabel', { + defaultMessage: 'Size', + }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.pie.headingLabel', { + defaultMessage: 'Value', + }), + }, accessors: layer.metric ? [{ columnId: layer.metric }] : [], supportsMoreColumns: !layer.metric, filterOperations: numberMetricOperations, diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap rename to x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap index 66162b0bee018..60afa4fe4ea88 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/to_expression.test.ts.snap @@ -64,6 +64,9 @@ Object { "seriesType": Array [ "area", ], + "showLines": Array [ + true, + ], "simpleView": Array [ false, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/annotations/helpers.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts index 1e02f929e90ce..1f923f85ee1be 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FramePublicAPI } from '../../types'; +import { FramePublicAPI } from '../../../types'; import { getStaticDate } from './helpers'; const frame = { diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx similarity index 96% rename from x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx rename to x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index 692d0f02725b2..480417ab2cbad 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -13,8 +13,8 @@ import { isRangeAnnotation, } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -import { layerTypes } from '../../../common'; -import type { FramePublicAPI, Visualization } from '../../types'; +import { layerTypes } from '../../../../common'; +import type { FramePublicAPI, Visualization } from '../../../types'; import { isHorizontalChart } from '../state_helpers'; import type { XYState, XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../types'; import { @@ -24,8 +24,8 @@ import { getDataLayers, isAnnotationsLayer, } from '../visualization_helpers'; -import { LensIconChartBarAnnotations } from '../../assets/chart_bar_annotations'; -import { generateId } from '../../id_generator'; +import { LensIconChartBarAnnotations } from '../../../assets/chart_bar_annotations'; +import { generateId } from '../../../id_generator'; const MAX_DATE = 8640000000000000; const MIN_DATE = -8640000000000000; @@ -402,6 +402,13 @@ export const getAnnotationsConfiguration = ({ { groupId: 'xAnnotations', groupLabel, + dimensionEditorGroupLabel: i18n.translate( + 'xpack.lens.indexPattern.annotationsDimensionEditorLabel', + { + defaultMessage: '{groupLabel} annotation', + values: { groupLabel }, + } + ), accessors: getAnnotationsAccessorColorConfig(layer), dataTestSubj: 'lnsXY_xAnnotationsPanel', invalid: !hasDateHistogram, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/visualizations/xy/axes_configuration.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/axes_configuration.test.ts index 0b14b54f27074..568cd38635acc 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/axes_configuration.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { Datatable } from '@kbn/expressions-plugin/public'; import { getAxesConfiguration } from './axes_configuration'; import { XYDataLayerConfig } from './types'; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/visualizations/xy/axes_configuration.ts similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts rename to x-pack/plugins/lens/public/visualizations/xy/axes_configuration.ts index d33604ea4508d..0c8e829f7dc0c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/axes_configuration.ts @@ -8,12 +8,12 @@ import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/public'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; -import { FormatFactory } from '../../common'; +import { FormatFactory } from '../../../common'; import { getDataBounds, validateAxisDomain, validateZeroInclusivityExtent, -} from '../shared_components'; +} from '../../shared_components'; import { XYDataLayerConfig } from './types'; interface FormattedMetric { diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.test.ts similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/color_assignment.test.ts index 666f729ffb79c..b59aadda1fdfa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.test.ts @@ -6,8 +6,8 @@ */ import { getColorAssignments } from './color_assignment'; -import type { FormatFactory } from '../../common'; -import { layerTypes } from '../../common'; +import type { FormatFactory } from '../../../common'; +import { layerTypes } from '../../../common'; import { XYDataLayerConfig } from './types'; import { Datatable } from '@kbn/expressions-plugin/common'; diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/color_assignment.ts rename to x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts index e7853384ec797..4a3dbc94d35ac 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts @@ -14,9 +14,9 @@ import { defaultAnnotationRangeColor, isRangeAnnotation, } from '@kbn/event-annotation-plugin/public'; -import type { AccessorConfig, FramePublicAPI } from '../types'; +import type { AccessorConfig, FramePublicAPI } from '../../types'; import { getColumnToLabelMap } from './state_helpers'; -import { FormatFactory } from '../../common'; +import { FormatFactory } from '../../../common'; import { isDataLayer, isReferenceLayer, isAnnotationsLayer } from './visualization_helpers'; import { getAnnotationsAccessorColorConfig } from './annotations/helpers'; import { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts similarity index 86% rename from x-pack/plugins/lens/public/xy_visualization/index.ts rename to x-pack/plugins/lens/public/visualizations/xy/index.ts index a7de5374c92d1..88a3a4a4ce048 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -10,9 +10,9 @@ import { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public' import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { LEGACY_TIME_AXIS } from '@kbn/charts-plugin/common'; -import type { EditorFrameSetup } from '../types'; -import type { LensPluginStartDependencies } from '../plugin'; -import type { FormatFactory } from '../../common'; +import type { EditorFrameSetup } from '../../types'; +import type { LensPluginStartDependencies } from '../../plugin'; +import type { FormatFactory } from '../../../common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -28,7 +28,7 @@ export class XyVisualization { { editorFrame }: XyVisualizationPluginSetupPlugins ) { editorFrame.registerVisualization(async () => { - const { getXyVisualization } = await import('../async_services'); + const { getXyVisualization } = await import('../../async_services'); const [, { charts, data, fieldFormats, eventAnnotation }] = await core.getStartServices(); const palettes = await charts.palettes.getPalettes(); const eventAnnotationService = await eventAnnotation.getService(); diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts index d83847cb54ca4..61f32b3adfc66 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FramePublicAPI } from '../types'; +import { FramePublicAPI } from '../../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; import { XYDataLayerConfig } from './types'; diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx similarity index 96% rename from x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx rename to x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx index 42717a3894303..293dd8e67db8f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx @@ -8,8 +8,8 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { Datatable } from '@kbn/expressions-plugin/public'; -import { layerTypes } from '../../common'; -import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; +import { layerTypes } from '../../../common'; +import type { DatasourceLayers, FramePublicAPI, Visualization } from '../../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; import type { @@ -27,8 +27,8 @@ import { isNumericMetric, isReferenceLayer, } from './visualization_helpers'; -import { generateId } from '../id_generator'; -import { LensIconChartBarReferenceLine } from '../assets/chart_bar_reference_line'; +import { generateId } from '../../id_generator'; +import { LensIconChartBarReferenceLine } from '../../assets/chart_bar_reference_line'; import { defaultReferenceLineColor } from './color_assignment'; export interface ReferenceLineBase { @@ -451,6 +451,13 @@ export const getReferenceConfiguration = ({ groups: groupsToShow.map(({ config = [], id, label, dataTestSubj, valid }) => ({ groupId: id, groupLabel: getAxisName(label, { isHorizontal }), + dimensionEditorGroupLabel: i18n.translate( + 'xpack.lens.indexPattern.referenceLineDimensionEditorLabel', + { + defaultMessage: '{groupLabel} reference line', + values: { groupLabel: getAxisName(label, { isHorizontal }) }, + } + ), accessors: config.map(({ forAccessor, color }) => getSingleColorConfig(forAccessor, color)), filterOperations: isNumericMetric, supportsMoreColumns: true, @@ -463,6 +470,9 @@ export const getReferenceConfiguration = ({ defaultMessage: 'Reference line value', }), ], + headingLabel: i18n.translate('xpack.lens.staticValue.headingLabel', { + defaultMessage: 'Placement', + }), }, supportFieldFormat: false, dataTestSubj, diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/state_helpers.ts rename to x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 975676e241b76..4831c05b12afa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -6,7 +6,7 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; +import type { FramePublicAPI, DatasourcePublicAPI } from '../../types'; import { visualizationTypes, XYLayerConfig, diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index f0a2b9f28623d..02b36d6b27c4f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -10,9 +10,9 @@ import { Position } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { getXyVisualization } from './xy_visualization'; -import { OperationDescriptor } from '../types'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; -import { layerTypes } from '../../common'; +import { OperationDescriptor } from '../../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { layerTypes } from '../../../common'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; import { defaultReferenceLineColor } from './color_assignment'; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/to_expression.ts rename to x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 6c06891f530e0..3953ec8d5562b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -19,11 +19,11 @@ import { AxisConfig, } from './types'; import type { ValidXYDataLayerConfig } from './types'; -import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types'; +import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../../types'; import { getColumnToLabelMap } from './state_helpers'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; -import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; +import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { getLayerTypeOptions, getDataLayers, @@ -31,8 +31,8 @@ import { getAnnotationsLayers, } from './visualization_helpers'; import { getUniqueLabels } from './annotations/helpers'; -import { layerTypes } from '../../common'; -import { axisExtentConfigToExpression } from '../shared_components'; +import { layerTypes } from '../../../common'; +import { axisExtentConfigToExpression } from '../../shared_components'; export const getSortedAccessors = ( datasource: DatasourcePublicAPI, @@ -469,6 +469,7 @@ const dataLayerToExpression = ( ) : [], seriesType: [seriesType], + showLines: seriesType === 'line' || seriesType === 'area' ? [true] : [false], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], ...(datasourceExpression diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts similarity index 88% rename from x-pack/plugins/lens/public/xy_visualization/types.ts rename to x-pack/plugins/lens/public/visualizations/xy/types.ts index 58c4cfb7e26c4..c2ca25c61c42e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -22,19 +22,19 @@ import type { YAxisConfig, } from '@kbn/expression-xy-plugin/common'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -import { LensIconChartArea } from '../assets/chart_area'; -import { LensIconChartAreaStacked } from '../assets/chart_area_stacked'; -import { LensIconChartAreaPercentage } from '../assets/chart_area_percentage'; -import { LensIconChartBar } from '../assets/chart_bar'; -import { LensIconChartBarStacked } from '../assets/chart_bar_stacked'; -import { LensIconChartBarPercentage } from '../assets/chart_bar_percentage'; -import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; -import { LensIconChartBarHorizontalStacked } from '../assets/chart_bar_horizontal_stacked'; -import { LensIconChartBarHorizontalPercentage } from '../assets/chart_bar_horizontal_percentage'; -import { LensIconChartLine } from '../assets/chart_line'; +import { LensIconChartArea } from '../../assets/chart_area'; +import { LensIconChartAreaStacked } from '../../assets/chart_area_stacked'; +import { LensIconChartAreaPercentage } from '../../assets/chart_area_percentage'; +import { LensIconChartBar } from '../../assets/chart_bar'; +import { LensIconChartBarStacked } from '../../assets/chart_bar_stacked'; +import { LensIconChartBarPercentage } from '../../assets/chart_bar_percentage'; +import { LensIconChartBarHorizontal } from '../../assets/chart_bar_horizontal'; +import { LensIconChartBarHorizontalStacked } from '../../assets/chart_bar_horizontal_stacked'; +import { LensIconChartBarHorizontalPercentage } from '../../assets/chart_bar_horizontal_percentage'; +import { LensIconChartLine } from '../../assets/chart_line'; -import type { VisualizationType, Suggestion } from '../types'; -import type { ValueLabelConfig } from '../../common/types'; +import type { VisualizationType, Suggestion } from '../../types'; +import type { ValueLabelConfig } from '../../../common/types'; export const YAxisModes = { AUTO: 'auto', diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/visualization.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index d16cbd86df3cf..a9848561ecded 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -7,7 +7,7 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; -import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; +import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../../types'; import type { State, XYState, @@ -18,9 +18,9 @@ import type { SeriesType, } from './types'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; -import { layerTypes } from '../../common'; -import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; -import { LensIconChartBar } from '../assets/chart_bar'; +import { layerTypes } from '../../../common'; +import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { LensIconChartBar } from '../../assets/chart_bar'; import type { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; @@ -1165,7 +1165,7 @@ describe('xy_visualization', () => { expect(options.map((o) => o.groupLabel)).toEqual([ 'Horizontal axis', 'Vertical axis', - 'Break down by', + 'Breakdown', ]); }); @@ -1183,7 +1183,7 @@ describe('xy_visualization', () => { expect(options.map((o) => o.groupLabel)).toEqual([ 'Vertical axis', 'Horizontal axis', - 'Break down by', + 'Breakdown', ]); }); @@ -1982,7 +1982,7 @@ describe('xy_visualization', () => { expect(accessorConfig.triggerIcon).toEqual('disabled'); }); - it('should show current palette for break down by dimension', () => { + it('should show current palette for breakdown dimension', () => { const palette = paletteServiceMock.get('mock'); const customColors = ['yellow', 'green']; (palette.getCategoricalColors as jest.Mock).mockReturnValue(customColors); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/visualization.tsx rename to x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 2eb15c96afe95..f5d241738dd4e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -22,7 +22,7 @@ import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; -import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; +import type { Visualization, AccessorConfig, FramePublicAPI } from '../../types'; import { State, visualizationTypes, @@ -33,7 +33,7 @@ import { YAxisMode, SeriesType, } from './types'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; import { getAccessorColorConfigs, getColorAssignments } from './color_assignment'; @@ -74,9 +74,9 @@ import { groupAxesByType } from './axes_configuration'; import { XYState } from './types'; import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_panel'; import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; -import { DimensionTrigger } from '../shared_components/dimension_trigger'; +import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; -import { onDropForVisualization } from '../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; +import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; export const getXyVisualization = ({ datatableUtilities, @@ -279,7 +279,7 @@ export const getXyVisualization = ({ { groupId: 'breakdown', groupLabel: i18n.translate('xpack.lens.xyChart.splitSeries', { - defaultMessage: 'Break down by', + defaultMessage: 'Breakdown', }), accessors: dataLayer.splitAccessor ? [ diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx rename to x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index e89edab464bfd..c36916f7f0306 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; -import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types'; +import { DatasourceLayers, OperationMetadata, VisualizationType } from '../../types'; import { State, visualizationTypes, @@ -19,11 +19,11 @@ import { SeriesType, } from './types'; import { isHorizontalChart } from './state_helpers'; -import { layerTypes } from '..'; -import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; -import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; -import { LensIconChartBarStacked } from '../assets/chart_bar_stacked'; -import { LayerType } from '../../common'; +import { layerTypes } from '../..'; +import { LensIconChartBarHorizontal } from '../../assets/chart_bar_horizontal'; +import { LensIconChartMixedXy } from '../../assets/chart_mixed_xy'; +import { LensIconChartBarStacked } from '../../assets/chart_bar_stacked'; +import { LayerType } from '../../../common'; export function getAxisName( axis: 'x' | 'y' | 'yLeft' | 'yRight', diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 5d6a807a262b3..6c6a425c14096 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -36,8 +36,12 @@ import { } from '@kbn/event-annotation-plugin/public'; import Color from 'color'; import { getDataLayers } from '../../visualization_helpers'; -import { FormatFactory } from '../../../../common'; -import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; +import { FormatFactory } from '../../../../../common'; +import { + DimensionEditorSection, + NameInput, + useDebouncedValue, +} from '../../../../shared_components'; import { isHorizontalChart } from '../../state_helpers'; import { defaultAnnotationLabel, defaultRangeAnnotationLabel } from '../../annotations/helpers'; import { ColorPicker } from '../color_picker'; @@ -45,7 +49,7 @@ import { IconSelectSetting, TextDecorationSetting } from '../shared/marker_decor import { LineStyleSettings } from '../shared/line_style_settings'; import { updateLayer } from '..'; import { annotationsIconSet } from './icon_set'; -import type { FramePublicAPI, VisualizationDimensionEditorProps } from '../../../types'; +import type { FramePublicAPI, VisualizationDimensionEditorProps } from '../../../../types'; import { State, XYState, XYAnnotationLayerConfig, XYDataLayerConfig } from '../../types'; export const toRangeAnnotationColor = (color = defaultAnnotationColor) => { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts index 32721285a4477..80cf7eccc9622 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { AvailableAnnotationIcon } from '@kbn/event-annotation-plugin/common'; -import { IconTriangle, IconCircle } from '../../../assets/annotation_icons'; +import { IconTriangle, IconCircle } from '../../../../assets/annotation_icons'; import { IconSet } from '../shared/icon_select'; export const annotationsIconSet: IconSet = [ diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss similarity index 91% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss index 3a0f4b944aa6c..32a87810f4025 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss @@ -8,5 +8,4 @@ .lnsConfigPanelDate__label { min-width: 56px; // makes both labels ("from" and "to") the same width - text-align: center; } \ No newline at end of file diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx index 40d367072d1ef..330fbf1f55da6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { AnnotationsPanel } from '.'; -import { FramePublicAPI } from '../../../types'; -import { layerTypes } from '../../..'; -import { createMockFramePublicAPI } from '../../../mocks'; +import { FramePublicAPI } from '../../../../types'; +import { layerTypes } from '../../../..'; +import { createMockFramePublicAPI } from '../../../../mocks'; import { State } from '../../types'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.scss b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.scss similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.scss rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.scss diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx index 692aa25845b6e..3589e0ff23d80 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { AxisSettingsPopover, AxisSettingsPopoverProps } from './axis_settings_popover'; -import { ToolbarPopover } from '../../shared_components'; -import { layerTypes } from '../../../common'; +import { ToolbarPopover } from '../../../shared_components'; +import { layerTypes } from '../../../../common'; import { ShallowWrapper } from 'enzyme'; function getRangeInputComponent(component: ShallowWrapper) { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx index 263a9131e1296..65d7b94281486 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx @@ -25,12 +25,12 @@ import { AxisTitleSettings, RangeInputField, BucketAxisBoundsControl, -} from '../../shared_components'; +} from '../../../shared_components'; import { isHorizontalChart } from '../state_helpers'; -import { EuiIconAxisBottom } from '../../assets/axis_bottom'; -import { EuiIconAxisLeft } from '../../assets/axis_left'; -import { EuiIconAxisRight } from '../../assets/axis_right'; -import { EuiIconAxisTop } from '../../assets/axis_top'; +import { EuiIconAxisBottom } from '../../../assets/axis_bottom'; +import { EuiIconAxisLeft } from '../../../assets/axis_left'; +import { EuiIconAxisRight } from '../../../assets/axis_right'; +import { EuiIconAxisTop } from '../../../assets/axis_top'; import { validateExtent } from '../axes_configuration'; import './axis_settings_popover.scss'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/color_picker.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/color_picker.tsx index 2f3b00befc2d4..2d0b28f4fac9a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/color_picker.tsx @@ -16,7 +16,7 @@ import { EuiIcon, euiPaletteColorBlind, } from '@elastic/eui'; -import { TooltipWrapper } from '../../shared_components'; +import { TooltipWrapper } from '../../../shared_components'; const tooltipContent = { auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx similarity index 91% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 359a3cb0de153..3c0c2239aaf2c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -10,16 +10,16 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import type { VisualizationDimensionEditorProps } from '../../types'; +import type { VisualizationDimensionEditorProps } from '../../../types'; import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types'; -import { FormatFactory } from '../../../common'; +import { FormatFactory } from '../../../../common'; import { getSeriesColor, isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; -import { PalettePicker, useDebouncedValue } from '../../shared_components'; +import { PalettePicker, useDebouncedValue } from '../../../shared_components'; import { getDataLayers, isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; import { ReferenceLinePanel } from './reference_line_config_panel'; import { AnnotationsPanel } from './annotations_config_panel'; -import { CollapseSetting } from '../../shared_components/collapse_setting'; +import { CollapseSetting } from '../../../shared_components/collapse_setting'; import { getSortedAccessors } from '../to_expression'; import { getColorAssignments, getAssignedColorConfig } from '../color_assignment'; @@ -136,13 +136,15 @@ export function DataDimensionEditor( setLocalState(updateLayer(localState, { ...layer, collapseFn }, index)); }} /> - { - setState(updateLayer(localState, { ...localLayer, palette: newPalette }, index)); - }} - /> + {!layer.collapseFn && ( + { + setState(updateLayer(localState, { ...localLayer, palette: newPalette }, index)); + }} + /> + )} ); } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx index 4a160cf34240c..d771edd9f10d9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx @@ -11,18 +11,18 @@ import { Position, ScaleType } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import { LegendSize } from '@kbn/visualizations-plugin/public'; -import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; +import type { VisualizationToolbarProps, FramePublicAPI } from '../../../types'; import { State, XYState, AxesSettingsConfig } from '../types'; import { isHorizontalChart } from '../state_helpers'; -import { hasNumericHistogramDimension, LegendSettingsPopover } from '../../shared_components'; +import { hasNumericHistogramDimension, LegendSettingsPopover } from '../../../shared_components'; import { AxisSettingsPopover } from './axis_settings_popover'; import { getAxesConfiguration, getXDomain, GroupsConfiguration } from '../axes_configuration'; import { VisualOptionsPopover } from './visual_options_popover'; import { getScaleType } from '../to_expression'; -import { TooltipWrapper } from '../../shared_components'; -import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; +import { TooltipWrapper } from '../../../shared_components'; +import { getDefaultVisualValuesForLayer } from '../../../shared_components/datasource_default_values'; import { getDataLayers } from '../visualization_helpers'; -import { LegendSettingsPopoverProps } from '../../shared_components/legend_settings_popover'; +import { LegendSettingsPopoverProps } from '../../../shared_components/legend_settings_popover'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx similarity index 94% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index d5523a0b10914..44d9f342cfcd7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -9,12 +9,12 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; -import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; +import type { VisualizationLayerWidgetProps, VisualizationType } from '../../../types'; import { State, visualizationTypes, SeriesType } from '../types'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; -import { StaticHeader } from '../../shared_components'; -import { LensIconChartBarReferenceLine } from '../../assets/chart_bar_reference_line'; -import { LensIconChartBarAnnotations } from '../../assets/chart_bar_annotations'; +import { StaticHeader } from '../../../shared_components'; +import { LensIconChartBarReferenceLine } from '../../../assets/chart_bar_reference_line'; +import { LensIconChartBarAnnotations } from '../../../assets/chart_bar_annotations'; import { updateLayer } from '.'; import { isAnnotationsLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/icon_set.ts similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/icon_set.ts diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/index.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/index.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx index fd4cad44c3244..a64e9b07f488b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx @@ -10,13 +10,13 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import { FillStyle } from '@kbn/expression-xy-plugin/common'; -import type { VisualizationDimensionEditorProps } from '../../../types'; +import type { VisualizationDimensionEditorProps } from '../../../../types'; import { State, XYState, XYReferenceLineLayerConfig, YConfig } from '../../types'; -import { FormatFactory } from '../../../../common'; +import { FormatFactory } from '../../../../../common'; import { ColorPicker } from '../color_picker'; import { updateLayer } from '..'; -import { useDebouncedValue } from '../../../shared_components'; +import { useDebouncedValue } from '../../../../shared_components'; import { idPrefix } from '../dimension_editor'; import { isHorizontalChart } from '../../state_helpers'; import { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/icon_select.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/icon_select.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/line_style_settings.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/line_style_settings.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx index 561e336400a37..e11c69f766a32 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx @@ -11,7 +11,7 @@ import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { IconPosition } from '@kbn/expression-xy-plugin/common'; import { YAxisMode } from '../../types'; -import { TooltipWrapper } from '../../../shared_components'; +import { TooltipWrapper } from '../../../../shared_components'; import { hasIcon, IconSelect, IconSet } from './icon_select'; import { idPrefix } from '../dimension_editor'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/end_value_definitions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/end_value_definitions.ts similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/end_value_definitions.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/end_value_definitions.ts diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx similarity index 95% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx index 09b381dd03f7a..236fc4c9eec7f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiRange } from '@elastic/eui'; -import { useDebouncedValue } from '../../../shared_components'; +import { useDebouncedValue } from '../../../../shared_components'; export interface FillOpacityOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx index ba8a246043bf2..dc621b8b236e0 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../shared_components'; +import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; import { XYState } from '../../types'; import { hasHistogramSeries } from '../../state_helpers'; -import type { FramePublicAPI } from '../../../types'; +import type { FramePublicAPI } from '../../../../types'; import { getDataLayers } from '../../visualization_helpers'; function getValueLabelDisableReason({ diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_value_option.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_value_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_value_option.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_value_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index 5b91ee70c6945..f48b8710d26d2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -8,14 +8,14 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { Position } from '@elastic/charts'; -import type { FramePublicAPI } from '../../../types'; -import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; +import type { FramePublicAPI } from '../../../../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../../../../mocks'; import { State, XYLayerConfig } from '../../types'; import { VisualOptionsPopover } from '.'; -import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components'; +import { ToolbarPopover, ValueLabelsSettings } from '../../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { FillOpacityOption } from './fill_opacity_option'; -import { layerTypes } from '../../../../common'; +import { layerTypes } from '../../../../../common'; describe('Visual options popover', () => { let frame: FramePublicAPI; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx rename to x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx index f28e932ae2ca5..83a14812b606f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx @@ -12,13 +12,13 @@ import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { XyToolbar } from '.'; import { DimensionEditor } from './dimension_editor'; import { AxisSettingsPopover } from './axis_settings_popover'; -import { FramePublicAPI } from '../../types'; +import { FramePublicAPI } from '../../../types'; import { State, XYState, XYDataLayerConfig } from '../types'; import { Position } from '@elastic/charts'; -import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; +import { createMockFramePublicAPI, createMockDatasource } from '../../../mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; -import { layerTypes } from '../../../common'; +import { layerTypes } from '../../../../common'; import { act } from 'react-dom/test-utils'; jest.mock('lodash', () => { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 14750dd34a127..52039d68a9fee 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -6,7 +6,7 @@ */ import { getSuggestions } from './xy_suggestions'; -import type { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../types'; +import type { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../../types'; import { State, XYState, @@ -14,17 +14,17 @@ import { XYAnnotationLayerConfig, XYDataLayerConfig, } from './types'; -import { generateId } from '../id_generator'; +import { generateId } from '../../id_generator'; import { getXyVisualization } from './xy_visualization'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; import type { PaletteOutput } from '@kbn/coloring'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { themeServiceMock } from '@kbn/core/public/mocks'; -jest.mock('../id_generator'); +jest.mock('../../id_generator'); const xyVisualization = getXyVisualization({ datatableUtilities: createDatatableUtilitiesMock(), diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts similarity index 99% rename from x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index ca42aabe616b6..7ae9374f0de98 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -15,7 +15,7 @@ import { TableSuggestionColumn, TableSuggestion, TableChangeType, -} from '../types'; +} from '../../types'; import { State, XYState, @@ -24,7 +24,7 @@ import { XYDataLayerConfig, SeriesType, } from './types'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_visualization.ts similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts rename to x-pack/plugins/lens/public/visualizations/xy/xy_visualization.ts diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.tsx index 15605a6a9ff77..dccbb3ab07451 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.tsx @@ -18,7 +18,10 @@ import { SCALING_TYPES } from '../../../../common/constants'; import { getGeoFields } from '../../../index_pattern_util'; interface Props { - onSourceConfigChange: (sourceConfig: Partial | null) => void; + onSourceConfigChange: ( + sourceConfig: Partial | null, + isPointsOnly: boolean + ) => void; } interface State { @@ -84,7 +87,8 @@ export class CreateSourceEditor extends Component { scalingType: field && field.isRuntimeField ? SCALING_TYPES.LIMIT : SCALING_TYPES.MVT, } : null; - this.props.onSourceConfigChange(sourceConfig); + const isPointsOnly = field ? field.type === 'geo_point' : false; + this.props.onSourceConfigChange(sourceConfig, isPointsOnly); }; _renderGeoSelect() { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx index 72a0539be6b08..146841174712a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx @@ -11,7 +11,13 @@ import { CreateSourceEditor } from './create_source_editor'; import { LayerWizard, RenderWizardArguments } from '../../layers'; import { ESSearchSource, sourceTitle } from './es_search_source'; import { BlendedVectorLayer, GeoJsonVectorLayer, MvtVectorLayer } from '../../layers/vector_layer'; -import { LAYER_WIZARD_CATEGORY, SCALING_TYPES, WIZARD_ID } from '../../../../common/constants'; +import { + LAYER_WIZARD_CATEGORY, + SCALING_TYPES, + STYLE_TYPE, + VECTOR_STYLES, + WIZARD_ID, +} from '../../../../common/constants'; import { DocumentsLayerIcon } from '../../layers/wizards/icons/documents_layer_icon'; import { ESSearchSourceDescriptor, @@ -42,13 +48,25 @@ export const esDocumentsLayerWizardConfig: LayerWizard = { }), icon: DocumentsLayerIcon, renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => { - const onSourceConfigChange = (sourceConfig: Partial | null) => { + const onSourceConfigChange = ( + sourceConfig: Partial | null, + isPointsOnly: boolean + ) => { if (!sourceConfig) { previewLayers([]); return; } - previewLayers([createDefaultLayerDescriptor(sourceConfig, mapColors)]); + const layerDescriptor = createDefaultLayerDescriptor(sourceConfig, mapColors); + if (isPointsOnly) { + layerDescriptor.style.properties[VECTOR_STYLES.LINE_WIDTH] = { + type: STYLE_TYPE.STATIC, + options: { + size: 0, + }, + }; + } + previewLayers([layerDescriptor]); }; return ; }, diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/create_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/create_source_editor.tsx index 577b5f8b42360..e1b4b9322e5eb 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/create_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/create_source_editor.tsx @@ -19,7 +19,10 @@ import { TopHitsForm } from './top_hits_form'; import { OnSourceChangeArgs } from '../../source'; interface Props { - onSourceConfigChange: (sourceConfig: Partial | null) => void; + onSourceConfigChange: ( + sourceConfig: Partial | null, + isPointsOnly: boolean + ) => void; } interface State { @@ -88,6 +91,8 @@ export class CreateSourceEditor extends Component { tooltipProperties.push(indexPattern.timeFieldName); } + const field = geoFieldName && indexPattern?.getFieldByName(geoFieldName); + const sourceConfig = indexPattern && geoFieldName && sortField && topHitsSplitField ? { @@ -101,7 +106,8 @@ export class CreateSourceEditor extends Component { topHitsSize, } : null; - this.props.onSourceConfigChange(sourceConfig); + const isPointsOnly = field ? field.type === 'geo_point' : false; + this.props.onSourceConfigChange(sourceConfig, isPointsOnly); }; _renderGeoSelect() { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx index 051afb41ce00f..ad92c5697d178 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx @@ -10,7 +10,12 @@ import React from 'react'; import { CreateSourceEditor } from './create_source_editor'; import { LayerWizard, RenderWizardArguments } from '../../../layers'; import { GeoJsonVectorLayer } from '../../../layers/vector_layer'; -import { LAYER_WIZARD_CATEGORY, WIZARD_ID } from '../../../../../common/constants'; +import { + LAYER_WIZARD_CATEGORY, + STYLE_TYPE, + VECTOR_STYLES, + WIZARD_ID, +} from '../../../../../common/constants'; import { TopHitsLayerIcon } from '../../../layers/wizards/icons/top_hits_layer_icon'; import { ESSearchSourceDescriptor } from '../../../../../common/descriptor_types'; import { ESSearchSource } from '../es_search_source'; @@ -25,7 +30,10 @@ export const esTopHitsLayerWizardConfig: LayerWizard = { }), icon: TopHitsLayerIcon, renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => { - const onSourceConfigChange = (sourceConfig: Partial | null) => { + const onSourceConfigChange = ( + sourceConfig: Partial | null, + isPointsOnly: boolean + ) => { if (!sourceConfig) { previewLayers([]); return; @@ -33,6 +41,14 @@ export const esTopHitsLayerWizardConfig: LayerWizard = { const sourceDescriptor = ESSearchSource.createDescriptor(sourceConfig); const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + if (isPointsOnly) { + layerDescriptor.style.properties[VECTOR_STYLES.LINE_WIDTH] = { + type: STYLE_TYPE.STATIC, + options: { + size: 0, + }, + }; + } previewLayers([layerDescriptor]); }; return ; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/__snapshots__/feature_properties.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/__snapshots__/feature_properties.test.tsx.snap index 951f65da05df0..12ed9e96b5a9b 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/__snapshots__/feature_properties.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/__snapshots__/feature_properties.test.tsx.snap @@ -1,44 +1,48 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`FeatureProperties should render 1`] = ` - - - - + - prop1 - - - - - - + + + - foobar2 - - - -
+
- foobar1 - -
- prop2 - + prop1 + + foobar1 + +
-
+ + prop2 + + + foobar2 + + + + + + `; exports[`FeatureProperties should show error message if unable to load tooltip content 1`] = ` @@ -55,81 +59,28 @@ exports[`FeatureProperties should show error message if unable to load tooltip c `; exports[`FeatureProperties should show filter button for filterable properties 1`] = ` - - - - - + - foobar1 - - - - - - - - -
- prop1 - +
- - - -
- prop2 - - foobar2 - -
-`; - -exports[`FeatureProperties should show view actions button when there are available actions 1`] = ` - - - - - - + + - - - + + + + + +
- prop1 - - foobar1 - - + prop1 + + foobar1 + - - - - -
+
+ prop2 + + foobar2 + +
+ +`; + +exports[`FeatureProperties should show view actions button when there are available actions 1`] = ` +
+ + + - prop2 - - + + + + - foobar2 - - - -
+ prop1 + + foobar1 + + + + + + + + + +
-
+ + prop2 + + + foobar2 + + + + + +
`; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/_index.scss b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/_index.scss index 92df0ffbaad92..cc2acdf78c7de 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/_index.scss @@ -1,12 +1,16 @@ .mapFeatureTooltip_table { width: 100%; - max-height: calc(49vh - #{$euiSizeXL * 2}); td { padding: $euiSizeXS; } } +.mapFeatureTooltip_tableWrapper { + overflow: auto; + max-height: calc(49vh - 64px); +} + .mapFeatureTooltip_row { border-bottom: 1px solid $euiColorLightestShade; } @@ -20,6 +24,7 @@ } .mapFeatureTooltip__propertyLabel { + min-width: $euiSizeXL * 2.5; max-width: $euiSizeXL * 4; font-weight: $euiFontWeightSemiBold; } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx index ca291f585590a..71d50d06b62e4 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx @@ -336,9 +336,11 @@ export class FeatureProperties extends Component { }); return ( - - {rows} -
+
+ + {rows} +
+
); } } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 59f679d041024..0388dcd86d397 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -735,7 +735,7 @@ export class MapEmbeddable ) { /** * Maps emit rendered when the data is loaded, as we don't have feedback from the maps rendering library atm. - * This means that the dashboard-loaded event might be fired while a map is still rendering in some cases. + * This means that the DASHBOARD_LOADED_EVENT event might be fired while a map is still rendering in some cases. * For more details please contact the maps team. */ this.updateOutput({ diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 64603b8843c95..2dd45ff50a2f7 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -332,6 +332,14 @@ export class SavedMap { return this._originatingApp ? this.getAppNameFromId(this._originatingApp) : undefined; } + public hasOriginatingApp(): boolean { + return !!this._originatingApp; + } + + public getOriginatingPath(): string | undefined { + return this._originatingPath; + } + public getAppNameFromId = (appId: string): string | undefined => { return this._getStateTransfer().getAppNameFromId(appId); }; @@ -341,7 +349,7 @@ export class SavedMap { } public hasSaveAndReturnConfig() { - const hasOriginatingApp = !!this._originatingApp; + const hasOriginatingApp = this.hasOriginatingApp(); const isNewMap = !this.getSavedObjectId(); return getIsAllowByValueEmbeddables() ? hasOriginatingApp : !isNewMap && hasOriginatingApp; } diff --git a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx index 9ff80168b96ef..2032d78ab46bc 100644 --- a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx @@ -19,6 +19,7 @@ import { withSuspense, } from '@kbn/presentation-util-plugin/public'; import { + getNavigateToApp, getMapsCapabilities, getIsAllowByValueEmbeddables, getInspector, @@ -96,6 +97,23 @@ export function getTopNavConfig({ } ); + if (savedMap.hasOriginatingApp()) { + topNavConfigs.push({ + label: i18n.translate('xpack.maps.topNav.cancel', { + defaultMessage: 'Cancel', + }), + run: () => { + getNavigateToApp()(savedMap.getOriginatingApp()!, { + path: savedMap.getOriginatingPath(), + }); + }, + testId: 'mapsCancelButton', + description: i18n.translate('xpack.maps.topNav.cancelButtonAriaLabel', { + defaultMessage: 'Return to the last app without saving changes', + }), + }); + } + if (getMapsCapabilities().save) { const hasSaveAndReturnConfig = savedMap.hasSaveAndReturnConfig(); const mapDescription = savedMap.getAttributes().description @@ -197,7 +215,7 @@ export function getTopNavConfig({ let saveModal; - if (savedMap.getOriginatingApp() || !getIsAllowByValueEmbeddables()) { + if (savedMap.hasOriginatingApp() || !getIsAllowByValueEmbeddables()) { saveModal = ( >; - onCreate: (swimlaneProps: { - panelTitle: string; - swimlaneType: SwimlaneType; - viewBy?: string; - }) => void; + onCreate: (swimlaneProps: ExplicitInput) => void; onCancel: () => void; } @@ -47,7 +49,7 @@ export const AnomalySwimlaneInitializer: FC = ( initialInput, }) => { const [panelTitle, setPanelTitle] = useState(defaultTitle); - const [swimlaneType, setSwimlaneType] = useState( + const [swimlaneType, setSwimlaneType] = useState( initialInput?.swimlaneType ?? SWIMLANE_TYPE.OVERALL ); const [viewBySwimlaneFieldName, setViewBySwimlaneFieldName] = useState(initialInput?.viewBy); @@ -81,6 +83,12 @@ export const AnomalySwimlaneInitializer: FC = ( (swimlaneType === SWIMLANE_TYPE.OVERALL || (swimlaneType === SWIMLANE_TYPE.VIEW_BY && !!viewBySwimlaneFieldName)); + const resultInput = { + panelTitle, + swimlaneType, + ...(viewBySwimlaneFieldName ? { viewBy: viewBySwimlaneFieldName } : {}), + }; + return ( @@ -162,15 +170,7 @@ export const AnomalySwimlaneInitializer: FC = ( /> - + { + onCreate={(explicitInput) => { modalSession.close(); resolve({ jobIds, - title: panelTitle, - swimlaneType, - viewBy, + title: explicitInput.panelTitle, + ...explicitInput, }); }} onCancel={() => { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts index 32f3bcaa90237..0cee2108e2ede 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/index.ts @@ -5,14 +5,16 @@ * 2.0. */ -import { LegacyRequest, MonitoringCore } from '../../../../types'; -import { MonitoringConfig } from '../../../../config'; +import type { LegacyRequest, MonitoringCore } from '../../../../types'; +import type { MonitoringConfig } from '../../../../config'; import { createValidationFunction } from '../../../../lib/create_route_validation_function'; import { getHealthRequestQueryRT } from '../../../../../common/http_api/_health'; -import { TimeRange } from '../../../../../common/http_api/shared'; +import type { TimeRange } from '../../../../../common/http_api/shared'; import { INDEX_PATTERN, INDEX_PATTERN_ENTERPRISE_SEARCH } from '../../../../../common/constants'; import { fetchMonitoredClusters } from './monitored_clusters'; +import { fetchMetricbeatErrors } from './metricbeat'; +import type { FetchParameters } from './types'; const DEFAULT_QUERY_TIMERANGE = { min: 'now-15m', max: 'now' }; const DEFAULT_QUERY_TIMEOUT_SECONDS = 15; @@ -44,19 +46,38 @@ export function registerV1HealthRoute(server: MonitoringCore) { const settings = extractSettings(server.config); - const monitoredClusters = await fetchMonitoredClusters({ + const fetchArgs: FetchParameters = { timeout, timeRange, - monitoringIndex: withCCS(INDEX_PATTERN), - entSearchIndex: withCCS(INDEX_PATTERN_ENTERPRISE_SEARCH), search: (params: any) => callWithRequest(req, 'search', params), logger, - }).catch((err: Error) => { - logger.error(`_health: failed to retrieve monitored clusters:\n${err.stack}`); - return { error: err.message }; - }); + }; - return { monitoredClusters, settings }; + const monitoredClustersFn = () => + fetchMonitoredClusters({ + ...fetchArgs, + monitoringIndex: withCCS(INDEX_PATTERN), + entSearchIndex: withCCS(INDEX_PATTERN_ENTERPRISE_SEARCH), + }).catch((err: Error) => { + logger.error(`_health: failed to retrieve monitored clusters:\n${err.stack}`); + return { error: err.message }; + }); + + const metricbeatErrorsFn = () => + fetchMetricbeatErrors({ + ...fetchArgs, + metricbeatIndex: server.config.ui.metricbeat.index, + }).catch((err: Error) => { + logger.error(`_health: failed to retrieve metricbeat data:\n${err.stack}`); + return { error: err.message }; + }); + + const [monitoredClusters, metricbeatErrors] = await Promise.all([ + monitoredClustersFn(), + metricbeatErrorsFn(), + ]); + + return { monitoredClusters, metricbeatErrors, settings }; }, }); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.test.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.test.ts new file mode 100644 index 0000000000000..ea14de84735ef --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.test.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 { buildMetricbeatErrors } from './build_metricbeat_errors'; +import assert from 'assert'; + +describe(__filename, () => { + describe('buildMetricbeatErrors', () => { + test('it should build an object containing dedup error messages per event.dataset', () => { + const metricbeatErrors = [ + { + key: 'beat', + errors_by_dataset: { + buckets: [ + { + key: 'state', + latest_docs: { + hits: { + hits: [ + { + _source: { + '@timestamp': '2022-07-26T08:43:32.625Z', + error: { + message: + 'error making http request: Get "http://host.docker.internal:5067/state": dial tcp 192.168.65.2:5067: connect: connection refused', + }, + }, + }, + { + _source: { + '@timestamp': '2022-07-26T08:42:32.625Z', + error: { + message: + 'error making http request: Get "http://host.docker.internal:5067/state": dial tcp 192.168.65.2:5067: connect: connection refused', + }, + }, + }, + { + _source: { + '@timestamp': '2022-07-26T08:41:32.625Z', + error: { + message: 'Generic random error', + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + ]; + + const monitoredClusters = buildMetricbeatErrors(metricbeatErrors); + assert.deepEqual(monitoredClusters, { + beat: { + state: [ + { + lastSeen: '2022-07-26T08:43:32.625Z', + message: + 'error making http request: Get "http://host.docker.internal:5067/state": dial tcp 192.168.65.2:5067: connect: connection refused', + }, + { + lastSeen: '2022-07-26T08:41:32.625Z', + message: 'Generic random error', + }, + ], + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.ts new file mode 100644 index 0000000000000..44f0df8c067cc --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/build_metricbeat_errors.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { MetricbeatMonitoredProduct } from '../types'; + +export type MetricbeatProducts = { + [product in MetricbeatMonitoredProduct]?: ErrorsByMetricset; +}; + +interface ErrorsByMetricset { + [dataset: string]: ErrorDetails[]; +} + +interface ErrorDetails { + message: string; + lastSeen: string; +} + +/** + * builds a normalized representation of the metricbeat errors from the provided + * query buckets with a product->metricset hierarchy where + * product: the monitored products (eg elasticsearch) + * metricset: the collected metricsets for a given entity + * + * example: + * { + * "product": { + * "logstash": { + * "node": { + * "message": "some error message", + * "lastSeen": "2022-05-17T16:56:52.929Z" + * } + * } + * } + * } + */ +export const buildMetricbeatErrors = (modulesBucket: any[]): MetricbeatProducts => { + return (modulesBucket ?? []).reduce((module, { key, errors_by_dataset: errorsByDataset }) => { + const datasets = buildMetricsets(errorsByDataset.buckets); + if (Object.keys(datasets).length === 0) { + return { ...module }; + } + + return { + ...module, + [key]: datasets, + }; + }, {} as MetricbeatProducts); +}; + +const buildMetricsets = (errorsByDataset: any[]): ErrorsByMetricset => { + return (errorsByDataset ?? []).reduce((dataset, { key, latest_docs: latestDocs }) => { + const errors = buildErrorMessages(latestDocs.hits.hits ?? []); + + if (errors.length === 0) { + return { ...dataset }; + } + + return { + ...dataset, + [key]: errors, + }; + }, {} as ErrorsByMetricset); +}; + +const getErrorMessage = (doc: any) => { + return doc._source?.error?.message; +}; + +const buildErrorMessages = (errorDocs: any[]): ErrorDetails[] => { + const seenErrorMessages = new Set(); + + return errorDocs + .filter((doc) => { + const message = getErrorMessage(doc); + if (seenErrorMessages.has(message)) { + return false; + } else { + seenErrorMessages.add(message); + return true; + } + }) + .map((uniqueDoc: any) => { + const source = uniqueDoc._source; + return { + message: getErrorMessage(uniqueDoc), + lastSeen: source['@timestamp'], + }; + }); +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.test.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.test.ts new file mode 100644 index 0000000000000..8f78996cff78b --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import assert from 'assert'; +import sinon from 'sinon'; +import type { Logger } from '@kbn/core/server'; +import { fetchMetricbeatErrors } from './fetch_metricbeat_errors'; + +const getMockLogger = () => + ({ + warn: sinon.spy(), + error: sinon.spy(), + } as unknown as Logger); + +describe(__filename, () => { + describe('fetchMetricbeatErrors', () => { + test('it fetch and build metricbeat errors response', async () => { + const response = { + aggregations: { + errors_aggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'logstash', + doc_count: 180, + errors_by_dataset: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'node', + doc_count: 90, + latest_docs: { + hits: { + total: { + value: 90, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.ds-metricbeat-8.4.0-2022.07.27-000001', + _id: 'djXRP4IBU_ii2T3qLwey', + _score: null, + _source: { + '@timestamp': '2022-07-27T13:20:49.070Z', + metricset: { + period: 10000, + name: 'node', + }, + error: { + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + }, + sort: [1658928049070], + }, + { + _index: '.ds-metricbeat-8.4.0-2022.07.27-000001', + _id: 'UTXRP4IBU_ii2T3qCAef', + _score: null, + _source: { + '@timestamp': '2022-07-27T13:20:39.066Z', + metricset: { + period: 10000, + name: 'node', + }, + error: { + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + }, + sort: [1658928039066], + }, + ], + }, + }, + }, + { + key: 'node_stats', + doc_count: 90, + latest_docs: { + hits: { + total: { + value: 90, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.ds-metricbeat-8.4.0-2022.07.27-000001', + _id: 'eTXRP4IBU_ii2T3qLwey', + _score: null, + _source: { + '@timestamp': '2022-07-27T13:20:49.580Z', + metricset: { + period: 10000, + name: 'node_stats', + }, + error: { + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + }, + sort: [1658928049580], + }, + { + _index: '.ds-metricbeat-8.4.0-2022.07.27-000001', + _id: 'VDXRP4IBU_ii2T3qCAef', + _score: null, + _source: { + '@timestamp': '2022-07-27T13:20:39.582Z', + metricset: { + period: 10000, + name: 'node_stats', + }, + error: { + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + }, + sort: [1658928039582], + }, + ], + }, + }, + }, + ], + }, + }, + ], + }, + }, + }; + + const searchFn = jest.fn().mockResolvedValueOnce(response); + + const monitoredClusters = await fetchMetricbeatErrors({ + timeout: 10, + metricbeatIndex: 'foo', + timeRange: { min: 1652979091217, max: 11652979091217 }, + search: searchFn, + logger: getMockLogger(), + }); + + assert.deepEqual(monitoredClusters, { + execution: { + timedOut: false, + errors: [], + }, + products: { + logstash: { + node: [ + { + lastSeen: '2022-07-27T13:20:49.070Z', + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + ], + node_stats: [ + { + lastSeen: '2022-07-27T13:20:49.580Z', + message: + 'error making http request: Get "http://host.docker.internal:9600/": dial tcp 192.168.65.2:9600: connect: connection refused', + }, + ], + }, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.ts new file mode 100644 index 0000000000000..ed445c1658c3b --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/fetch_metricbeat_errors.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FetchParameters, FetchExecution, MonitoredProduct } from '../types'; + +import type { MetricbeatProducts } from './build_metricbeat_errors'; + +import { metricbeatErrorsQuery } from './metricbeat_errors_query'; +import { buildMetricbeatErrors } from './build_metricbeat_errors'; + +interface MetricbeatResponse { + products?: MetricbeatProducts; + execution: FetchExecution; +} + +export const fetchMetricbeatErrors = async ({ + timeout, + metricbeatIndex, + timeRange, + search, + logger, +}: FetchParameters & { + metricbeatIndex: string; +}): Promise => { + const getMetricbeatErrors = async () => { + const { aggregations, timed_out: timedOut } = await search({ + index: metricbeatIndex, + body: metricbeatErrorsQuery({ + timeRange, + timeout, + products: [ + MonitoredProduct.Beats, + MonitoredProduct.Elasticsearch, + MonitoredProduct.EnterpriseSearch, + MonitoredProduct.Kibana, + MonitoredProduct.Logstash, + ], + }), + size: 0, + ignore_unavailable: true, + }); + const buckets = aggregations?.errors_aggregation?.buckets ?? []; + return { products: buildMetricbeatErrors(buckets), timedOut: Boolean(timedOut) }; + }; + + try { + const { products, timedOut } = await getMetricbeatErrors(); + return { + products, + execution: { + timedOut, + errors: [], + }, + }; + } catch (err) { + logger.error(`fetchMetricbeatErrors: failed to fetch:\n${err.stack}`); + return { + execution: { + timedOut: false, + errors: [err.message], + }, + }; + } +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/index.ts new file mode 100644 index 0000000000000..e0c8a2e2e0a52 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { fetchMetricbeatErrors } from './fetch_metricbeat_errors'; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/metricbeat_errors_query.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/metricbeat_errors_query.ts new file mode 100644 index 0000000000000..28d1b5cc8d227 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/metricbeat/metricbeat_errors_query.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { MetricbeatMonitoredProduct, QueryOptions } from '../types'; + +const MAX_BUCKET_SIZE = 50; + +/** + * Returns a nested aggregation of error messages per event.datasets. + * Each module (beats, kibana...) can contain one or multiple metricsets with error messages + */ +interface MetricbeatErrorsQueryOptions extends QueryOptions { + products: MetricbeatMonitoredProduct[]; +} + +export const metricbeatErrorsQuery = ({ + timeRange, + timeout, + products, +}: MetricbeatErrorsQueryOptions) => { + if (!timeRange) throw new Error('metricbeatErrorsQuery: missing timeRange parameter'); + return { + timeout: `${timeout}s`, + query: { + bool: { + filter: { + bool: { + must: [ + { + exists: { + field: 'error.message', + }, + }, + { + terms: { + 'event.module': Object.values(products), + }, + }, + { + range: { + timestamp: { + gte: timeRange.min, + lte: timeRange.max, + }, + }, + }, + ], + }, + }, + }, + }, + aggs: { + errors_aggregation: errorsAggregation, + }, + }; +}; + +const errorsByMetricset = { + terms: { + field: 'metricset.name', + }, + aggs: { + latest_docs: { + top_hits: { + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + size: MAX_BUCKET_SIZE, + _source: { + includes: ['@timestamp', 'error', 'metricset'], + }, + }, + }, + }, +}; + +const errorsAggregation = { + terms: { + field: 'event.module', + }, + aggs: { + errors_by_dataset: errorsByMetricset, + }, +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts index df959c499d4b2..6a5c0fa8f796a 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/build_monitored_clusters.ts @@ -7,6 +7,7 @@ import type { Logger } from '@kbn/core/server'; import { isEmpty, mapValues, merge, omitBy, reduce } from 'lodash'; +import { MonitoredProduct } from '../types'; enum CollectionMode { Internal = 'internal-monitoring', @@ -15,15 +16,6 @@ enum CollectionMode { Unknown = 'unknown', } -enum MonitoredProduct { - Cluster = 'cluster', - Elasticsearch = 'elasticsearch', - Kibana = 'kibana', - Beats = 'beats', - Logstash = 'logstash', - EnterpriseSearch = 'enterpriseSearch', -} - interface MonitoredMetricsets { [metricset: string]: { [collectionMode in CollectionMode]: { @@ -45,10 +37,11 @@ export interface MonitoredClusters { [clusterUuid: string]: MonitoredProducts; } -const internalMonitoringPattern = /^\.monitoring-(es|kibana|beats|logstash)-7-[0-9]{4}\..*/; -const metricbeatMonitoring7Pattern = /^\.monitoring-(es|kibana|beats|logstash|ent-search)-7.*-mb.*/; +const internalMonitoringPattern = /(.*:)?\.monitoring-(es|kibana|beats|logstash)-7-[0-9]{4}\..*/; +const metricbeatMonitoring7Pattern = + /(.*:)?\.monitoring-(es|kibana|beats|logstash|ent-search)-7.*-mb.*/; const metricbeatMonitoring8Pattern = - /^\.ds-\.monitoring-(es|kibana|beats|logstash|ent-search)-8-mb.*/; + /(.*:)?\.ds-\.monitoring-(es|kibana|beats|logstash|ent-search)-8-mb.*/; const getCollectionMode = (index: string): CollectionMode => { if (internalMonitoringPattern.test(index)) return CollectionMode.Internal; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/fetch_monitored_clusters.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/fetch_monitored_clusters.ts index 95f56aea5f625..a9f007a2db468 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/fetch_monitored_clusters.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/monitored_clusters/fetch_monitored_clusters.ts @@ -5,27 +5,19 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; import { merge } from 'lodash'; -import { ElasticsearchResponse } from '../../../../../../common/types/es'; -import { TimeRange } from '../../../../../../common/http_api/shared'; - import { buildMonitoredClusters, MonitoredClusters } from './build_monitored_clusters'; import { monitoredClustersQuery, persistentMetricsetsQuery, enterpriseSearchQuery, } from './monitored_clusters_query'; - -type SearchFn = (params: any) => Promise; +import type { FetchParameters, FetchExecution } from '../types'; interface MonitoredClustersResponse { clusters?: MonitoredClusters; - execution: { - timedOut?: boolean; - errors?: string[]; - }; + execution: FetchExecution; } /** @@ -40,13 +32,9 @@ export const fetchMonitoredClusters = async ({ timeRange, search, logger, -}: { - timeout: number; - timeRange: TimeRange; +}: FetchParameters & { monitoringIndex: string; entSearchIndex: string; - search: SearchFn; - logger: Logger; }): Promise => { const getMonitoredClusters = async ( index: string, diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/_health/types.ts b/x-pack/plugins/monitoring/server/routes/api/v1/_health/types.ts new file mode 100644 index 0000000000000..7d3db44aeac74 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/_health/types.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/core/server'; +import type { TimeRange } from '../../../../../common/http_api/shared'; +import type { ElasticsearchResponse } from '../../../../../common/types/es'; + +export enum MonitoredProduct { + Cluster = 'cluster', + Elasticsearch = 'elasticsearch', + Kibana = 'kibana', + Beats = 'beat', + Logstash = 'logstash', + EnterpriseSearch = 'enterpriseSearch', +} +export type MetricbeatMonitoredProduct = Exclude; + +export type SearchFn = (params: any) => Promise; + +export interface QueryOptions { + timeRange?: TimeRange; + timeout: number; // in seconds +} + +export interface FetchParameters { + timeout: number; + timeRange: TimeRange; + search: SearchFn; + logger: Logger; +} + +export interface FetchExecution { + timedOut?: boolean; + errors?: string[]; +} diff --git a/x-pack/plugins/observability/kibana.json b/x-pack/plugins/observability/kibana.json index 32fa629762885..5464bffccd9cd 100644 --- a/x-pack/plugins/observability/kibana.json +++ b/x-pack/plugins/observability/kibana.json @@ -31,7 +31,8 @@ "triggersActionsUi", "inspector", "unifiedSearch", - "sharedUX" + "sharedUX", + "security" ], "ui": true, "server": true, diff --git a/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx index f119b07def3b7..85a52ffc4fd75 100644 --- a/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx +++ b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx @@ -6,7 +6,7 @@ */ import type { GetRenderCellValue } from '@kbn/triggers-actions-ui-plugin/public'; -import { observabilityFeatureId } from '../../common'; +import { casesFeatureId, observabilityFeatureId } from '../../common'; import { useBulkAddToCaseActions } from '../hooks/use_alert_bulk_case_actions'; import { TopAlert, useToGetInternalFlyout } from '../pages/alerts'; import { getRenderCellValue } from '../pages/alerts/components/render_cell_value'; @@ -19,6 +19,7 @@ const getO11yAlertsTableConfiguration = ( observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry ) => ({ id: observabilityFeatureId, + casesFeatureId, columns: alertO11yColumns.map(addDisplayNames), useInternalFlyout: () => { const { header, body, footer } = useToGetInternalFlyout(observabilityRuleTypeRegistry); diff --git a/x-pack/plugins/observability/public/hooks/use_alert_bulk_case_actions.ts b/x-pack/plugins/observability/public/hooks/use_alert_bulk_case_actions.ts index 0ca4f5a7187ba..40219feb09c21 100644 --- a/x-pack/plugins/observability/public/hooks/use_alert_bulk_case_actions.ts +++ b/x-pack/plugins/observability/public/hooks/use_alert_bulk_case_actions.ts @@ -15,7 +15,6 @@ import { } from '../pages/alerts/containers/alerts_table_t_grid/translations'; import { useGetUserCasesPermissions } from './use_get_user_cases_permissions'; import { ObservabilityAppServices } from '../application/types'; -import { observabilityFeatureId } from '../../common'; export interface UseAddToCaseActions { onClose?: () => void; @@ -46,9 +45,7 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi disableOnQuery: true, disabledLabel: ADD_TO_CASE_DISABLED, onClick: (items?: TimelineItem[]) => { - const caseAttachments = items - ? casesUi.helpers.groupAlertsByRule(items, observabilityFeatureId) - : []; + const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items) : []; createCaseFlyout.open({ attachments: caseAttachments }); }, }, @@ -59,9 +56,7 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi disabledLabel: ADD_TO_CASE_DISABLED, 'data-test-subj': 'attach-existing-case', onClick: (items?: TimelineItem[]) => { - const caseAttachments = items - ? casesUi.helpers.groupAlertsByRule(items, observabilityFeatureId) - : []; + const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items) : []; selectCaseModal.open({ attachments: caseAttachments }); }, }, diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 96989d2fd114f..04cb8289dd247 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -87,7 +87,7 @@ export { NavigationWarningPromptProvider, Prompt } from './utils/navigation_warn export { getApmTraceUrl } from './utils/get_apm_trace_url'; export { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/exploratory_view_url'; export type { AllSeries } from './components/shared/exploratory_view/hooks/use_series_storage'; -export type { SeriesUrl } from './components/shared/exploratory_view/types'; +export type { SeriesUrl, UrlFilter } from './components/shared/exploratory_view/types'; export type { ObservabilityRuleTypeFormatter, diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.test.tsx new file mode 100644 index 0000000000000..019b7a974e874 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.test.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { render } from '../../../../utils/test_helper'; +import React from 'react'; +import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting'; +import { createObservabilityRuleTypeRegistryMock } from '../../../../rules/observability_rule_type_registry_mock'; +import AlertsFlyoutBody from './alerts_flyout_body'; +import { inventoryThresholdAlert } from '../../../../rules/fixtures/example_alerts'; +import { parseAlert } from '../parse_alert'; +import { RULE_DETAILS_PAGE_ID } from '../../../rule_details/types'; + +describe('AlertsFlyoutBody', () => { + jest + .spyOn(useUiSettingHook, 'useUiSetting') + .mockImplementation(() => 'MMM D, YYYY @ HH:mm:ss.SSS'); + const observabilityRuleTypeRegistryMock = createObservabilityRuleTypeRegistryMock(); + + const setup = (id: string) => { + const dataFieldEs = inventoryThresholdAlert.reduce( + (acc, d) => ({ ...acc, [d.field]: d.value }), + {} + ); + const alert = parseAlert(observabilityRuleTypeRegistryMock)(dataFieldEs); + return render(); + }; + + it('should render View rule detail link', async () => { + const flyout = setup('test'); + expect(flyout.getByTestId('viewRuleDetailsFlyout')).toBeInTheDocument(); + }); + + it('should NOT render View rule detail link for RULE_DETAILS_PAGE_ID', async () => { + const flyout = setup(RULE_DETAILS_PAGE_ID); + expect(flyout.queryByTestId('viewRuleDetailsFlyout')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.tsx index 7e90f0ed80799..9a8b97df31939 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_body.tsx @@ -26,20 +26,23 @@ import { } from '@kbn/rule-data-utils'; import moment from 'moment-timezone'; import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; +import { RULE_DETAILS_PAGE_ID } from '../../../rule_details/types'; import { asDuration } from '../../../../../common/utils/formatters'; import { translations, paths } from '../../../../config'; import { AlertStatusIndicator } from '../../../../components/shared/alert_status_indicator'; import { FlyoutProps } from './types'; // eslint-disable-next-line import/no-default-export -export default function AlertsFlyoutBody(props: FlyoutProps) { - const alert = props.alert; +export default function AlertsFlyoutBody({ alert, id: pageId }: FlyoutProps) { const { services } = useKibana(); const { http } = services; const dateFormat = useUiSetting('dateFormat'); const prepend = http?.basePath.prepend; - const ruleId = get(props.alert.fields, ALERT_RULE_UUID) ?? null; - const linkToRule = ruleId && prepend ? prepend(paths.observability.ruleDetails(ruleId)) : null; + const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null; + const linkToRule = + pageId !== RULE_DETAILS_PAGE_ID && ruleId && prepend + ? prepend(paths.observability.ruleDetails(ruleId)) + : null; const overviewListItems = [ { title: translations.alertsFlyout.statusLabel, diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/index.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/index.tsx index 771d8529261cc..40f7caa48f841 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/index.tsx @@ -24,7 +24,7 @@ export const useToGetInternalFlyout = ( const alert = parseAlert(observabilityRuleTypeRegistry)( props.alert as unknown as Record ); - return ; + return ; }, [observabilityRuleTypeRegistry] ); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/types.ts b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/types.ts index 43e27b4cc98c2..26bb9c60fc67e 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/types.ts +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/types.ts @@ -9,4 +9,5 @@ import { TopAlert } from '../../containers'; export interface FlyoutProps { alert: TopAlert; + id?: string; } diff --git a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx new file mode 100644 index 0000000000000..cd5c892fdbb19 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.test.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act } from '@testing-library/react-hooks'; +import { kibanaStartMock } from '../../../utils/kibana_react.mock'; +import React from 'react'; +import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { ObservabilityActions, ObservabilityActionsProps } from './observability_actions'; +import { inventoryThresholdAlert } from '../../../rules/fixtures/example_alerts'; +import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types'; +import { createObservabilityRuleTypeRegistryMock } from '../../../rules/observability_rule_type_registry_mock'; +import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; + +const mockUseKibanaReturnValue = kibanaStartMock.startContract(); + +jest.mock('../../../utils/kibana_react', () => ({ + __esModule: true, + useKibana: jest.fn(() => mockUseKibanaReturnValue), +})); + +jest.mock('../../../hooks/use_get_user_cases_permissions', () => ({ + useGetUserCasesPermissions: jest.fn(() => ({})), +})); + +describe('ObservabilityActions component', () => { + const setup = async (pageId: string) => { + const props: ObservabilityActionsProps = { + eventId: '6d4c6d74-d51a-495c-897d-88ced3b95e30', + ecsData: { + _id: '6d4c6d74-d51a-495c-897d-88ced3b95e30', + _index: '.internal.alerts-observability.metrics.alerts-default-000001', + }, + data: inventoryThresholdAlert as unknown as TimelineNonEcsData[], + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), + setEventsDeleted: jest.fn(), + setFlyoutAlert: jest.fn(), + id: pageId, + }; + + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + return wrapper; + }; + + it('should hide "View rule details" menu item for rule page id', async () => { + const wrapper = await setup(RULE_DETAILS_PAGE_ID); + wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); + expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0); + expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1); + }); + + it('should show "View rule details" menu item', async () => { + const wrapper = await setup('nothing'); + wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); + expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1); + expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1); + }); + + it('should create a valid link for rule details page', async () => { + const wrapper = await setup('nothing'); + wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); + expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1); + expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().prop('href')).toBe( + '/app/observability/alerts/rules/06f53080-0f91-11ed-9d86-013908b232ef' + ); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx new file mode 100644 index 0000000000000..ab66d058301f5 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButtonIcon, + EuiFlexItem, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiPopover, + EuiToolTip, +} from '@elastic/eui'; + +import React, { useMemo, useState, useCallback } from 'react'; + +import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; +import { CommentType } from '@kbn/cases-plugin/common'; +import type { ActionProps } from '@kbn/timelines-plugin/common'; +import { useKibana } from '../../../utils/kibana_react'; +import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions'; +import { parseAlert } from './parse_alert'; +import { translations, paths } from '../../../config'; +import { + ADD_TO_EXISTING_CASE, + ADD_TO_NEW_CASE, +} from '../containers/alerts_table_t_grid/translations'; +import { ObservabilityAppServices } from '../../../application/types'; +import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types'; +import type { TopAlert } from '../containers/alerts_page/alerts_page'; +import { ObservabilityRuleTypeRegistry } from '../../..'; + +export type ObservabilityActionsProps = Pick< + ActionProps, + 'data' | 'eventId' | 'ecsData' | 'setEventsDeleted' +> & { + setFlyoutAlert: React.Dispatch>; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; + id?: string; +}; + +export function ObservabilityActions({ + data, + eventId, + ecsData, + id: pageId, + observabilityRuleTypeRegistry, + setFlyoutAlert, +}: ObservabilityActionsProps) { + const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {}); + const [openActionsPopoverId, setActionsPopover] = useState(null); + const { cases, http } = useKibana().services; + + const parseObservabilityAlert = useMemo( + () => parseAlert(observabilityRuleTypeRegistry), + [observabilityRuleTypeRegistry] + ); + + const alert = parseObservabilityAlert(dataFieldEs); + + const closeActionsPopover = useCallback(() => { + setActionsPopover(null); + }, []); + + const toggleActionsPopover = useCallback((id) => { + setActionsPopover((current) => (current ? null : id)); + }, []); + + const userCasesPermissions = useGetUserCasesPermissions(); + const ruleId = alert.fields['kibana.alert.rule.uuid'] ?? null; + const linkToRule = + pageId !== RULE_DETAILS_PAGE_ID && ruleId + ? http.basePath.prepend(paths.observability.ruleDetails(ruleId)) + : null; + const caseAttachments: CaseAttachmentsWithoutOwner = useMemo(() => { + return ecsData?._id + ? [ + { + alertId: ecsData?._id ?? '', + index: ecsData?._index ?? '', + type: CommentType.alert, + rule: cases.helpers.getRuleIdFromEvent({ ecs: ecsData, data: data ?? [] }), + }, + ] + : []; + }, [ecsData, cases.helpers, data]); + + const createCaseFlyout = cases.hooks.getUseCasesAddToNewCaseFlyout(); + + const selectCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal(); + + const handleAddToNewCaseClick = useCallback(() => { + createCaseFlyout.open({ attachments: caseAttachments }); + closeActionsPopover(); + }, [createCaseFlyout, caseAttachments, closeActionsPopover]); + + const handleAddToExistingCaseClick = useCallback(() => { + selectCaseModal.open({ attachments: caseAttachments }); + closeActionsPopover(); + }, [caseAttachments, closeActionsPopover, selectCaseModal]); + + const actionsMenuItems = useMemo(() => { + return [ + ...(userCasesPermissions.create && userCasesPermissions.read + ? [ + + {ADD_TO_EXISTING_CASE} + , + + {ADD_TO_NEW_CASE} + , + ] + : []), + + ...(!!linkToRule + ? [ + + {translations.alertsTable.viewRuleDetailsButtonText} + , + ] + : []), + + ...[ + { + closeActionsPopover(); + setFlyoutAlert(alert); + }} + > + {translations.alertsTable.viewAlertDetailsButtonText} + , + ], + ]; + }, [ + userCasesPermissions.create, + userCasesPermissions.read, + handleAddToExistingCaseClick, + handleAddToNewCaseClick, + linkToRule, + alert, + setFlyoutAlert, + closeActionsPopover, + ]); + + const actionsToolTip = + actionsMenuItems.length <= 0 + ? translations.alertsTable.notEnoughPermissions + : translations.alertsTable.moreActionsTextLabel; + + return ( + <> + + + + + + + + toggleActionsPopover(eventId)} + data-test-subj="alertsTableRowActionMore" + /> + + } + isOpen={openActionsPopoverId === eventId} + closePopover={closeActionsPopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx index ed2c3b6ae192b..5f163d64708d7 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx @@ -23,16 +23,7 @@ import { ALERT_START, } from '@kbn/rule-data-utils'; -import { - EuiButtonIcon, - EuiDataGridColumn, - EuiFlexGroup, - EuiFlexItem, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiPopover, - EuiToolTip, -} from '@elastic/eui'; +import { EuiDataGridColumn, EuiFlexGroup } from '@elastic/eui'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; @@ -53,8 +44,6 @@ import type { ControlColumnProps, RowRenderer, } from '@kbn/timelines-plugin/common'; -import { CaseAttachments } from '@kbn/cases-plugin/public'; -import { CommentType } from '@kbn/cases-plugin/common'; import { getAlertsPermissions } from '../../../../hooks/use_alert_permission'; import type { TopAlert } from '../alerts_page/alerts_page'; @@ -63,13 +52,15 @@ import { getRenderCellValue } from '../../components/render_cell_value'; import { observabilityAppId, observabilityFeatureId } from '../../../../../common'; import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions'; import { usePluginContext } from '../../../../hooks/use_plugin_context'; -import { LazyAlertsFlyout, ObservabilityRuleTypeRegistry } from '../../../..'; -import { parseAlert } from '../../components/parse_alert'; -import { translations, paths } from '../../../../config'; +import { LazyAlertsFlyout } from '../../../..'; +import { translations } from '../../../../config'; import { addDisplayNames } from './add_display_names'; -import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from './translations'; import { ObservabilityAppServices } from '../../../../application/types'; import { useBulkAddToCaseActions } from '../../../../hooks/use_alert_bulk_case_actions'; +import { + ObservabilityActions, + ObservabilityActionsProps, +} from '../../components/observability_actions'; interface AlertsTableTGridProps { indexNames: string[]; @@ -82,14 +73,6 @@ interface AlertsTableTGridProps { itemsPerPage?: number; } -export type ObservabilityActionsProps = Pick< - ActionProps, - 'data' | 'eventId' | 'ecsData' | 'setEventsDeleted' -> & { - setFlyoutAlert: React.Dispatch>; - observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; -}; - const EventsThContent = styled.div.attrs(({ className = '' }) => ({ className: `siemEventsTable__thContent ${className}`, }))<{ textAlign?: string; width?: number }>` @@ -146,164 +129,6 @@ const NO_ROW_RENDER: RowRenderer[] = []; const trailingControlColumns: never[] = []; -export function ObservabilityActions({ - data, - eventId, - ecsData, - observabilityRuleTypeRegistry, - setFlyoutAlert, -}: ObservabilityActionsProps) { - const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {}); - const [openActionsPopoverId, setActionsPopover] = useState(null); - const { cases, http } = useKibana().services; - - const parseObservabilityAlert = useMemo( - () => parseAlert(observabilityRuleTypeRegistry), - [observabilityRuleTypeRegistry] - ); - - const alert = parseObservabilityAlert(dataFieldEs); - - const closeActionsPopover = useCallback(() => { - setActionsPopover(null); - }, []); - - const toggleActionsPopover = useCallback((id) => { - setActionsPopover((current) => (current ? null : id)); - }, []); - - const userCasesPermissions = useGetUserCasesPermissions(); - const ruleId = alert.fields['kibana.alert.rule.uuid'] ?? null; - const linkToRule = ruleId ? http.basePath.prepend(paths.observability.ruleDetails(ruleId)) : null; - const caseAttachments: CaseAttachments = useMemo(() => { - return ecsData?._id - ? [ - { - alertId: ecsData?._id ?? '', - index: ecsData?._index ?? '', - owner: observabilityFeatureId, - type: CommentType.alert, - rule: cases.helpers.getRuleIdFromEvent({ ecs: ecsData, data: data ?? [] }), - }, - ] - : []; - }, [ecsData, cases.helpers, data]); - - const createCaseFlyout = cases.hooks.getUseCasesAddToNewCaseFlyout(); - - const selectCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal(); - - const handleAddToNewCaseClick = useCallback(() => { - createCaseFlyout.open({ attachments: caseAttachments }); - closeActionsPopover(); - }, [createCaseFlyout, caseAttachments, closeActionsPopover]); - - const handleAddToExistingCaseClick = useCallback(() => { - selectCaseModal.open({ attachments: caseAttachments }); - closeActionsPopover(); - }, [caseAttachments, closeActionsPopover, selectCaseModal]); - - const actionsMenuItems = useMemo(() => { - return [ - ...(userCasesPermissions.create && userCasesPermissions.read - ? [ - - {ADD_TO_EXISTING_CASE} - , - - {ADD_TO_NEW_CASE} - , - ] - : []), - - ...(!!linkToRule - ? [ - - {translations.alertsTable.viewRuleDetailsButtonText} - , - ] - : []), - - ...[ - { - closeActionsPopover(); - setFlyoutAlert(alert); - }} - > - {translations.alertsTable.viewAlertDetailsButtonText} - , - ], - ]; - }, [ - userCasesPermissions.create, - userCasesPermissions.read, - handleAddToExistingCaseClick, - handleAddToNewCaseClick, - linkToRule, - alert, - setFlyoutAlert, - closeActionsPopover, - ]); - - const actionsToolTip = - actionsMenuItems.length <= 0 - ? translations.alertsTable.notEnoughPermissions - : translations.alertsTable.moreActionsTextLabel; - - return ( - <> - - - - - - - - toggleActionsPopover(eventId)} - data-test-subj="alertsTableRowActionMore" - /> - - } - isOpen={openActionsPopoverId === eventId} - closePopover={closeActionsPopover} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - - - ); -} const FIELDS_WITHOUT_CELL_ACTIONS = [ '@timestamp', 'signal.rule.risk_score', diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx index dd74bb92822e0..26af884840570 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy'; import { ObservabilityRuleTypeRegistry } from '../../../../rules/create_observability_rule_type_registry'; -import { ObservabilityActions } from './alerts_table_t_grid'; -import type { ObservabilityActionsProps } from './alerts_table_t_grid'; +import { ObservabilityActions } from '../../components/observability_actions'; +import type { ObservabilityActionsProps } from '../../components/observability_actions'; const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data'] => { return Object.entries(alerts).reduce( @@ -19,12 +19,17 @@ const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data'] const fakeSetEventsDeleted = () => []; export const getRowActions = (observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry) => { return () => ({ - renderCustomActionsRow: (alert: EcsFieldsResponse, setFlyoutAlert: (data: unknown) => void) => { + renderCustomActionsRow: ( + alert: EcsFieldsResponse, + setFlyoutAlert: (data: unknown, id?: string) => void, + id?: string + ) => { return ( ; export interface ObservabilityPublicPluginsSetup { @@ -73,6 +75,7 @@ export interface ObservabilityPublicPluginsStart { sharedUX: SharedUXPluginStart; ruleTypeRegistry: RuleTypeRegistryContract; actionTypeRegistry: ActionTypeRegistryContract; + security: SecurityPluginStart; } export type ObservabilityPublicStart = ReturnType; diff --git a/x-pack/plugins/observability/public/rules/fixtures/example_alerts.ts b/x-pack/plugins/observability/public/rules/fixtures/example_alerts.ts new file mode 100644 index 0000000000000..1feb79a4c6ac1 --- /dev/null +++ b/x-pack/plugins/observability/public/rules/fixtures/example_alerts.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 inventoryThresholdAlert = [ + { + field: 'kibana.alert.workflow_status', + value: ['open'], + }, + { + field: 'kibana.alert.status', + value: ['active'], + }, + { + field: 'kibana.alert.rule.uuid', + value: ['06f53080-0f91-11ed-9d86-013908b232ef'], + }, + { + field: 'kibana.alert.reason', + value: ['CPU usage is 106.5% in the last 1 min for host-0. Alert when > 1%.'], + }, + { + field: 'kibana.alert.rule.producer', + value: ['infrastructure'], + }, + { + field: 'kibana.alert.rule.consumer', + value: ['alerts'], + }, + { + field: 'kibana.alert.rule.category', + value: ['Inventory'], + }, + { + field: 'kibana.alert.start', + value: ['2022-07-29T22:51:51.904Z'], + }, + { + field: 'event.action', + value: ['open'], + }, + { + field: 'kibana.alert.rule.rule_type_id', + value: ['metrics.alert.inventory.threshold'], + }, + { + field: 'kibana.alert.duration.us', + value: [0], + }, + { + field: '@timestamp', + value: ['2022-07-29T22:51:51.904Z'], + }, + { + field: 'kibana.alert.instance.id', + value: ['host-0'], + }, + { + field: 'kibana.alert.rule.name', + value: ['Test Alert'], + }, + { + field: 'kibana.space_ids', + value: ['default'], + }, + { + field: 'kibana.alert.uuid', + value: ['6d4c6d74-d51a-495c-897d-88ced3b95e30'], + }, + { + field: 'kibana.alert.rule.execution.uuid', + value: ['b8cccff3-2d3e-41d0-a91e-6aae8efce8ba'], + }, + { + field: 'kibana.version', + value: ['8.5.0'], + }, + { + field: 'event.kind', + value: ['signal'], + }, + { + field: 'kibana.alert.rule.parameters', + value: [ + { + sourceId: 'default', + criteria: [ + { + comparator: '>', + timeSize: 1, + metric: 'cpu', + threshold: [1], + customMetric: { + field: '', + aggregation: 'avg', + id: 'alert-custom-metric', + type: 'custom', + }, + timeUnit: 'm', + }, + ], + nodeType: 'host', + }, + ], + }, + { + field: '_id', + value: '6d4c6d74-d51a-495c-897d-88ced3b95e30', + }, + { + field: '_index', + value: '.internal.alerts-observability.metrics.alerts-default-000001', + }, +]; diff --git a/x-pack/plugins/observability/public/utils/kibana_react.ts b/x-pack/plugins/observability/public/utils/kibana_react.ts index baf0f7d674015..09e624b84e89a 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.ts +++ b/x-pack/plugins/observability/public/utils/kibana_react.ts @@ -10,10 +10,12 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { ObservabilityPublicPluginsStart } from '../plugin'; -export type StartServices = CoreStart & - ObservabilityPublicPluginsStart & { +export type StartServices = CoreStart & + ObservabilityPublicPluginsStart & + AdditionalServices & { storage: Storage; }; -const useTypedKibana = () => useKibana(); +const useTypedKibana = () => + useKibana>(); export { useTypedKibana as useKibana }; diff --git a/x-pack/plugins/security/common/model/user_profile.ts b/x-pack/plugins/security/common/model/user_profile.ts index c4e04229e1976..c6156982aab7e 100644 --- a/x-pack/plugins/security/common/model/user_profile.ts +++ b/x-pack/plugins/security/common/model/user_profile.ts @@ -10,6 +10,15 @@ import { VISUALIZATION_COLORS } from '@elastic/eui'; import type { AuthenticatedUser } from './authenticated_user'; import { getUserDisplayName } from './user'; +/** + * IMPORTANT: + * + * The types in this file are duplicated at + * `packages/kbn-user-profile-components/src/user_profile.ts` + * + * When making changes please ensure to keep both files in sync. + */ + /** * Describes basic properties stored in user profile. */ @@ -19,11 +28,6 @@ export interface UserProfile { */ uid: string; - /** - * Indicates whether user profile is enabled or not. - */ - enabled: boolean; - /** * Information about the user that owns profile. */ @@ -142,11 +146,11 @@ export const USER_AVATAR_MAX_INITIALS = 2; * If a color is present on the user profile itself, then that is used. * Otherwise, a color is provided from EUI's Visualization Colors based on the display name. * - * @param {UserProfileUserInfoWithSecurity} user User info + * @param {UserProfileUserInfo} user User info * @param {UserProfileAvatarData} avatar User avatar */ export function getUserAvatarColor( - user: Pick, + user: Pick, avatar?: UserProfileAvatarData ) { if (avatar && avatar.color) { @@ -163,11 +167,11 @@ export function getUserAvatarColor( * If initials are present on the user profile itself, then that is used. * Otherwise, the initials are calculated based off the words in the display name, with a max length of 2 characters. * - * @param {UserProfileUserInfoWithSecurity} user User info + * @param {UserProfileUserInfo} user User info * @param {UserProfileAvatarData} avatar User avatar */ export function getUserAvatarInitials( - user: Pick, + user: Pick, avatar?: UserProfileAvatarData ) { if (avatar && avatar.initials) { diff --git a/x-pack/plugins/security/public/account_management/index.ts b/x-pack/plugins/security/public/account_management/index.ts index 2fcdeb4b05b9c..e1a4957aa71e7 100644 --- a/x-pack/plugins/security/public/account_management/index.ts +++ b/x-pack/plugins/security/public/account_management/index.ts @@ -7,4 +7,8 @@ export { accountManagementApp } from './account_management_app'; export { UserProfileAPIClient } from './user_profile/user_profile_api_client'; -export type { UserProfileBulkGetParams, UserProfileGetCurrentParams } from './user_profile'; +export type { + UserProfileBulkGetParams, + UserProfileGetCurrentParams, + UserProfileSuggestParams, +} from './user_profile'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/index.ts b/x-pack/plugins/security/public/account_management/user_profile/index.ts index d0eb6877f0eaa..ed34d7d4a4339 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/index.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/index.ts @@ -11,4 +11,5 @@ export type { UserProfileProps, UserProfileFormValues } from './user_profile'; export type { UserProfileGetCurrentParams, UserProfileBulkGetParams, + UserProfileSuggestParams, } from './user_profile_api_client'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx index 8a8c25baaf738..fafe66a00515c 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx @@ -15,7 +15,6 @@ import { coreMock, scopedHistoryMock, themeServiceMock } from '@kbn/core/public/ import { UserProfileAPIClient } from '..'; import type { UserProfileData } from '../../../common'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; -import { UserAvatar } from '../../components'; import { UserAPIClient } from '../../management'; import { securityMock } from '../../mocks'; import { Providers } from '../account_management_app'; @@ -205,7 +204,7 @@ describe('useUserProfileForm', () => { ); - expect(testWrapper.exists(UserAvatar)).toBeTruthy(); + expect(testWrapper.exists('UserAvatar')).toBeTruthy(); }); it('should not display if the User is a cloud user', () => { @@ -228,7 +227,7 @@ describe('useUserProfileForm', () => { ); - expect(testWrapper.exists(UserAvatar)).toBeFalsy(); + expect(testWrapper.exists('UserAvatar')).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index 0f6016b8aeb4a..7d0bf823c0f06 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -33,6 +33,7 @@ import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { UserAvatar } from '@kbn/user-profile-components'; import type { AuthenticatedUser, UserProfileAvatarData } from '../../../common'; import { @@ -41,7 +42,7 @@ import { getUserAvatarColor, getUserAvatarInitials, } from '../../../common/model'; -import { UserAvatar, useSecurityApiClients } from '../../components'; +import { useSecurityApiClients } from '../../components'; import { Breadcrumb } from '../../components/breadcrumb'; import { FormChangesProvider, diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts index 5aef4c6e9f8ba..6c4c53ef4ec30 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts @@ -39,6 +39,28 @@ export interface UserProfileBulkGetParams { dataPath?: string; } +/** + * Parameters for the suggest API. + */ +export interface UserProfileSuggestParams { + /** + * Query string used to match name-related fields in user profiles. The following fields are treated as + * name-related: username, full_name and email. + */ + name: string; + + /** + * Desired number of suggestions to return. The default value is 10. + */ + size?: number; + + /** + * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; +} + export class UserProfileAPIClient { private readonly internalDataUpdates$: Subject = new Subject(); @@ -76,6 +98,28 @@ export class UserProfileAPIClient { }); } + /** + * Suggests multiple user profiles by search criteria. + * + * Note: This endpoint is not provided out-of-the-box by the platform. You need to expose your own + * version within your app. An example of how to do this can be found in: + * `examples/user_profile_examples/server/plugin.ts` + * + * @param path Path to your app's suggest endpoint. + * @param params Suggest operation parameters. + * @param params.name Query string used to match name-related fields in user profiles. The + * following fields are treated as name-related: username, full_name and email. + * @param params.size Desired number of suggestions to return. The default value is 10. + * @param params.dataPath By default, suggest API returns user information, but does not return + * any user data. The optional "dataPath" parameter can be used to return personal data for this + * user (within `kibana` namespace only). + */ + public suggest(path: string, params: UserProfileSuggestParams) { + return this.http.post>>(path, { + body: JSON.stringify(params), + }); + } + /** * Updates user profile data of the current user. * @param data Application data to be written (merged with existing data). diff --git a/x-pack/plugins/security/public/components/index.ts b/x-pack/plugins/security/public/components/index.ts index 4787bcacd8662..c2f8baf3f068a 100644 --- a/x-pack/plugins/security/public/components/index.ts +++ b/x-pack/plugins/security/public/components/index.ts @@ -13,5 +13,3 @@ export { useUserProfile, useCurrentUser, } from './use_current_user'; -export { UserAvatar } from './user_avatar'; -export type { UserAvatarProps } from './user_avatar'; diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index d69480145de03..0f2b90721ad9e 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -20,7 +20,11 @@ export type { AuthenticatedUser } from '../common/model'; export type { SecurityLicense, SecurityLicenseFeatures } from '../common/licensing'; export type { UiApi, ChangePasswordProps, PersonalInfoProps } from './ui_api'; export type { UserMenuLink, SecurityNavControlServiceStart } from './nav_control'; -export type { UserProfileBulkGetParams, UserProfileGetCurrentParams } from './account_management'; +export type { + UserProfileBulkGetParams, + UserProfileGetCurrentParams, + UserProfileSuggestParams, +} from './account_management'; export type { AuthenticationServiceStart, AuthenticationServiceSetup } from './authentication'; diff --git a/x-pack/plugins/security/public/mocks.ts b/x-pack/plugins/security/public/mocks.ts index 1b6dfe771ff08..e776d55f17d06 100644 --- a/x-pack/plugins/security/public/mocks.ts +++ b/x-pack/plugins/security/public/mocks.ts @@ -22,7 +22,7 @@ function createStartMock() { return { authc: authenticationMock.createStart(), navControlService: navControlServiceMock.createStart(), - userProfiles: { getCurrent: jest.fn(), bulkGet: jest.fn() }, + userProfiles: { getCurrent: jest.fn(), bulkGet: jest.fn(), suggest: jest.fn() }, uiApi: getUiApiMock.createStart(), }; } diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx index 40ec3704a6c89..2936f705b75a2 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx @@ -85,26 +85,11 @@ describe('SecurityNavControl', () => { "name": "basic1", "type": "basic", }, - "authentication_realm": Object { - "name": "native1", - "type": "native", - }, - "authentication_type": "realm", - "elastic_cloud_user": false, - "email": "email", - "enabled": true, - "full_name": "full name", - "lookup_realm": Object { - "name": "native1", - "type": "native", - }, - "metadata": Object { - "_reserved": false, - }, - "roles": Array [ - "user-role", - ], - "username": "user", + "email": "some@email", + "realm_domain": "some-realm-domain", + "realm_name": "some-realm", + "roles": Array [], + "username": "some-username", } } /> diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx index 30de1b9d9c554..03f162c28dcf7 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx @@ -20,10 +20,11 @@ import type { Observable } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { UserAvatar } from '@kbn/user-profile-components'; import type { UserProfileAvatarData } from '../../common'; import { getUserDisplayName, isUserAnonymous } from '../../common/model'; -import { useCurrentUser, UserAvatar, useUserProfile } from '../components'; +import { useCurrentUser, useUserProfile } from '../components'; export interface UserMenuLink { label: string; @@ -64,9 +65,9 @@ export const SecurityNavControl: FunctionComponent = ({ data-test-subj="userMenuButton" style={{ lineHeight: 'normal' }} > - {currentUser.value && userProfile.value ? ( + {userProfile.value ? ( { "userProfiles": Object { "bulkGet": [Function], "getCurrent": [Function], + "suggest": [Function], }, } `); diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index c9a663e4c2b33..a7ce4e855962f 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -21,10 +21,8 @@ import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/pu import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; -import type { UserProfile, UserProfileData, UserProfileWithSecurity } from '../common'; import type { SecurityLicense } from '../common/licensing'; import { SecurityLicenseService } from '../common/licensing'; -import type { UserProfileBulkGetParams, UserProfileGetCurrentParams } from './account_management'; import { accountManagementApp, UserProfileAPIClient } from './account_management'; import { AnalyticsService } from './analytics'; import { AnonymousAccessService } from './anonymous_access'; @@ -192,6 +190,9 @@ export class SecurityPlugin bulkGet: this.securityApiClients.userProfiles.bulkGet.bind( this.securityApiClients.userProfiles ), + suggest: this.securityApiClients.userProfiles.suggest.bind( + this.securityApiClients.userProfiles + ), }, }; } @@ -232,28 +233,7 @@ export interface SecurityPluginStart { /** * A set of methods to work with Kibana user profiles. */ - userProfiles: { - /** - * Retrieves the user profile of the current user. If the profile isn't available, e.g. for the anonymous users or - * users authenticated via authenticating proxies, the `null` value is returned. - * @param [params] Get current user profile operation parameters. - * @param params.dataPath By default `getCurrent()` returns user information, but does not return any user data. The - * optional "dataPath" parameter can be used to return personal data for this user. - */ - getCurrent( - params?: UserProfileGetCurrentParams - ): Promise | null>; - /** - * Retrieves multiple user profiles by their identifiers. - * @param params Bulk get operation parameters. - * @param params.uids List of user profile identifiers. - * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The - * optional "dataPath" parameter can be used to return personal data for the requested user profiles. - */ - bulkGet( - params: UserProfileBulkGetParams - ): Promise>>; - }; + userProfiles: Pick; /** * Exposes UI components that will be loaded asynchronously. diff --git a/x-pack/plugins/security/server/authorization/check_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_privileges.test.ts index 2e7d6d1c3b853..a0109eb46f7dc 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.test.ts @@ -1403,8 +1403,7 @@ describe('#checkPrivilegesWithRequest.atSpaces', () => { [`saved_object:${savedObjectTypes[0]}/get`]: false, [`saved_object:${savedObjectTypes[1]}/get`]: true, }, - // @ts-expect-error this is wrong on purpose - 'space:space_1': { + 'space:space_2': { [mockActions.login]: true, [mockActions.version]: true, [`saved_object:${savedObjectTypes[0]}/get`]: false, @@ -1414,7 +1413,7 @@ describe('#checkPrivilegesWithRequest.atSpaces', () => { }, }); expect(result).toMatchInlineSnapshot( - `[Error: Invalid response received from Elasticsearch has_privilege endpoint. Error: [application.kibana-our_application]: Payload did not match expected resources]` + `[Error: Invalid response received from Elasticsearch has_privilege endpoint. Error: [application.kibana-our_application]: Payload did not match expected actions]` ); }); @@ -1431,8 +1430,7 @@ describe('#checkPrivilegesWithRequest.atSpaces', () => { [mockActions.login]: true, [mockActions.version]: true, }, - // @ts-expect-error this is wrong on purpose - 'space:space_1': { + 'space:space_2': { [mockActions.login]: true, [mockActions.version]: true, [`saved_object:${savedObjectTypes[0]}/get`]: false, @@ -1442,7 +1440,7 @@ describe('#checkPrivilegesWithRequest.atSpaces', () => { }, }); expect(result).toMatchInlineSnapshot( - `[Error: Invalid response received from Elasticsearch has_privilege endpoint. Error: [application.kibana-our_application]: Payload did not match expected resources]` + `[Error: Invalid response received from Elasticsearch has_privilege endpoint. Error: [application.kibana-our_application]: Payload did not match expected actions]` ); }); diff --git a/x-pack/plugins/security/server/security_checkup/check_cluster_data.test.ts b/x-pack/plugins/security/server/security_checkup/check_cluster_data.test.ts index 092b42468fc8a..f8a8b72901486 100644 --- a/x-pack/plugins/security/server/security_checkup/check_cluster_data.test.ts +++ b/x-pack/plugins/security/server/security_checkup/check_cluster_data.test.ts @@ -113,7 +113,7 @@ function mockIndicesStatsResponseFactory( listOfIndicesWithCount.forEach((indexPair) => { result!.indices![indexPair.name] = { - primaries: { docs: { count: indexPair.count } }, + total: { docs: { count: indexPair.count } }, }; }); diff --git a/x-pack/plugins/security/server/security_checkup/check_cluster_data.ts b/x-pack/plugins/security/server/security_checkup/check_cluster_data.ts index 63ff7822566da..236cdf735595e 100644 --- a/x-pack/plugins/security/server/security_checkup/check_cluster_data.ts +++ b/x-pack/plugins/security/server/security_checkup/check_cluster_data.ts @@ -13,16 +13,18 @@ export const createClusterDataCheck = () => { return async function doesClusterHaveUserData(esClient: ElasticsearchClient, log: Logger) { if (!clusterHasUserData) { try { - const { indices = {} } = await esClient.indices.stats(); - const indexIds = indices ? Object.keys(indices) : []; + const { indices = {} } = await esClient.indices.stats({ + filter_path: 'indices.*.total.docs.count', + }); + + const indexIds = Object.keys(indices); - clusterHasUserData = indexIds.some((indexName: string) => { + clusterHasUserData = indexIds.some((indexId: string) => { // Check index to see if it starts with known internal prefixes - const isInternalIndex = - indexName.startsWith('.') || indexName.startsWith('kibana_sample_'); + const isInternalIndex = indexId.startsWith('.') || indexId.startsWith('kibana_sample_'); // Check index to see if it has any docs - const hasDocs = (indices[indexName].primaries?.docs?.count || 0) > 0; + const hasDocs = (indices[indexId].total?.docs?.count || 0) > 0; return !isInternalIndex && hasDocs; }); diff --git a/x-pack/plugins/security/server/user_profile/user_profile_service.ts b/x-pack/plugins/security/server/user_profile/user_profile_service.ts index aa8174ef24095..32dc1ec51cd88 100644 --- a/x-pack/plugins/security/server/user_profile/user_profile_service.ts +++ b/x-pack/plugins/security/server/user_profile/user_profile_service.ts @@ -62,12 +62,12 @@ export interface UserProfileServiceStart { ): Promise>>; /** - * Retrieves a single user profile by identifier. + * Suggests multiple user profiles by search criteria. * @param params Suggest operation parameters. - * @param params.name Query string used to match name-related fields in user profiles. The following fields are - * treated as name-related: username, full_name and email. - * @param params.dataPath By default API returns user information, but does not return any user data. The optional - * "dataPath" parameter can be used to return personal data for this user (within `kibana` namespace). + * @param params.name Query string used to match name-related fields in user profiles. The following fields are treated as name-related: username, full_name and email. + * @param params.size Desired number of suggestion to return. The default value is 10. + * @param params.dataPath By default, suggest API returns user information, but does not return any user data. The optional "dataPath" parameter can be used to return personal data for this user (within `kibana` namespace only). + * @param params.requiredPrivileges The set of the privileges that users associated with the suggested user profile should have in the specified space. If not specified, privileges check isn't performed and all matched profiles are returned irrespective to the privileges of the associated users. */ suggest( params: UserProfileSuggestParams diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.test.ts index ad4350b864499..12a0c08582a0f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.test.ts @@ -5,8 +5,9 @@ * 2.0. */ +import * as t from 'io-ts'; import type { CreateRulesSchema, SavedQueryCreateSchema } from './rule_schemas'; -import { createRulesSchema } from './rule_schemas'; +import { createRulesSchema, responseSchema } from './rule_schemas'; import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; @@ -20,7 +21,7 @@ import { } from './rule_schemas.mock'; import { getListArrayMock } from '../types/lists.mock'; -describe('create rules schema', () => { +describe('rules schema', () => { test('empty objects do not validate', () => { const payload = {}; @@ -1287,4 +1288,208 @@ describe('create rules schema', () => { expect(getPaths(left(message.errors))).toEqual(['invalid keys "data_view_id"']); }); }); + + describe('response', () => { + const testSchema = { + required: { + testRequiredString: t.string, + }, + optional: { + testOptionalString: t.string, + }, + defaultable: { + testDefaultableString: t.string, + }, + }; + const schema = responseSchema(testSchema.required, testSchema.optional, testSchema.defaultable); + + describe('required fields', () => { + test('should allow required fields with the correct type', () => { + const payload = { + testRequiredString: 'required_string', + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('should not allow required fields to be undefined', () => { + const payload = { + testRequiredString: undefined, + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "testRequiredString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('should not allow required fields to be omitted entirely', () => { + const payload = { + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "testRequiredString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('should not allow required fields with an incorrect type', () => { + const payload = { + testRequiredString: 5, + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "testRequiredString"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('optional fields', () => { + test('should allow optional fields with the correct type', () => { + const payload: t.TypeOf = { + testRequiredString: 'required_string', + testOptionalString: 'optional_string', + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('should allow optional fields to be undefined', () => { + const payload: t.TypeOf = { + testRequiredString: 'required_string', + testOptionalString: undefined, + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('should allow optional fields to be omitted entirely', () => { + const payload = { + testRequiredString: 'required_string', + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('should not allow optional fields with an incorrect type', () => { + const payload = { + testRequiredString: 'required_string', + testOptionalString: 5, + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "testOptionalString"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('defaultable fields', () => { + test('should allow defaultable fields with the correct type', () => { + const payload = { + testRequiredString: 'required_string', + testDefaultableString: 'defaultable_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('should not allow defaultable fields to be undefined', () => { + const payload = { + testRequiredString: 'required_string', + testDefaultableString: undefined, + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "testDefaultableString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('should allow defaultable fields to be omitted entirely', () => { + const payload = { + testRequiredString: 'required_string', + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "testDefaultableString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('should not allow defaultable fields with an incorrect type', () => { + const payload = { + testRequiredString: 'required_string', + testDefaultableString: 5, + }; + + const decoded = schema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "testDefaultableString"', + ]); + expect(message.schema).toEqual({}); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts index e55fb9b6d763c..46186479bf726 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts @@ -110,7 +110,11 @@ const patchSchema = < ]); }; -const responseSchema = < +type OrUndefined

= { + [K in keyof P]: P[K] | t.UndefinedC; +}; + +export const responseSchema = < Required extends t.Props, Optional extends t.Props, Defaultable extends t.Props @@ -119,9 +123,19 @@ const responseSchema = < optionalFields: Optional, defaultableFields: Defaultable ) => { + // This bit of logic is to force all fields to be accounted for in conversions from the internal + // rule schema to the response schema. Rather than use `t.partial`, which makes each field optional, + // we make each field required but possibly undefined. The result is that if a field is forgotten in + // the conversion from internal schema to response schema TS will report an error. If we just used t.partial + // instead, then optional fields can be accidentally omitted from the conversion - and any actual values + // in those fields internally will be stripped in the response. + const optionalWithUndefined = Object.keys(optionalFields).reduce((acc, key) => { + acc[key] = t.union([optionalFields[key], t.undefined]); + return acc; + }, {}) as OrUndefined; return t.intersection([ t.exact(t.type(requiredFields)), - t.exact(t.partial(optionalFields)), + t.exact(t.type(optionalWithUndefined)), t.exact(t.type(defaultableFields)), ]); }; diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts index a6e40325cac4e..3ae8c8f6d0644 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts @@ -153,7 +153,7 @@ export const policyFactoryWithoutPaidFeatures = ( ): PolicyConfig => { const rollbackConfig = { rollback: { - remediation: { + self_healing: { enabled: false, }, }, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 2f66b9848fb9f..6e72d411768e5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -923,7 +923,7 @@ export interface PolicyConfig { alerts?: { [key: string]: unknown; rollback: { - remediation: { + self_healing: { enabled: boolean; }; }; diff --git a/x-pack/plugins/security_solution/common/license/policy_config.test.ts b/x-pack/plugins/security_solution/common/license/policy_config.test.ts index 97e18399cd260..d0066eeb051a8 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.test.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.test.ts @@ -125,14 +125,14 @@ describe('policy_config and licenses', () => { it('allows advanced rollback option when Platinum', () => { const policy = policyFactory(); - policy.windows.advanced = { alerts: { rollback: { remediation: { enabled: true } } } }; // make policy change + policy.windows.advanced = { alerts: { rollback: { self_healing: { enabled: true } } } }; // make policy change const valid = isEndpointPolicyValidForLicense(policy, Platinum); expect(valid).toBeTruthy(); }); it('blocks advanced rollback option when below Platinum', () => { const policy = policyFactory(); - policy.windows.advanced = { alerts: { rollback: { remediation: { enabled: true } } } }; // make policy change + policy.windows.advanced = { alerts: { rollback: { self_healing: { enabled: true } } } }; // make policy change let valid = isEndpointPolicyValidForLicense(policy, Gold); expect(valid).toBeFalsy(); @@ -515,7 +515,7 @@ describe('policy_config and licenses', () => { policy.windows.advanced = { alerts: { rollback: { - remediation: { + self_healing: { enabled: true, }, }, diff --git a/x-pack/plugins/security_solution/common/license/policy_config.ts b/x-pack/plugins/security_solution/common/license/policy_config.ts index ac5f0458958d3..5e2774ef6a018 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.ts @@ -234,8 +234,8 @@ function isEndpointAdvancedPolicyValidForLicense(policy: PolicyConfig, license: // only platinum or higher may use rollback if ( - policy.windows.advanced?.alerts?.rollback.remediation.enabled !== - defaults.windows.advanced?.alerts?.rollback.remediation.enabled + policy.windows.advanced?.alerts?.rollback.self_healing.enabled !== + defaults.windows.advanced?.alerts?.rollback.self_healing.enabled ) { return false; } diff --git a/x-pack/plugins/security_solution/cypress/integration/hosts/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/hosts/inspect.spec.ts index 892fc1eb4c7f3..8246222f5598d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/hosts/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/hosts/inspect.spec.ts @@ -13,7 +13,6 @@ import { clickInspectButton, closesModal, openStatsAndTables } from '../../tasks import { login, visit, visitHostDetailsPage } from '../../tasks/login'; import { HOSTS_URL } from '../../urls/navigation'; -import { waitForPageToBeLoaded } from '../../tasks/common'; describe('Inspect', () => { before(() => { @@ -22,7 +21,6 @@ describe('Inspect', () => { context('Hosts stats and tables', () => { before(() => { visit(HOSTS_URL); - waitForPageToBeLoaded(); }); afterEach(() => { closesModal(); diff --git a/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts index 5ab92be404c9d..b288ecc537e99 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts @@ -146,7 +146,7 @@ describe('ml conditional links', () => { visitWithoutDateRange(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -154,7 +154,7 @@ describe('ml conditional links', () => { visitWithoutDateRange(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -162,7 +162,7 @@ describe('ml conditional links', () => { visitWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/network/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/network/inspect.spec.ts index 58ba46616cdac..c54072799b290 100644 --- a/x-pack/plugins/security_solution/cypress/integration/network/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/network/inspect.spec.ts @@ -6,7 +6,6 @@ */ import { INSPECT_MODAL, INSPECT_NETWORK_BUTTONS_IN_SECURITY } from '../../screens/inspect'; -import { waitForPageToBeLoaded } from '../../tasks/common'; import { closesModal, openStatsAndTables } from '../../tasks/inspect'; import { login, visit } from '../../tasks/login'; @@ -18,7 +17,6 @@ describe('Inspect', () => { before(() => { login(); visit(NETWORK_URL); - waitForPageToBeLoaded(); }); afterEach(() => { closesModal(); diff --git a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts index faedd11b1d12b..cc1cfdf40009c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts @@ -255,7 +255,7 @@ describe('url state', () => { cy.get(ANOMALIES_TAB).should( 'have.attr', 'href', - "/app/security/hosts/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')" + "/app/security/hosts/name/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')" ); cy.get(BREADCRUMBS) @@ -270,7 +270,7 @@ describe('url state', () => { .should( 'have.attr', 'href', - `/app/security/hosts/siem-kibana?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))` + `/app/security/hosts/name/siem-kibana?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))` ); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts index 9a862e914b7a5..cba3ba66109cc 100644 --- a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts +++ b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts @@ -29,4 +29,4 @@ export const KIBANA_NAVIGATION_TOGGLE = '[data-test-subj="toggleNavButton"]'; export const SPACES_BUTTON = '[data-test-subj="spacesNavSelector"]'; -export const getGoToSpaceMenuItem = (space: string) => `[data-test-subj="${space}-gotoSpace"]`; +export const getGoToSpaceMenuItem = (space: string) => `[data-test-subj="space-avatar-${space}"]`; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx index d68bb2d2266bb..f77b9d37156fa 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx @@ -31,14 +31,9 @@ jest.mock('@elastic/charts', () => { }; }); -jest.mock('uuid', () => { - const actual = jest.requireActual('uuid'); - - return { - ...actual, - v4: jest.fn().mockReturnValue('test-uuid'), - }; -}); +jest.mock('uuid', () => ({ + v4: jest.fn().mockReturnValue('test-uuid'), +})); jest.mock('../../../overview/components/detection_response/alerts_by_status/chart_label', () => { return { @@ -184,4 +179,10 @@ describe('DonutChart', () => { const { container } = render(); expect(container.querySelector(`[data-test-subj="legend"]`)).not.toBeInTheDocument(); }); + + test('should render label within a tooltip', () => { + const { container } = render(); + const tooltip = container.getElementsByClassName('euiToolTipAnchor')[0]; + expect(tooltip.textContent).toBe(props.label); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx index f1db20798a582..fd611fd2da3ab 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui'; import React, { useMemo } from 'react'; import type { Datum, NodeColorAccessor, PartialTheme } from '@elastic/charts'; @@ -83,6 +83,7 @@ export const DonutChart = ({ }), [euiTheme.colors.disabled] ); + return ( {title} - {data ? ( - - {label} - - ) : ( - + + {label} - )} + {data == null || totalCount == null || totalCount === 0 ? ( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts index f6a08ea9f6c85..0f2cce38662d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts @@ -653,6 +653,12 @@ export const generateAlertDetailsDataMock = () => [ values: ['dummy.exe'], originalValue: ['dummy.exe'], }, + { + category: 'rule', + field: 'rule.name', + values: ['Test Rule Name'], + originalValue: ['Test Rule Name'], + }, ]; export const mockAlertDetailsData = generateAlertDetailsDataMock(); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx index 54a1b2fdfba8d..d967334dd953e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx @@ -74,7 +74,7 @@ describe('AlertSummaryView', () => { ); - ['host.name', 'user.name', i18n.RULE_TYPE, 'query'].forEach((fieldId) => { + ['host.name', 'user.name', i18n.RULE_TYPE, 'query', 'rule.name'].forEach((fieldId) => { expect(getByText(fieldId)); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx index d9b88ecf12a83..5b383a89958d1 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx @@ -38,6 +38,7 @@ const alwaysDisplayedFields: EventSummaryField[] = [ { id: 'host.name' }, { id: 'agent.id', overrideField: AGENT_STATUS_FIELD_NAME, label: i18n.AGENT_STATUS }, { id: 'user.name' }, + { id: 'rule.name' }, { id: ALERT_RULE_TYPE, label: i18n.RULE_TYPE }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.test.tsx index db2ce64b99789..bd324c582455f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.test.tsx @@ -42,19 +42,6 @@ describe('InsightAccordion', () => { expect(screen.getByText(errorText)).toBeInTheDocument(); }); - it("shows the text and a disabled button when it's in the empty state", () => { - const text = 'the text'; - render( - - - - ); - - const button = screen.getByRole('button', { name: text }); - expect(button).toBeInTheDocument(); - expect(button).toHaveAttribute('aria-disabled'); - }); - it('shows the text and renders the correct content', () => { const text = 'the text'; const contentText = 'content text'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.tsx index f84b306f1bc93..67635b52fff3f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insight_accordion.tsx @@ -18,12 +18,7 @@ const StyledAccordion = euiStyled(EuiAccordion)` border-radius: 6px; `; -const EmptyAccordion = euiStyled(StyledAccordion)` - color: ${({ theme }) => theme.eui.euiColorDisabledText}; - pointer-events: none; -`; - -export type InsightAccordionState = 'loading' | 'error' | 'success' | 'empty'; +export type InsightAccordionState = 'loading' | 'error' | 'success'; interface Props { prefix: string; @@ -61,24 +56,6 @@ export const InsightAccordion = React.memo( onToggle={onToggle} /> ); - case 'empty': - // Since EuiAccordions don't have an empty state and they don't allow to style the arrow - // we're using a custom styled Accordion here and we're adding the faded-out button manually. - return ( - - - {text} - - } - buttonProps={{ - 'aria-disabled': true, - }} - arrowDisplay="none" - /> - ); case 'success': // The accordion can display the content now return ( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx index 0a1adaf9ce9e4..0171595fcc3b0 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx @@ -17,7 +17,12 @@ import { InsightAccordion } from './insight_accordion'; import { SimpleAlertTable } from './simple_alert_table'; import { InvestigateInTimelineButton } from '../table/investigate_in_timeline_button'; import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations'; -import { PROCESS_ANCESTRY, PROCESS_ANCESTRY_COUNT, PROCESS_ANCESTRY_ERROR } from './translations'; +import { + PROCESS_ANCESTRY, + PROCESS_ANCESTRY_COUNT, + PROCESS_ANCESTRY_EMPTY, + PROCESS_ANCESTRY_ERROR, +} from './translations'; interface Props { data: TimelineEventsDetailsItem; @@ -65,12 +70,15 @@ export const RelatedAlertsByProcessAncestry = React.memo( const [cache, setCache] = useState>({}); const onToggle = useCallback((isOpen: boolean) => setShowContent(isOpen), []); + const isEmpty = !!cache.alertIds && cache.alertIds.length === 0; // Makes sure the component is not fetching data before the accordion // has been openend. const renderContent = useCallback(() => { if (!showContent) { return null; + } else if (isEmpty) { + return PROCESS_ANCESTRY_EMPTY; } else if (cache.alertIds) { return ( ( onCacheLoad={setCache} /> ); - }, [showContent, cache, data, eventId, timelineId, index, originalDocumentId]); - - const isEmpty = !!cache.alertIds && cache.alertIds.length === 0; + }, [showContent, cache, data, eventId, timelineId, index, originalDocumentId, isEmpty]); return ( ( fieldType: fieldFromBrowserField?.type, }); + const isEmpty = count === 0; + + let state: InsightAccordionState = 'loading'; + if (error) { + state = 'error'; + } else if (alertIds || isEmpty) { + state = 'success'; + } + const renderContent = useCallback(() => { if (!alertIds || !cellData?.dataProviders) { return null; + } else if (isEmpty && state !== 'loading') { + return SESSION_EMPTY; } return ( <> @@ -80,16 +91,7 @@ export const RelatedAlertsBySession = React.memo( ); - }, [alertIds, cellData?.dataProviders]); - - let state: InsightAccordionState = 'loading'; - if (error) { - state = 'error'; - } else if (count === 0) { - state = 'empty'; - } else if (alertIds) { - state = 'success'; - } + }, [alertIds, cellData?.dataProviders, isEmpty, state]); return ( ( fieldType: fieldFromBrowserField?.type, }); + const isEmpty = count === 0; + + let state: InsightAccordionState = 'loading'; + if (error) { + state = 'error'; + } else if (alertIds) { + state = 'success'; + } + const renderContent = useCallback(() => { if (!alertIds || !cellData?.dataProviders) { return null; + } else if (isEmpty && state !== 'loading') { + return SOURCE_EVENT_EMPTY; } return ( <> @@ -80,16 +96,7 @@ export const RelatedAlertsBySourceEvent = React.memo( ); - }, [alertIds, cellData?.dataProviders]); - - let state: InsightAccordionState = 'loading'; - if (error) { - state = 'error'; - } else if (count === 0) { - state = 'empty'; - } else if (alertIds) { - state = 'success'; - } + }, [alertIds, cellData?.dataProviders, isEmpty, state]); return ( (({ eventId }) => { const toasts = useToasts(); const [relatedCases, setRelatedCases] = useState(undefined); - const [areCasesLoading, setAreCasesLoading] = useState(true); const [hasError, setHasError] = useState(false); const renderContent = useCallback(() => renderCaseContent(relatedCases), [relatedCases]); @@ -50,7 +49,6 @@ export const RelatedCases = React.memo(({ eventId }) => { toasts.addWarning(CASES_ERROR_TOAST(error)); } setRelatedCases(relatedCaseList); - setAreCasesLoading(false); }, [eventId, cases.api, toasts]); useEffect(() => { @@ -60,8 +58,6 @@ export const RelatedCases = React.memo(({ eventId }) => { let state: InsightAccordionState = 'loading'; if (hasError) { state = 'error'; - } else if (!areCasesLoading && relatedCases?.length === 0) { - state = 'empty'; } else if (relatedCases) { state = 'success'; } @@ -121,7 +117,6 @@ function getTextFromState(state: InsightAccordionState, caseCount = 0) { case 'error': return CASES_ERROR; case 'success': - case 'empty': return CASES_COUNT(caseCount); default: return ''; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/insights/translations.ts index 29825ee47cdb3..e77bbeb60ae8e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/translations.ts @@ -34,6 +34,13 @@ export const PROCESS_ANCESTRY_ERROR = i18n.translate( } ); +export const PROCESS_ANCESTRY_EMPTY = i18n.translate( + 'xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_empty', + { + defaultMessage: 'There are no related alerts by process ancestry.', + } +); + export const SESSION_LOADING = i18n.translate( 'xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_source_event_loading', { defaultMessage: 'Loading related alerts by source event' } @@ -46,6 +53,13 @@ export const SESSION_ERROR = i18n.translate( } ); +export const SESSION_EMPTY = i18n.translate( + 'xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_session_empty', + { + defaultMessage: 'There are no related alerts by session', + } +); + export const SESSION_COUNT = (count?: number) => i18n.translate( 'xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_session_count', @@ -66,6 +80,13 @@ export const SOURCE_EVENT_ERROR = i18n.translate( } ); +export const SOURCE_EVENT_EMPTY = i18n.translate( + 'xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_source_event_empty', + { + defaultMessage: 'There are no related alerts by source event', + } +); + export const SOURCE_EVENT_COUNT = (count?: number) => i18n.translate( 'xpack.securitySolution.alertDetails.overview.insights_related_alerts_by_source_event_count', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx index 9779215550c38..73095d11decaf 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx @@ -97,7 +97,7 @@ const FlyoutBodySection = styled.section` `} `; -const FlyoutCheckboxesSection = styled(EuiFlyoutBody)` +const FlyoutCheckboxesSection = styled.section` overflow-y: inherit; height: auto; .euiFlyoutBody__overflowContent { @@ -361,7 +361,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ !isIndexPatternLoading && !isRuleLoading && !mlJobLoading && ( - <> + {isRuleEQLSequenceStatement && ( <> @@ -439,7 +439,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ )} - + )} diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx index 831ac9bb4f4c0..caa9c00e1a90c 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx @@ -15,10 +15,10 @@ export const getTabsOnHostsUrl = (tabName: HostsTableType, search?: string) => `/${tabName}${appendSearch(search)}`; export const getHostDetailsUrl = (detailName: string, search?: string) => - `/${detailName}${appendSearch(search)}`; + `/name/${detailName}${appendSearch(search)}`; export const getTabsOnHostDetailsUrl = ( detailName: string, tabName: HostsTableType, search?: string -) => `/${detailName}/${tabName}${appendSearch(search)}`; +) => `/name/${detailName}/${tabName}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_users.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_users.tsx index 20171634ad6ea..8a90810228d01 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_users.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_users.tsx @@ -9,10 +9,10 @@ import type { UsersTableType } from '../../../users/store/model'; import { appendSearch } from './helpers'; export const getUsersDetailsUrl = (detailName: string, search?: string) => - `/${detailName}${appendSearch(search)}`; + `/name/${detailName}${appendSearch(search)}`; export const getTabsOnUsersDetailsUrl = ( detailName: string, tabName: UsersTableType, search?: string -) => `/${detailName}/${tabName}${appendSearch(search)}`; +) => `/name/${detailName}/${tabName}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx index 13fdb96b86aeb..478916c2b6c0d 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx @@ -60,13 +60,13 @@ describe('Custom Links', () => { describe('HostDetailsLink', () => { test('should render valid link to Host Details with hostName as the display text', () => { const wrapper = mount(); - expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`); + expect(wrapper.find('EuiLink').prop('href')).toEqual(`/name/${encodeURIComponent(hostName)}`); expect(wrapper.text()).toEqual(hostName); }); test('should render valid link to Host Details with child text as the display text', () => { const wrapper = mount({hostName}); - expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`); + expect(wrapper.find('EuiLink').prop('href')).toEqual(`/name/${encodeURIComponent(hostName)}`); expect(wrapper.text()).toEqual(hostName); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx index a18d51cb63773..17b46ca398191 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -88,7 +88,9 @@ export const MlHostConditionalContainer = React.memo(() => { }); return ( - + ); } }} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 43ed208148caa..585e5f094da77 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -240,7 +240,7 @@ describe('Navigation Breadcrumbs', () => { hostsBreadcrumbs, { text: 'siem-kibana', - href: 'securitySolutionUI/hosts/siem-kibana', + href: 'securitySolutionUI/hosts/name/siem-kibana', }, { text: 'Authentications', href: '' }, ]); @@ -458,7 +458,7 @@ describe('Navigation Breadcrumbs', () => { }), expect.objectContaining({ text: 'siem-kibana', - href: 'securitySolutionUI/hosts/siem-kibana', + href: 'securitySolutionUI/hosts/name/siem-kibana', onClick: expect.any(Function), }), { @@ -556,7 +556,7 @@ describe('Navigation Breadcrumbs', () => { hostsBreadcrumbs, { text: 'siem-kibana', - href: 'securitySolutionUI/hosts/siem-kibana', + href: 'securitySolutionUI/hosts/name/siem-kibana', }, { text: 'Authentications', href: '' }, ]); @@ -787,7 +787,7 @@ describe('Navigation Breadcrumbs', () => { }), expect.objectContaining({ text: 'siem-kibana', - href: 'securitySolutionUI/hosts/siem-kibana', + href: 'securitySolutionUI/hosts/name/siem-kibana', onClick: expect.any(Function), }), { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index 2648ac258b114..c5aa5fbfce880 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -89,7 +89,7 @@ describe('Table Navigation', () => { `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(firstTab.props().href).toBe( - `/app/securitySolutionUI/hosts/siem-window/authentications${SEARCH_QUERY}` + `/app/securitySolutionUI/hosts/name/siem-window/authentications${SEARCH_QUERY}` ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/page/index.tsx b/x-pack/plugins/security_solution/public/common/components/page/index.tsx index f6a154d787f78..9a0e4d2f0e556 100644 --- a/x-pack/plugins/security_solution/public/common/components/page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/page/index.tsx @@ -18,6 +18,23 @@ export const SecuritySolutionAppWrapper = styled.div` `; SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper'; +/** + * Stylesheet for Eui class overrides for components that may be displayed when content + * on the page has been set to display in full screen mode. It ensures that certain Eui + * components, that position themselves just below the kibana header, are displayed correctly + * when shown above content that is set to `full screen`. + */ +export const FULL_SCREEN_CONTENT_OVERRIDES_CSS_STYLESHEET = () => css` + .euiOverlayMask--belowHeader { + top: 0 !important; + } + + .euiFlyout { + top: 0 !important; + height: 100% !important; + } +`; + /** * Stylesheet with Eui class overrides in order to address display issues caused when * the Timeline overlay is opened. These are normally adjustments to ensure that the diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx index 9aa85975a1b46..059b1cfd660c6 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx @@ -7,14 +7,11 @@ import { useCallback, useMemo } from 'react'; import { CommentType } from '@kbn/cases-plugin/common'; -import { APP_ID } from '../../../../common/constants'; import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana'; import { ADD_TO_CASE_SUCCESS } from './translations'; import type { LensAttributes } from './types'; -const owner = APP_ID; - export const useAddToExistingCase = ({ onAddToCaseClicked, lensAttributes, @@ -33,7 +30,6 @@ export const useAddToExistingCase = ({ timeRange, attributes: lensAttributes, })}}`, - owner, type: CommentType.user as const, }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx index a0b367738ad9b..36efbd55228f7 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx @@ -8,7 +8,6 @@ import { useCallback, useMemo } from 'react'; import { CommentType } from '@kbn/cases-plugin/common'; -import { APP_ID } from '../../../../common/constants'; import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana'; import { ADD_TO_CASE_SUCCESS } from './translations'; @@ -20,8 +19,6 @@ export interface UseAddToNewCaseProps { lensAttributes: LensAttributes | null; } -const owner = APP_ID; - export const useAddToNewCase = ({ onClick, timeRange, lensAttributes }: UseAddToNewCaseProps) => { const userCasesPermissions = useGetUserCasesPermissions(); const { cases } = useKibana().services; @@ -32,7 +29,6 @@ export const useAddToNewCase = ({ onClick, timeRange, lensAttributes }: UseAddTo timeRange, attributes: lensAttributes, })}}`, - owner, type: CommentType.user as const, }, ]; diff --git a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx index c8418595be87b..58c7536e5db13 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState, useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { SCROLLING_DISABLED_CLASS_NAME } from '../../../../common/constants'; @@ -76,3 +76,39 @@ export const useTimelineFullScreen = (): TimelineFullScreen => { ); return memoizedReturn; }; + +/** + * Checks to see if there is a EUI Data Grid in full screen mode in the document tree + */ +export const useHasDataGridFullScreen = (): boolean => { + const [isDataGridFullScreen, setIsDataGridFullScreen] = useState(false); + + useEffect(() => { + const observeTarget = document.body; + const docBodyObserver = new MutationObserver((changes) => { + for (const change of changes) { + if (change.attributeName === 'class') { + setIsDataGridFullScreen(observeTarget.classList.contains('euiDataGrid__restrictBody')); + } + } + }); + + docBodyObserver.observe(observeTarget, { attributes: true }); + + return () => docBodyObserver.disconnect(); + }, []); + + return isDataGridFullScreen; +}; + +/** + * Checks to see if any content (ex. timeline, global or data grid) is + * currently being displayed in full screen mode + */ +export const useHasFullScreenContent = (): boolean => { + const { globalFullScreen } = useGlobalFullScreen(); + const { timelineFullScreen } = useTimelineFullScreen(); + const dataGridFullScreen = useHasDataGridFullScreen(); + + return globalFullScreen || timelineFullScreen || dataGridFullScreen; +}; diff --git a/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx b/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx index 03e5f96b58707..d8f1b984b8b09 100644 --- a/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx @@ -11,7 +11,7 @@ import type { GetRenderCellValue, } from '@kbn/triggers-actions-ui-plugin/public'; -import { APP_ID } from '../../../../common/constants'; +import { APP_ID, CASES_FEATURE_ID } from '../../../../common/constants'; import { getTimelinesInStorageByIds } from '../../../timelines/containers/local_storage'; import { TimelineId } from '../../../../common/types'; import { columns } from '../../../detections/configurations/security_solution_detections'; @@ -31,6 +31,7 @@ const registerAlertsTableConfiguration = ( registry.register({ id: APP_ID, + casesFeatureId: CASES_FEATURE_ID, columns: alertColumns, getRenderCellValue: useRenderCellValue as GetRenderCellValue, useInternalFlyout: () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx index c8ac06cee92a0..17a5a8448a702 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx @@ -8,11 +8,10 @@ import React, { useCallback, useMemo } from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; import { CommentType } from '@kbn/cases-plugin/common'; -import type { CaseAttachments } from '@kbn/cases-plugin/public'; +import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; import type { TimelineNonEcsData } from '../../../../../common/search_strategy'; import { TimelineId } from '../../../../../common/types'; -import { APP_ID } from '../../../../../common/constants'; import type { Ecs } from '../../../../../common/ecs'; import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from '../translations'; @@ -40,13 +39,12 @@ export const useAddToCaseActions = ({ return ecsData?.event?.kind?.includes('signal'); }, [ecsData]); - const caseAttachments: CaseAttachments = useMemo(() => { + const caseAttachments: CaseAttachmentsWithoutOwner = useMemo(() => { return ecsData?._id ? [ { alertId: ecsData?._id ?? '', index: ecsData?._index ?? '', - owner: APP_ID, type: CommentType.alert, rule: casesUi.helpers.getRuleIdFromEvent({ ecs: ecsData, data: nonEcsData ?? [] }), }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_bulk_add_to_case_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_bulk_add_to_case_actions.tsx index 0251c130981f6..fa397e5389725 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_bulk_add_to_case_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_bulk_add_to_case_actions.tsx @@ -6,7 +6,6 @@ */ import { useMemo } from 'react'; -import { APP_ID } from '../../../../../common/constants'; import type { TimelineItem } from '../../../../../common/search_strategy'; import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; import { ADD_TO_CASE_DISABLED, ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from '../translations'; @@ -40,7 +39,7 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi disableOnQuery: true, disabledLabel: ADD_TO_CASE_DISABLED, onClick: (items?: TimelineItem[]) => { - const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items, APP_ID) : []; + const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items) : []; createCaseFlyout.open({ attachments: caseAttachments }); }, }, @@ -51,7 +50,7 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi disabledLabel: ADD_TO_CASE_DISABLED, 'data-test-subj': 'attach-existing-case', onClick: (items?: TimelineItem[]) => { - const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items, APP_ID) : []; + const caseAttachments = items ? casesUi.helpers.groupAlertsByRule(items) : []; selectCaseModal.open({ attachments: caseAttachments }); }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx index 9744e3fcf720e..dc06ad7c8e840 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx @@ -140,9 +140,10 @@ const RulePreviewComponent: React.FC = ({ return true; // Don't do the expensive logic if we don't need it } if (isMlLoading) { - const selectedJobs = jobs.filter(({ id }) => machineLearningJobId.includes(id)); - return selectedJobs.every((job) => isJobStarted(job.jobState, job.datafeedState)); + return false; } + const selectedJobs = jobs.filter(({ id }) => machineLearningJobId.includes(id)); + return selectedJobs.every((job) => isJobStarted(job.jobState, job.datafeedState)); }, [jobs, machineLearningJobId, ruleType, isMlLoading]); const [queryPreviewIdSelected, setQueryPreviewRadioIdSelected] = useState(QUICK_QUERY_SELECT_ID); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts index 165e14ba0d159..a8b12a9ebc121 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts @@ -78,13 +78,15 @@ export const usePreviewRule = ({ 1000; const { unit: intervalUnit, value: intervalValue } = getTimeTypeValue(advancedOptions.interval); - const { unit: lookbackUnit, value: lookbackValue } = getTimeTypeValue(advancedOptions.lookback); - const duration = moment.duration(intervalValue, intervalUnit as 's' | 'm' | 'h'); - duration.add(lookbackValue, lookbackUnit as 's' | 'm' | 'h'); + const duration = moment.duration(intervalValue, intervalUnit); const ruleIntervalDuration = duration.asMilliseconds(); invocationCount = Math.max(Math.ceil(timeframeDuration / ruleIntervalDuration), 1); interval = advancedOptions.interval; + + const { unit: lookbackUnit, value: lookbackValue } = getTimeTypeValue(advancedOptions.lookback); + duration.add(lookbackValue, lookbackUnit); + from = `now-${duration.asSeconds()}s`; } const showInvocationCountWarning = invocationCount > REASONABLE_INVOCATION_COUNT; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts index a941fe57a3b5a..ae56e6a525508 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts @@ -50,10 +50,10 @@ describe('helpers', () => { expect(result).toEqual({ unit: 'm', value: 0 }); }); - test('returns timeObj with unit set to empty string if no expected time type found', () => { + test('returns timeObj with unit set to default unit value of "ms" if no expected time type found', () => { const result = getTimeTypeValue('5l'); - expect(result).toEqual({ unit: '', value: 5 }); + expect(result).toEqual({ unit: 'ms', value: 5 }); }); test('returns timeObj with unit of s and value 5 when time is 5s ', () => { @@ -80,10 +80,10 @@ describe('helpers', () => { expect(result).toEqual({ unit: 'm', value: 5 }); }); - test('returns timeObj with value of 0 and unit of "" if random string passed in', () => { + test('returns timeObj with value of 0 and unit of "ms" if random string passed in', () => { const result = getTimeTypeValue('random'); - expect(result).toEqual({ unit: '', value: 0 }); + expect(result).toEqual({ unit: 'ms', value: 0 }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts index bec0746bde5d1..1de5085829b45 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts @@ -50,9 +50,9 @@ import { stepActionsDefaultValue } from '../../../../components/rules/step_rule_ import type { FieldValueThreshold } from '../../../../components/rules/threshold_input'; import type { EqlOptionsSelected } from '../../../../../../common/search_strategy'; -export const getTimeTypeValue = (time: string): { unit: string; value: number } => { - const timeObj = { - unit: '', +export const getTimeTypeValue = (time: string): { unit: Unit; value: number } => { + const timeObj: { unit: Unit; value: number } = { + unit: 'ms', value: 0, }; const filterTimeVal = time.match(/\d+/g); @@ -65,7 +65,7 @@ export const getTimeTypeValue = (time: string): { unit: string; value: number } filterTimeType != null && ['s', 'm', 'h'].includes(filterTimeType[0]) ) { - timeObj.unit = filterTimeType[0]; + timeObj.unit = filterTimeType[0] as Unit; } return timeObj; }; @@ -461,8 +461,8 @@ export const formatScheduleStepData = (scheduleData: ScheduleStepRule): Schedule formatScheduleData.interval ); const { unit: fromUnit, value: fromValue } = getTimeTypeValue(formatScheduleData.from); - const duration = moment.duration(intervalValue, intervalUnit as 's' | 'm' | 'h'); - duration.add(fromValue, fromUnit as 's' | 'm' | 'h'); + const duration = moment.duration(intervalValue, intervalUnit); + duration.add(fromValue, fromUnit); formatScheduleData.from = `now-${duration.asSeconds()}s`; formatScheduleData.to = 'now'; } diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx index da7495d79695f..e40c32883e0cd 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx @@ -97,7 +97,7 @@ describe('body', () => { test(`it should pass expected object properties to ${componentName}`, () => { const wrapper = mount( - + - `${HOSTS_PATH}/${hostName}/${tabName}`; + `${HOSTS_PATH}/name/${hostName}/${tabName}`; export const navTabsHostDetails = ({ hasMlUserPermissions, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx index 105ab3e6e4157..57f164ddd3b24 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx @@ -55,7 +55,7 @@ export const HostsContainer = React.memo(() => ( /> )} /> - ( }) => ( )} /> - - ( + + )} + /> + ( diff --git a/x-pack/plugins/security_solution/public/hosts/pages/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/types.ts index a102d9b06072c..17e2af205f799 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/types.ts @@ -13,7 +13,7 @@ import type { GlobalTimeArgs } from '../../common/containers/use_global_time'; import type { InputsModelId } from '../../common/store/inputs/constants'; import { HOSTS_PATH } from '../../../common/constants'; -export const hostDetailsPagePath = `${HOSTS_PATH}/:detailName`; +export const hostDetailsPagePath = `${HOSTS_PATH}/name/:detailName`; export type HostsTabsProps = GlobalTimeArgs & { filterQuery: string; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_policy_link.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_policy_link.tsx index 97b51cf604260..b33d8059aa4ac 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_policy_link.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_policy_link.tsx @@ -7,7 +7,8 @@ import React, { memo, useMemo } from 'react'; import type { EuiLinkAnchorProps } from '@elastic/eui'; -import { EuiLink } from '@elastic/eui'; +import { EuiLink, EuiText, EuiIcon } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { getPolicyDetailPath } from '../common/routing'; import { useNavigateByRouterEventHandler } from '../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { useAppUrl } from '../../common/lib/kibana/hooks'; @@ -27,7 +28,7 @@ export const EndpointPolicyLink = memo< >(({ policyId, backLink, children, missingPolicies = {}, ...otherProps }) => { const { getAppUrl } = useAppUrl(); const { toRoutePath, toRouteUrl } = useMemo(() => { - const path = getPolicyDetailPath(policyId); + const path = policyId ? getPolicyDetailPath(policyId) : ''; return { toRoutePath: backLink ? { pathname: path, state: { backLink } } : path, toRouteUrl: getAppUrl({ path }), @@ -35,10 +36,20 @@ export const EndpointPolicyLink = memo< }, [policyId, getAppUrl, backLink]); const clickHandler = useNavigateByRouterEventHandler(toRoutePath); - if (missingPolicies[policyId]) { + if (!policyId || missingPolicies[policyId]) { return ( {children} + { + + +   + + + } ); } diff --git a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx index ce2ef375b45c3..b5c7629b76aa6 100644 --- a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx +++ b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx @@ -13,7 +13,11 @@ import classnames from 'classnames'; import { useLocation } from 'react-router-dom'; import type { EuiPortalProps } from '@elastic/eui/src/components/portal/portal'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; -import { TIMELINE_OVERRIDES_CSS_STYLESHEET } from '../../../common/components/page'; +import { useHasFullScreenContent } from '../../../common/containers/use_full_screen'; +import { + FULL_SCREEN_CONTENT_OVERRIDES_CSS_STYLESHEET, + TIMELINE_OVERRIDES_CSS_STYLESHEET, +} from '../../../common/components/page'; import { SELECTOR_TIMELINE_IS_VISIBLE_CSS_CLASS_NAME, TIMELINE_EUI_THEME_ZINDEX_LEVEL, @@ -62,6 +66,11 @@ const OverlayRootContainer = styled.div` padding: ${({ theme: { eui } }) => eui.euiSizeXL}; } + &.fullScreen { + top: 0; + height: 100%; + } + .fullHeight { height: 100%; } @@ -70,14 +79,26 @@ const OverlayRootContainer = styled.div` const PAGE_OVERLAY_CSS_CLASSNAME = 'securitySolution-pageOverlay'; const PAGE_OVERLAY_DOCUMENT_BODY_IS_VISIBLE_CLASSNAME = `${PAGE_OVERLAY_CSS_CLASSNAME}-isVisible`; const PAGE_OVERLAY_DOCUMENT_BODY_LOCK_CLASSNAME = `${PAGE_OVERLAY_CSS_CLASSNAME}-lock`; +const PAGE_OVERLAY_DOCUMENT_BODY_FULLSCREEN_CLASSNAME = `${PAGE_OVERLAY_CSS_CLASSNAME}-fullScreen`; const PageOverlayGlobalStyles = createGlobalStyle<{ theme: EuiTheme }>` body.${PAGE_OVERLAY_DOCUMENT_BODY_LOCK_CLASSNAME} { overflow: hidden; } + //------------------------------------------------------------------------------------------- + // Style overrides for when Page Overlay is in full screen mode + //------------------------------------------------------------------------------------------- + // Needs to override some position of EUI components to ensure they are displayed correctly + // when the top Kibana header is not visible + //------------------------------------------------------------------------------------------- + body.${PAGE_OVERLAY_DOCUMENT_BODY_FULLSCREEN_CLASSNAME} { + ${FULL_SCREEN_CONTENT_OVERRIDES_CSS_STYLESHEET} + } + //------------------------------------------------------------------------------------------- // TIMELINE SPECIFIC STYLES + //------------------------------------------------------------------------------------------- // The timeline overlay uses a custom z-index, which causes issues with any other content that // is normally appended to the 'document.body' directly (like popups, masks, flyouts, etc). // The styles below will be applied anytime the timeline is opened/visible and attempts to @@ -113,6 +134,14 @@ const unSetDocumentBodyLock = () => { document.body.classList.remove(PAGE_OVERLAY_DOCUMENT_BODY_LOCK_CLASSNAME); }; +const setDocumentBodyFullScreen = () => { + document.body.classList.add(PAGE_OVERLAY_DOCUMENT_BODY_FULLSCREEN_CLASSNAME); +}; + +const unSetDocumentBodyFullScreen = () => { + document.body.classList.remove(PAGE_OVERLAY_DOCUMENT_BODY_FULLSCREEN_CLASSNAME); +}; + export interface PageOverlayProps { children: ReactNode; @@ -182,6 +211,7 @@ export const PageOverlay = memo( }) => { const { pathname } = useLocation(); const isMounted = useIsMounted(); + const showInFullScreen = useHasFullScreenContent(); const [openedOnPathName, setOpenedOnPathName] = useState(null); const portalEleRef = useRef(); @@ -204,6 +234,7 @@ export const PageOverlay = memo( [PAGE_OVERLAY_CSS_CLASSNAME]: true, scrolling: enableScrolling, hidden: isHidden, + fullScreen: showInFullScreen, 'eui-scrollBar': enableScrolling, 'padding-xs': paddingSize === 'xs', 'padding-s': paddingSize === 's', @@ -211,7 +242,7 @@ export const PageOverlay = memo( 'padding-l': paddingSize === 'l', 'padding-xl': paddingSize === 'xl', }); - }, [enableScrolling, isHidden, paddingSize]); + }, [enableScrolling, isHidden, paddingSize, showInFullScreen]); // Capture the URL `pathname` that the overlay was opened for useEffect(() => { @@ -256,20 +287,26 @@ export const PageOverlay = memo( if (isHidden) { unSetDocumentBodyOverlayIsVisible(); unSetDocumentBodyLock(); + unSetDocumentBodyFullScreen(); } else { setDocumentBodyOverlayIsVisible(); if (lockDocumentBody) { setDocumentBodyLock(); } + + if (showInFullScreen) { + setDocumentBodyFullScreen(); + } } } return () => { unSetDocumentBodyLock(); unSetDocumentBodyOverlayIsVisible(); + unSetDocumentBodyFullScreen(); }; - }, [isHidden, isMounted, lockDocumentBody]); + }, [isHidden, isMounted, lockDocumentBody, showInFullScreen]); return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts index 7f8ceb7ccea49..233a21e599b0a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts @@ -13,7 +13,7 @@ export const isPolicyOutOfDate = ( reported: HostMetadata['Endpoint']['policy']['applied'], current: HostInfo['policy_info'] ): boolean => { - if (current === undefined || current === null) { + if (!current || !reported.id) { return false; // we don't know, can't declare it out-of-date } return !( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts b/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts index ba96d43994401..6208416252cdd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts @@ -938,13 +938,13 @@ export const AdvancedPolicySchema: AdvancedPolicySchemaType[] = [ ), }, { - key: 'windows.advanced.alerts.rollback.remediation.enabled', + key: 'windows.advanced.alerts.rollback.self_healing.enabled', first_supported_version: '8.4', documentation: i18n.translate( - 'xpack.securitySolution.endpoint.policy.advanced.windows.advanced.alerts.rollback.remediation.enabled', + 'xpack.securitySolution.endpoint.policy.advanced.windows.advanced.alerts.rollback.self_healing.enabled', { defaultMessage: - 'Remediate malware artifacts when prevention alerts are triggered. Warning: data loss can occur. Default: false', + 'Self-healing erases attack artifacts when prevention alerts are triggered. Warning: data loss can occur. Default: false', } ), license: 'platinum', diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx index 7a330a82c1d5a..122bfa6fdd07c 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx @@ -15,6 +15,7 @@ import { EuiPanel, EuiSpacer, EuiText, + EuiToolTip, } from '@elastic/eui'; import type { CaseStatuses } from '@kbn/cases-plugin/common'; @@ -104,7 +105,11 @@ const getTableColumns: GetTableColumns = () => [ textOnly: true, 'data-test-subj': 'recentlyCreatedCaseName', - render: (id: string, { name }) => {name}, + render: (id: string, { name }) => ( + + {name} + + ), }, { field: 'totalAlerts', diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx index c72db22f41067..a2373b0734ece 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx @@ -16,6 +16,7 @@ import { EuiPanel, EuiSpacer, EuiTablePagination, + EuiToolTip, } from '@elastic/eui'; import { FormattedCount } from '../../../../common/components/formatted_number'; @@ -103,7 +104,11 @@ const getTableColumns: GetTableColumns = (handleClick) => [ truncateText: true, textOnly: true, 'data-test-subj': 'hostSeverityAlertsTable-hostName', - render: (hostName: string) => , + render: (hostName: string) => ( + + + + ), }, { field: 'totalAlerts', diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 521d9c30554f6..0eed36ff14102 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -55,7 +55,11 @@ export const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo, openRu render: (name: string, { id }) => { const url = getAppUrl({ deepLinkId: SecurityPageName.rules, path: `id/${id}` }); return ( - + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} [ truncateText: true, textOnly: true, 'data-test-subj': 'userSeverityAlertsTable-userName', - render: (userName: string) => , + render: (userName: string) => ( + + + + ), }, { field: 'totalAlerts', diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index e7b92c5f5f756..42924f4a40886 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -137,7 +137,7 @@ export class Plugin implements IPlugin raspberrypi @@ -201,7 +201,7 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot raspberrypi diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 1104ff630b61a..e639e1b3cc519 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -23,7 +23,7 @@ import type { TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, } from '@kbn/triggers-actions-ui-plugin/public'; import type { CasesUiStart } from '@kbn/cases-plugin/public'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/public'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import type { TimelinesUIStart } from '@kbn/timelines-plugin/public'; import type { SessionViewStart } from '@kbn/session-view-plugin/public'; import type { KubernetesSecurityStart } from '@kbn/kubernetes-security-plugin/public'; @@ -87,7 +87,7 @@ export interface StartPlugins { spaces?: SpacesPluginStart; dataViewFieldEditor: IndexPatternFieldEditorStart; osquery?: OsqueryPluginStart; - security: SecurityPluginSetup; + security: SecurityPluginStart; cloudSecurityPosture: CspClientPluginStart; threatIntelligence: ThreatIntelligencePluginStart; } diff --git a/x-pack/plugins/security_solution/public/users/pages/constants.ts b/x-pack/plugins/security_solution/public/users/pages/constants.ts index b8788fb5ac29e..c128de47236a4 100644 --- a/x-pack/plugins/security_solution/public/users/pages/constants.ts +++ b/x-pack/plugins/security_solution/public/users/pages/constants.ts @@ -8,7 +8,7 @@ import { USERS_PATH } from '../../../common/constants'; import { UsersTableType } from '../store/model'; -export const usersDetailsPagePath = `${USERS_PATH}/:detailName`; +export const usersDetailsPagePath = `${USERS_PATH}/name/:detailName`; export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|)`; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx index b57d1014e0167..90bc856e0d058 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx @@ -12,7 +12,7 @@ import { UsersTableType } from '../../store/model'; import { USERS_PATH } from '../../../../common/constants'; const getTabsOnUsersDetailsUrl = (userName: string, tabName: UsersTableType) => - `${USERS_PATH}/${userName}/${tabName}`; + `${USERS_PATH}/name/${userName}/${tabName}`; export const navTabsUsersDetails = ( userName: string, diff --git a/x-pack/plugins/security_solution/public/users/pages/index.tsx b/x-pack/plugins/security_solution/public/users/pages/index.tsx index 055bb2bb71ab2..b75959b5265a9 100644 --- a/x-pack/plugins/security_solution/public/users/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/index.tsx @@ -34,7 +34,7 @@ export const UsersContainer = React.memo(() => { /> )} /> - { }) => ( )} /> - ( + + )} + /> + ( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts index 3d0d3f5a96363..b4fcee14a1804 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts @@ -7,6 +7,7 @@ import type { StartServicesAccessor } from '@kbn/core/server'; import type { SecuritySolutionPluginRouter } from '../../types'; import type { StartPlugins } from '../../plugin'; +import type { ConfigType } from '../../config'; import { validateEvents, validateEntities, @@ -19,7 +20,8 @@ import { handleEvents } from './resolver/events'; export const registerResolverRoutes = async ( router: SecuritySolutionPluginRouter, - startServices: StartServicesAccessor + startServices: StartServicesAccessor, + config: ConfigType ) => { const [, { ruleRegistry }] = await startServices(); router.post( @@ -28,7 +30,7 @@ export const registerResolverRoutes = async ( validate: validateTree, options: { authRequired: true }, }, - handleTree(ruleRegistry) + handleTree(ruleRegistry, config) ); router.post( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/handler.ts index 598e47bd7f6cb..8084dd26fc9ec 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/handler.ts @@ -9,14 +9,21 @@ import type { RequestHandler } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; import type { RuleRegistryPluginStartContract } from '@kbn/rule-registry-plugin/server'; import type { validateTree } from '../../../../../common/endpoint/schema/resolver'; +import type { ConfigType } from '../../../../config'; import { Fetcher } from './utils/fetch'; export function handleTree( - ruleRegistry: RuleRegistryPluginStartContract + ruleRegistry: RuleRegistryPluginStartContract, + config: ConfigType ): RequestHandler> { return async (context, req, res) => { const client = (await context.core).elasticsearch.client; - const alertsClient = await ruleRegistry.getRacClientWithRequest(req); + const { + experimentalFeatures: { insightsRelatedAlertsByProcessAncestry }, + } = config; + const alertsClient = insightsRelatedAlertsByProcessAncestry + ? await ruleRegistry.getRacClientWithRequest(req) + : undefined; const fetcher = new Fetcher(client, alertsClient); const body = await fetcher.tree(req.body); return res.ok({ diff --git a/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/console_templates/enable_host_risk_score.console b/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/console_templates/enable_host_risk_score.console index 4be696fbee0c4..0f2ab246a815f 100644 --- a/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/console_templates/enable_host_risk_score.console +++ b/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/console_templates/enable_host_risk_score.console @@ -83,7 +83,12 @@ PUT ml_host_risk_score_{{space_name}} "type": "date" }, "risk": { - "type": "keyword" + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } }, "risk_stats": { "properties": { @@ -205,7 +210,12 @@ PUT ml_host_risk_score_latest_{{space_name}} "type": "date" }, "risk": { - "type": "keyword" + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } }, "risk_stats": { "properties": { diff --git a/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/routes/__snapshots__/read_prebuilt_dev_tool_content_route.test.ts.snap b/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/routes/__snapshots__/read_prebuilt_dev_tool_content_route.test.ts.snap index 85f86aebc7521..1cb33ff1b15e4 100644 --- a/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/routes/__snapshots__/read_prebuilt_dev_tool_content_route.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/prebuilt_dev_tool_content/routes/__snapshots__/read_prebuilt_dev_tool_content_route.test.ts.snap @@ -86,7 +86,12 @@ PUT ml_host_risk_score_default \\"type\\": \\"date\\" }, \\"risk\\": { - \\"type\\": \\"keyword\\" + \\"type\\": \\"text\\", + \\"fields\\": { + \\"keyword\\": { + \\"type\\": \\"keyword\\" + } + } }, \\"risk_stats\\": { \\"properties\\": { @@ -208,7 +213,12 @@ PUT ml_host_risk_score_latest_default \\"type\\": \\"date\\" }, \\"risk\\": { - \\"type\\": \\"keyword\\" + \\"type\\": \\"text\\", + \\"fields\\": { + \\"keyword\\": { + \\"type\\": \\"keyword\\" + } + } }, \\"risk_stats\\": { \\"properties\\": { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index ea75ed05e81f3..0024228b23417 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -22,6 +22,7 @@ import { EQL_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, ML_RULE_TYPE_ID, + NEW_TERMS_RULE_TYPE_ID, QUERY_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID, SIGNALS_ID, @@ -491,6 +492,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { SAVED_QUERY_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, + NEW_TERMS_RULE_TYPE_ID, ], }, }, diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 0b6ef6b6f4fe7..9161ee89cd52c 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -120,7 +120,7 @@ export const initRoutes = ( patchRulesBulkRoute(router, ml, logger); deleteRulesBulkRoute(router, logger); performBulkActionRoute(router, ml, logger); - registerResolverRoutes(router, getStartServices); + registerResolverRoutes(router, getStartServices, config); registerRuleMonitoringRoutes(router); diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index eb5099cb7be33..e14af8c553736 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -199,6 +199,42 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'Number of notifications enabled' }, }, }, + new_terms: { + enabled: { + type: 'long', + _meta: { description: 'Number of new_terms rules enabled' }, + }, + disabled: { + type: 'long', + _meta: { description: 'Number of new_terms rules disabled' }, + }, + alerts: { + type: 'long', + _meta: { description: 'Number of alerts generated by new_terms rules' }, + }, + cases: { + type: 'long', + _meta: { + description: 'Number of cases attached to new_terms detection rule alerts', + }, + }, + legacy_notifications_enabled: { + type: 'long', + _meta: { description: 'Number of legacy notifications enabled' }, + }, + legacy_notifications_disabled: { + type: 'long', + _meta: { description: 'Number of legacy notifications disabled' }, + }, + notifications_enabled: { + type: 'long', + _meta: { description: 'Number of notifications enabled' }, + }, + notifications_disabled: { + type: 'long', + _meta: { description: 'Number of notifications enabled' }, + }, + }, elastic_total: { enabled: { type: 'long', _meta: { description: 'Number of elastic rules enabled' } }, disabled: { diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts index b4902e40db822..eb32c58bda6cf 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts @@ -67,6 +67,16 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, }, + new_terms: { + enabled: 0, + disabled: 0, + alerts: 0, + cases: 0, + legacy_notifications_enabled: 0, + legacy_notifications_disabled: 0, + notifications_enabled: 0, + notifications_disabled: 0, + }, elastic_total: { enabled: 0, disabled: 0, diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts index 4b8f62ad01ed3..499c79b11fcfa 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts @@ -22,6 +22,7 @@ export interface RulesTypeUsage { eql: FeatureTypeUsage; machine_learning: FeatureTypeUsage; threat_match: FeatureTypeUsage; + new_terms: FeatureTypeUsage; elastic_total: FeatureTypeUsage; custom_total: FeatureTypeUsage; } diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts index d878d0a5145ab..b029d37e0082d 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts @@ -222,6 +222,12 @@ describe('Detections Usage and Metrics', () => { ${'threat_match'} | ${true} | ${false} | ${true} | ${0} | ${0} | ${1} | ${0} ${'threat_match'} | ${false} | ${true} | ${false} | ${0} | ${1} | ${0} | ${0} ${'threat_match'} | ${false} | ${false} | ${false} | ${0} | ${0} | ${0} | ${0} + ${'new_terms'} | ${true} | ${true} | ${false} | ${1} | ${0} | ${0} | ${0} + ${'new_terms'} | ${true} | ${false} | ${true} | ${0} | ${0} | ${1} | ${0} + ${'new_terms'} | ${false} | ${false} | ${true} | ${0} | ${0} | ${0} | ${1} + ${'new_terms'} | ${true} | ${false} | ${true} | ${0} | ${0} | ${1} | ${0} + ${'new_terms'} | ${false} | ${true} | ${false} | ${0} | ${1} | ${0} | ${0} + ${'new_terms'} | ${false} | ${false} | ${false} | ${0} | ${0} | ${0} | ${0} `( 'expect { "ruleType": $ruleType, "enabled": $enabled, "hasLegacyNotification": $hasLegacyNotification, "hasNotification": $hasNotification } to equal { legacy_notifications_enabled: $expectedLegacyNotificationsEnabled, legacy_notifications_disabled: $expectedLegacyNotificationsDisabled, notifications_enabled: $expectedNotificationsEnabled, notifications_disabled, $expectedNotificationsDisabled }', ({ diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.ts index 3aa3c3bbc29b0..26eeffb6b9f0a 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.ts @@ -59,6 +59,15 @@ export const updateRuleUsage = ( detectionRuleMetric, }), }; + } else if (detectionRuleMetric.rule_type === 'new_terms') { + updatedUsage = { + ...usage, + new_terms: updateQueryUsage({ + ruleType: detectionRuleMetric.rule_type, + usage, + detectionRuleMetric, + }), + }; } if (detectionRuleMetric.elastic_rule) { diff --git a/x-pack/plugins/security_solution/server/usage/queries/get_detection_rules.ts b/x-pack/plugins/security_solution/server/usage/queries/get_detection_rules.ts index 36a81c074dc48..9f2a13c15e528 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/get_detection_rules.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/get_detection_rules.ts @@ -19,6 +19,7 @@ import { QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID, + NEW_TERMS_RULE_TYPE_ID, } from '@kbn/securitysolution-rules'; import type { RuleSearchResult } from '../types'; @@ -44,6 +45,7 @@ export const getDetectionRules = async ({ `${filterAttribute}: ${SAVED_QUERY_RULE_TYPE_ID}`, `${filterAttribute}: ${THRESHOLD_RULE_TYPE_ID}`, `${filterAttribute}: ${INDICATOR_RULE_TYPE_ID}`, + `${filterAttribute}: ${NEW_TERMS_RULE_TYPE_ID}`, ].join(' OR '); const query: SavedObjectsCreatePointInTimeFinderOptions = { diff --git a/x-pack/plugins/security_solution/server/usage/queries/get_event_log_by_type_and_status.ts b/x-pack/plugins/security_solution/server/usage/queries/get_event_log_by_type_and_status.ts index 6813ffd647819..40ec06e201280 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/get_event_log_by_type_and_status.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/get_event_log_by_type_and_status.ts @@ -13,6 +13,7 @@ import { QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID, + NEW_TERMS_RULE_TYPE_ID, } from '@kbn/securitysolution-rules'; import type { EventLogTypeStatusAggs, RuleSearchResult } from '../types'; import type { EventLogStatusMetric } from '../detections/rules/types'; @@ -92,6 +93,7 @@ const _getEventLogByTypeAndStatus = async ({ QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID, + NEW_TERMS_RULE_TYPE_ID, ], }); diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts index 5b21a07154e87..866948f7215a2 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts @@ -18,7 +18,8 @@ export const formatSyntheticsPolicy = ( 'monitor.project.name': string; 'monitor.project.id': string; } - > + >, + isLegacy?: boolean ) => { const configKeys = Object.keys(config) as ConfigKey[]; @@ -45,6 +46,8 @@ export const formatSyntheticsPolicy = ( if (configItem) { if (formatters[key]) { configItem.value = formatters[key]?.(config); + } else if (key === ConfigKey.MONITOR_SOURCE_TYPE && isLegacy) { + configItem.value = undefined; } else { configItem.value = config[key] === undefined || config[key] === null ? null : config[key]; } diff --git a/x-pack/plugins/synthetics/common/runtime_types/common.ts b/x-pack/plugins/synthetics/common/runtime_types/common.ts index 4262a1a244568..15306ee0f495f 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/common.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/common.ts @@ -29,7 +29,6 @@ export const SummaryType = t.partial({ export const StatesIndexStatusType = t.type({ indexExists: t.boolean, - docCount: t.number, indices: t.string, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx index d3803a0aa0260..3412b779c4c6f 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx @@ -33,6 +33,7 @@ export function getMonitorListColumns({ errorSummariesById, canEditSynthetics, reloadPage, + loading, syntheticsMonitors, }: { basePath: string; @@ -41,6 +42,7 @@ export function getMonitorListColumns({ errorSummariesById: Map; canEditSynthetics: boolean; syntheticsMonitors: EncryptedSyntheticsSavedMonitor[]; + loading: boolean; reloadPage: () => void; }) { const getIsMonitorUnHealthy = (monitor: EncryptedSyntheticsSavedMonitor) => { @@ -132,7 +134,12 @@ export function getMonitorListColumns({ defaultMessage: 'Enabled', }), render: (_enabled: boolean, monitor: EncryptedSyntheticsSavedMonitor) => ( - + ), }, { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx index 6c89c89342c96..be1123b12d417 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import { EuiSwitch, EuiSwitchEvent, EuiLoadingSpinner } from '@elastic/eui'; import React, { useMemo } from 'react'; +import { EuiSwitch, EuiSwitchEvent, EuiLoadingSpinner } from '@elastic/eui'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { FETCH_STATUS } from '@kbn/observability-plugin/public'; import { useCanEditSynthetics } from '../../../../../../hooks/use_capabilities'; import { ConfigKey, EncryptedSyntheticsMonitor } from '../../../../../../../common/runtime_types'; @@ -18,9 +19,16 @@ interface Props { monitor: EncryptedSyntheticsMonitor; reloadPage: () => void; initialLoading?: boolean; + isSwitchable?: boolean; } -export const MonitorEnabled = ({ id, monitor, reloadPage, initialLoading }: Props) => { +export const MonitorEnabled = ({ + id, + monitor, + reloadPage, + initialLoading = false, + isSwitchable = true, +}: Props) => { const isDisabled = !useCanEditSynthetics(); const monitorName = monitor[ConfigKey.NAME]; @@ -51,7 +59,7 @@ export const MonitorEnabled = ({ id, monitor, reloadPage, initialLoading }: Prop {isLoading || initialLoading ? ( ) : ( - )} ); }; + +const SwitchWithCursor = euiStyled(EuiSwitch)<{ isSwitchable: boolean }>` + & > button { + cursor: ${({ isSwitchable }) => (isSwitchable ? undefined : 'not-allowed')}; + } +`; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx index 0be43f3d337ac..bf19c52b4c340 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_list.tsx @@ -111,6 +111,7 @@ export const MonitorList = ({ errorSummariesById, canEditSynthetics, syntheticsMonitors, + loading, reloadPage, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts index dda469403063f..0dee2edfd7903 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts @@ -7,7 +7,7 @@ import { IHttpFetchError } from '@kbn/core-http-browser'; import { PayloadAction } from '@reduxjs/toolkit'; -import { call, put, takeLeading } from 'redux-saga/effects'; +import { call, put, takeEvery, takeLeading } from 'redux-saga/effects'; import { fetchEffectFactory } from '../utils/fetch_effect'; import { fetchMonitorListAction, @@ -30,7 +30,7 @@ export function* fetchMonitorListEffect() { } export function* upsertMonitorEffect() { - yield takeLeading( + yield takeEvery( fetchUpsertMonitorAction, function* (action: PayloadAction): Generator { try { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts index 7e600804518a1..199b811790169 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts @@ -45,7 +45,8 @@ export const useUpdatePolicy = ({ const { formattedPolicy, dataStream, currentInput } = formatSyntheticsPolicy( newPolicy, monitorType, - config + config, + true ); // prevent an infinite loop of updating the policy diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts index 02b10956694c1..157a2f5054fec 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts @@ -20,11 +20,10 @@ describe('fetch saga effect factory', () => { it('works with success workflow', () => { const indexStatusResult = { indexExists: true, - docCount: 2712532, indices: 'heartbeat-*,synthetics-*', }; const fetchStatus = async (): Promise => { - return { indexExists: true, docCount: 2712532, indices: 'heartbeat-*,synthetics-*' }; + return { indexExists: true, indices: 'heartbeat-*,synthetics-*' }; }; fetchEffect = fetchEffectFactory( fetchStatus, diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts index 854d1184e9730..4ccb6965a5122 100644 --- a/x-pack/plugins/synthetics/public/plugin.ts +++ b/x-pack/plugins/synthetics/public/plugin.ts @@ -122,7 +122,7 @@ export class UptimePlugin hasData: async () => { const dataHelper = await getUptimeDataHelper(); const status = await dataHelper.indexStatus(); - return { hasData: status.docCount > 0, indices: status.indices }; + return { hasData: status.indexExists, indices: status.indices }; }, fetchData: async (params: FetchDataParams) => { const dataHelper = await getUptimeDataHelper(); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts index 093b16662214a..283aa4c04827b 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts @@ -17,14 +17,12 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy result: { body: { _shards: { total }, - count, }, }, } = await uptimeEsClient.count({ terminate_after: 1 }); return { indices, indexExists: total > 0, - docCount: count, }; } catch (e) { if (e.meta.statusCode === 404) { @@ -32,7 +30,6 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy return { indices: '', indexExists: false, - docCount: 0, }; } throw e; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/telemetry/sender.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/telemetry/sender.ts index d6267b462d9fe..610f43aa9cfd9 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/telemetry/sender.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/telemetry/sender.ts @@ -103,20 +103,27 @@ export class TelemetryEventsSender { this.isSending = false; } - private async fetchClusterInfo(): Promise { + private async fetchClusterInfo(): Promise { if (this.esClient === undefined || this.esClient === null) { throw Error('elasticsearch client is unavailable: cannot retrieve cluster information'); } - return await this.esClient.info(); + try { + return await this.esClient.info(); + } catch (e) { + this.logger.debug(`Error fetching cluster information: ${e}`); + } } private async fetchLicenseInfo() { if (this.esClient === undefined || this.esClient === null) { throw Error('elasticsearch client is unavailable: cannot retrieve license information'); } - - return await this.esClient.license.get(); + try { + return await this.esClient.license.get(); + } catch (e) { + this.logger.debug(`Error fetching license information: ${e}`); + } } public async sendEvents(telemetryUrl: string, queue: TelemetryQueue) { diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index f9c9e78b0cb38..ae6b48d5d4cec 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -9335,6 +9335,58 @@ } } }, + "new_terms": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules enabled" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules disabled" + } + }, + "alerts": { + "type": "long", + "_meta": { + "description": "Number of alerts generated by new_terms rules" + } + }, + "cases": { + "type": "long", + "_meta": { + "description": "Number of cases attached to new_terms detection rule alerts" + } + }, + "legacy_notifications_enabled": { + "type": "long", + "_meta": { + "description": "Number of legacy notifications enabled" + } + }, + "legacy_notifications_disabled": { + "type": "long", + "_meta": { + "description": "Number of legacy notifications disabled" + } + }, + "notifications_enabled": { + "type": "long", + "_meta": { + "description": "Number of notifications enabled" + } + }, + "notifications_disabled": { + "type": "long", + "_meta": { + "description": "Number of notifications enabled" + } + } + } + }, "elastic_total": { "properties": { "enabled": { diff --git a/x-pack/plugins/threat_intelligence/common/constants.ts b/x-pack/plugins/threat_intelligence/common/constants.ts index a2f8b09322309..a25ee5a691da4 100644 --- a/x-pack/plugins/threat_intelligence/common/constants.ts +++ b/x-pack/plugins/threat_intelligence/common/constants.ts @@ -14,3 +14,5 @@ export const DEFAULT_THREAT_INDEX_KEY = 'securitySolution:defaultThreatIndex' as export const DEFAULT_DATE_FORMAT = 'dateFormat' as const; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz' as const; + +export const THREAT_QUERY_BASE = 'event.type: indicator and event.category : threat'; diff --git a/x-pack/plugins/threat_intelligence/common/types/indicator.ts b/x-pack/plugins/threat_intelligence/common/types/indicator.ts index 502ed31845564..4a219794009bf 100644 --- a/x-pack/plugins/threat_intelligence/common/types/indicator.ts +++ b/x-pack/plugins/threat_intelligence/common/types/indicator.ts @@ -18,19 +18,23 @@ export enum RawIndicatorFieldId { UrlDomain = 'threat.indicator.url.domain', FileMd5 = 'threat.indicator.file.hash.md5', FileSha256 = 'threat.indicator.file.hash.sha256', + TimeStamp = '@timestamp', } export interface Indicator { + _id?: unknown; fields: Partial>; } export const generateMockIndicator = (): Indicator => ({ fields: { + '@timestamp': ['2022-01-01T01:01:01.000Z'], 'threat.indicator.type': ['ipv4-addr'], 'threat.indicator.ip': ['12.68.554.87'], 'threat.indicator.first_seen': ['2022-01-01T01:01:01.000Z'], - 'threat.feed.name': ['Abuse_CH'], + 'threat.feed.name': ['[Filebeat] AbuseCH Malware'], }, + _id: Math.random(), }); export const generateMockUrlIndicator = (): Indicator => { diff --git a/x-pack/plugins/threat_intelligence/cypress/integration/sources/indicators.spec.ts b/x-pack/plugins/threat_intelligence/cypress/integration/sources/indicators.spec.ts index 117a7da337fa4..0275ac3571688 100644 --- a/x-pack/plugins/threat_intelligence/cypress/integration/sources/indicators.spec.ts +++ b/x-pack/plugins/threat_intelligence/cypress/integration/sources/indicators.spec.ts @@ -19,6 +19,7 @@ import { TABLE_CONTROLS, INDICATOR_TYPE_CELL, EMPTY_STATE, + FIELD_SELECTOR, } from '../../screens/indicators'; import { login } from '../../tasks/login'; @@ -49,6 +50,8 @@ describe('Indicators page basics', () => { cy.get(FILTERS_GLOBAL_CONTAINER).should('exist'); cy.get(`${FILTERS_GLOBAL_CONTAINER} ${TIME_RANGE_PICKER}`).should('exist'); + + cy.get(`${FIELD_SELECTOR}`).should('exist'); }); it('should show the indicator flyout on ioc click', () => { @@ -111,4 +114,18 @@ describe('Indicator page search', () => { cy.get(EMPTY_STATE).should('exist').and('contain.text', 'No results'); }); }); + + it('should have the default selected field, then update when user selects', () => { + const threatFeedName = 'threat.feed.name'; + cy.get(`${FIELD_SELECTOR}`).should('have.value', threatFeedName); + + const threatIndicatorIp: string = 'threat.indicator.ip'; + + cy.get(`${FIELD_SELECTOR}`) + .should('exist') + .select(threatIndicatorIp) + .should('have.value', threatIndicatorIp); + + cy.get(`${FIELD_SELECTOR}`).should('have.value', threatIndicatorIp); + }); }); diff --git a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts index ccdf7c0fd4b0d..f5be07e1bbd56 100644 --- a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts +++ b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts @@ -32,3 +32,5 @@ export const EMPTY_STATE = '[data-test-subj="indicatorsTableEmptyState"]'; export const TABLE_CONTROLS = '[data-test-sub="dataGridControls"]'; export const INDICATOR_TYPE_CELL = '[data-gridcell-column-id="threat.indicator.type"]'; + +export const FIELD_SELECTOR = '[data-test-subj="tiIndicatorFieldSelectorDropdown"]'; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx new file mode 100644 index 0000000000000..c002343369988 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Observable } from 'rxjs'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import * as hook from '../../hooks/use_kibana'; + +export interface MockSearchServiceParams { + searchSubject?: Observable; + calculateSubject?: TimeRangeBounds; +} + +export const mockKibanaDataService = ({ + searchSubject, + calculateSubject, +}: MockSearchServiceParams) => { + const search = jest.fn().mockReturnValue(searchSubject); + const showError = jest.fn(); + const getUiSetting = jest.fn(); + const calculateBounds = jest.fn().mockReturnValue(calculateSubject); + + (hook as jest.Mocked).useKibana.mockReturnValue({ + services: { + data: { + search: { search, showError }, + query: { timefilter: { timefilter: { calculateBounds } } }, + }, + uiSettings: { get: getUiSetting }, + }, + } as any); + + return { + search, + showError, + getUiSetting, + calculateBounds, + }; +}; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_search_service.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_search_service.tsx deleted file mode 100644 index 2ac11ed4e55c0..0000000000000 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_search_service.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Observable } from 'rxjs'; -import * as hook from '../../hooks/use_kibana'; - -export const mockSearchService = (subject: Observable) => { - const search = jest.fn().mockReturnValue(subject); - const showError = jest.fn(); - const getUiSetting = jest.fn(); - - (hook as jest.Mocked).useKibana.mockReturnValue({ - services: { data: { search: { search, showError } }, uiSettings: { get: getUiSetting } }, - } as any); - - return { - search, - showError, - getUiSetting, - }; -}; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx index e398bb7ae1fdb..5fe52b890ca31 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment/moment'; import React, { FC } from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { coreMock } from '@kbn/core/public/mocks'; @@ -17,8 +18,6 @@ import { KibanaContextProvider } from '../../hooks/use_kibana'; import { Services, ThreatIntelligenceSecuritySolutionContext } from '../../types'; import { SecuritySolutionContext } from '../../containers/security_solution_context'; -const mockCoreStart = coreMock.createStart(); - export const localStorageMock = (): IStorage => { let store: Record = {}; @@ -46,11 +45,12 @@ export const createTiStorageMock = () => { }; }; -const data = dataPluginMock.createStartContract(); const { storage } = createTiStorageMock(); export const unifiedSearch = unifiedSearchPluginMock.createStartContract(); +const validDate: string = '1 Jan 2022 00:00:00 GMT'; +const data = dataPluginMock.createStartContract(); const dataServiceMock = { ...data, query: { @@ -72,6 +72,14 @@ const dataServiceMock = { }) ), }, + timefilter: { + timefilter: { + calculateBounds: jest.fn().mockImplementation(() => ({ + min: moment(validDate), + max: moment(validDate).add(1, 'days'), + })), + }, + }, }, search: { ...data.search, @@ -92,6 +100,12 @@ const dataServiceMock = { }, }; +const core = coreMock.createStart(); +const coreServiceMock = { + ...core, + uiSettings: { get: jest.fn().mockImplementation(mockUiSetting) }, +}; + const mockSecurityContext: ThreatIntelligenceSecuritySolutionContext = { getFiltersGlobalComponent: () => @@ -99,10 +113,8 @@ const mockSecurityContext: ThreatIntelligenceSecuritySolutionContext = {

, }; -mockCoreStart.uiSettings.get.mockImplementation(mockUiSetting); - const mockedServices = { - ...mockCoreStart, + ...coreServiceMock, data: dataServiceMock, storage, unifiedSearch, diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts new file mode 100644 index 0000000000000..1acf8d0213341 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { convertAggregationToChartSeries } from './barchart'; +import { Aggregation } from '../../modules/indicators/hooks/use_aggregated_indicators'; + +const aggregation1: Aggregation = { + events: { + buckets: [ + { + doc_count: 0, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 10, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH Malware', +}; +const aggregation2: Aggregation = { + events: { + buckets: [ + { + doc_count: 20, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 8, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH MalwareBazaar', +}; + +describe('barchart', () => { + describe('convertAggregationToChartSeries', () => { + it('should convert Aggregation[] to ChartSeries[]', () => { + expect(convertAggregationToChartSeries([aggregation1, aggregation2])).toEqual([ + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 10, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 20, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 8, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts new file mode 100644 index 0000000000000..93f6b4ce6fd62 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.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 { + Aggregation, + AggregationValue, + ChartSeries, +} from '../../modules/indicators/hooks/use_aggregated_indicators'; + +/** + * Converts data received from an Elastic search with date_histogram aggregation enabled to something usable in the "@elastic/chart" BarChart component + * @param aggregations An array of {@link Aggregation} objects to process + * @returns An array of {@link ChartSeries} directly usable in a BarChart component + */ +export const convertAggregationToChartSeries = (aggregations: Aggregation[]): ChartSeries[] => + aggregations.reduce( + (accumulated: ChartSeries[], current: Aggregation) => + accumulated.concat( + current.events.buckets.map((val: AggregationValue) => ({ + x: val.key_as_string, + y: val.doc_count, + g: current.key, + })) + ), + [] + ); diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/dates.test.ts b/x-pack/plugins/threat_intelligence/public/common/utils/dates.test.ts index b82ce5ada8f86..8fa9f15bac263 100644 --- a/x-pack/plugins/threat_intelligence/public/common/utils/dates.test.ts +++ b/x-pack/plugins/threat_intelligence/public/common/utils/dates.test.ts @@ -6,7 +6,13 @@ */ import moment from 'moment-timezone'; -import { dateFormatter } from './dates'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { + dateFormatter, + getDateDifferenceInDays, + barChartTimeAxisLabelFormatter, + calculateBarchartColumnTimeInterval, +} from './dates'; import { EMPTY_VALUE } from '../../../common/constants'; const mockValidStringDate = '1 Jan 2022 00:00:00 GMT'; @@ -32,13 +38,82 @@ describe('dates', () => { expect(dateFormatter(date, mockTimeZone, mockDateFormat)).toEqual('Jan 1st 22'); }); - it('should return EMPTY_VALUE for invalid string date', () => { + it(`should return ${EMPTY_VALUE} for invalid string date`, () => { expect(dateFormatter(mockInvalidStringDate, mockTimeZone)).toEqual(EMPTY_VALUE); }); - it('should return EMPTY_VALUE for invalid moment date', () => { + it(`should return ${EMPTY_VALUE} for invalid moment date`, () => { const date = moment(mockInvalidStringDate); expect(dateFormatter(date, mockTimeZone)).toEqual(EMPTY_VALUE); }); }); + + describe('getDaysDiff', () => { + it('should return correct number of days between two dates', () => { + const minDate: moment.Moment = moment(mockValidStringDate); + const maxDate: moment.Moment = moment(mockValidStringDate).add(4, 'days'); + + expect(getDateDifferenceInDays(minDate, maxDate)).toEqual(4); + }); + + it('should return 2 if dates are close to each other (less than one day apart)', () => { + const minDate: moment.Moment = moment(mockValidStringDate); + const maxDate: moment.Moment = moment(mockValidStringDate).add(12, 'hours'); + + expect(getDateDifferenceInDays(minDate, maxDate)).toEqual(2); + }); + + it('should handle maxDate older than minDate', () => { + const minDate: moment.Moment = moment(mockValidStringDate).add(4, 'days'); + const maxDate: moment.Moment = moment(mockValidStringDate); + + expect(getDateDifferenceInDays(minDate, maxDate)).toEqual(2); + }); + + it('should return 0 if dates are identical', () => { + const minDate: moment.Moment = moment(mockValidStringDate); + const maxDate: moment.Moment = moment(mockValidStringDate); + + expect(getDateDifferenceInDays(minDate, maxDate)).toEqual(0); + }); + }); + + describe('dateTimeFormatter', () => { + it('should return a string', () => { + const dateRange: TimeRangeBounds = { + min: moment(mockValidStringDate), + max: moment(mockValidStringDate).add(4, 'days'), + }; + + expect(typeof barChartTimeAxisLabelFormatter(dateRange)).toBe('function'); + }); + }); + + describe('calculateBarchartTimeInterval', () => { + it('should handle number dates', () => { + const from = moment(mockValidStringDate).valueOf(); + const to = moment(mockValidStringDate).add(1, 'days').valueOf(); + + const interval = calculateBarchartColumnTimeInterval(from, to); + expect(interval).toContain('ms'); + expect(parseInt(interval, 10) > 0).toBeTruthy(); + }); + + it('should handle moment dates', () => { + const from = moment(mockValidStringDate); + const to = moment(mockValidStringDate).add(1, 'days'); + + const interval = calculateBarchartColumnTimeInterval(from, to); + expect(interval).toContain('ms'); + expect(parseInt(interval, 10) > 0).toBeTruthy(); + }); + + it('should handle dateTo older than dateFrom', () => { + const from = moment(mockValidStringDate).add(1, 'days'); + const to = moment(mockValidStringDate); + + const interval = calculateBarchartColumnTimeInterval(from, to); + expect(parseInt(interval, 10) > 0).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/dates.tsx b/x-pack/plugins/threat_intelligence/public/common/utils/dates.tsx index 0465179cfc527..ef5fe20f97d15 100644 --- a/x-pack/plugins/threat_intelligence/public/common/utils/dates.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/utils/dates.tsx @@ -7,8 +7,21 @@ import moment from 'moment'; moment.suppressDeprecationWarnings = true; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { niceTimeFormatByDay, timeFormatter } from '@elastic/charts'; +import { TickFormatter } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs'; import { EMPTY_VALUE } from '../../../common/constants'; +export const FULL_DATE = 'MMMM Do YYYY @ HH:mm:ss'; +export const BARCHART_NUMBER_OF_COLUMNS = 16; + +/** + * Converts a string or moment date to the 'MMMM Do YYYY @ HH:mm:ss' format. + * @param date Date to be formatted + * @param timeZone Timezone used to format the date + * @param format Optional format used by moment to format the date + * @returns The formatted string or {@link EMPTY_VALUE} if the input date wasn't valid + */ export const dateFormatter = ( date: string | moment.Moment, timeZone: string, @@ -18,3 +31,50 @@ export const dateFormatter = ( typeof date === 'string' ? moment.tz(date, timeZone) : date.tz(timeZone); return momentDate.isValid() ? momentDate.format(format) : EMPTY_VALUE; }; + +/** + * Calculates the difference in days between two moment dates + * @param minDate Min (older) date + * @param maxDate Max (newer) date + * @returns The number of days between the two input dates (returns 2 if the difference is less than 1 to play nice with the {@link niceTimeFormatByDay} function + */ +export const getDateDifferenceInDays = (minDate: moment.Moment, maxDate: moment.Moment): number => { + const differenceInDays = maxDate.diff(minDate, 'days'); + + if (differenceInDays <= 1 && !minDate.isSame(maxDate)) { + return 2; // to return proper pattern from niceTimeFormatByDay + } + + return differenceInDays; +}; + +/** + * Nicely formats the label for a BartChart's time axis (uses {@link niceTimeFormatByDay} and {@link timeFormatter}from the '@elastic/charts' library) + * @param dateRange Min and max values for the time axis ({@link TimeRangeBounds}) + * @returns A function ({@link timeFormatter} from the '@elastic/charts' library) that returns a formatted label as a string + */ +export const barChartTimeAxisLabelFormatter = (dateRange: TimeRangeBounds): TickFormatter => { + const diff = getDateDifferenceInDays( + dateRange.min as moment.Moment, + dateRange.max as moment.Moment + ); + const format = niceTimeFormatByDay(diff); + return timeFormatter(format); +}; + +/** + * Calculates the time interval in ms for a specific number of columns + * @param dateFrom Min (older) date for the barchart + * @param dateTo Max (newer) date for the barchart + * @param numberOfColumns Desired number of columns (defaulted to {@link BARCHART_NUMBER_OF_COLUMNS}) + * @returns The interval in ms for a column (for example '100000ms') + */ +export const calculateBarchartColumnTimeInterval = ( + dateFrom: number | moment.Moment, + dateTo: number | moment.Moment, + numberOfColumns = BARCHART_NUMBER_OF_COLUMNS +): string => { + const from: number = moment.isMoment(dateFrom) ? dateFrom.valueOf() : dateFrom; + const to: number = moment.isMoment(dateTo) ? dateTo.valueOf() : dateTo; + return `${Math.floor(moment(to).diff(moment(from)) / numberOfColumns)}ms`; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap new file mode 100644 index 0000000000000..dad1f1cb46ee4 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render barchart 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+
+
+
+
+
+
+ , + "container":
+
+
+
+
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx new file mode 100644 index 0000000000000..1b127ad56b11b --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import React from 'react'; +import { Story } from '@storybook/react'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { ChartSeries } from '../../hooks/use_aggregated_indicators'; +import { IndicatorsBarChart } from './indicators_barchart'; + +const mockIndicators: ChartSeries[] = [ + { + x: '1 Jan 2022 00:00:00 GMT', + y: 2, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 00:00:00 GMT', + y: 10, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 25, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 18:00:00 GMT', + y: 15, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, +]; +const validDate: string = '1 Jan 2022 00:00:00 GMT'; +const numberOfDays = 1; +const mockDateRange: TimeRangeBounds = { + min: moment(validDate), + max: moment(validDate).add(numberOfDays, 'days'), +}; +const mockHeight = '500px'; + +export default { + component: IndicatorsBarChart, + title: 'IndicatorsBarChart', +}; + +export const Default: Story = () => ( + +); + +export const NoData: Story = () => ( + +); + +export const CustomHeight: Story = () => ( + +); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx new file mode 100644 index 0000000000000..5872cc5c83fde --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment-timezone'; +import React from 'react'; +import { render } from '@testing-library/react'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; +import { ChartSeries } from '../../hooks/use_aggregated_indicators'; +import { IndicatorsBarChart } from './indicators_barchart'; + +moment.suppressDeprecationWarnings = true; +moment.tz.setDefault('UTC'); + +const mockIndicators: ChartSeries[] = [ + { + x: '1 Jan 2022 00:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 00:00:00 GMT', + y: 10, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 25, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 18:00:00 GMT', + y: 15, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, +]; +const validDate: string = '1 Jan 2022 00:00:00 GMT'; +const mockDateRange: TimeRangeBounds = { + min: moment(validDate), + max: moment(validDate).add(1, 'days'), +}; + +describe('', () => { + it('should render barchart', () => { + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx new file mode 100644 index 0000000000000..466a262f753e7 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { VFC } from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { EuiThemeProvider } from '@elastic/eui'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { barChartTimeAxisLabelFormatter } from '../../../../common/utils/dates'; +import { ChartSeries } from '../../hooks/use_aggregated_indicators'; + +const ID = 'tiIndicator'; +const DEFAULT_CHART_HEIGHT = '200px'; +const DEFAULT_CHART_WIDTH = '100%'; + +export interface IndicatorsBarChartProps { + indicators: ChartSeries[]; + dateRange: TimeRangeBounds; + height?: string; +} + +export const IndicatorsBarChart: VFC = ({ + indicators, + dateRange, + height = DEFAULT_CHART_HEIGHT, +}) => { + return ( + + + + + + + + + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap new file mode 100644 index 0000000000000..de4d6c198961f --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap @@ -0,0 +1,220 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render barchart and field selector dropdown 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+
+

+ Trend +

+
+
+
+ +
+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+ , + "container":
+
+
+

+ Trend +

+
+
+
+ +
+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx new file mode 100644 index 0000000000000..cda6671d03fda --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import React from 'react'; +import { of } from 'rxjs'; +import { Story } from '@storybook/react'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { CoreStart } from '@kbn/core/public'; +import { TimeRange } from '@kbn/es-query'; +import { Aggregation, AGGREGATION_NAME } from '../../hooks/use_aggregated_indicators'; +import { DEFAULT_TIME_RANGE } from '../../hooks/use_filters/utils'; +import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; + +export default { + component: IndicatorsBarChartWrapper, + title: 'IndicatorsBarChartWrapper', +}; + +const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; +const mockIndexPatterns: DataView[] = [ + { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], + } as DataView, +]; + +const validDate: string = '1 Jan 2022 00:00:00 GMT'; +const numberOfDays: number = 1; +const aggregation1: Aggregation = { + events: { + buckets: [ + { + doc_count: 0, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 10, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH Malware', +}; +const aggregation2: Aggregation = { + events: { + buckets: [ + { + doc_count: 20, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 8, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH MalwareBazaar', +}; +const KibanaReactContext = createKibanaReactContext({ + data: { + search: { + search: () => + of({ + rawResponse: { + aggregations: { + [AGGREGATION_NAME]: { + buckets: [aggregation1, aggregation2], + }, + }, + }, + }), + }, + query: { + timefilter: { + timefilter: { + calculateBounds: () => ({ + min: moment(validDate), + max: moment(validDate).add(numberOfDays, 'days'), + }), + }, + }, + }, + }, + uiSettings: { get: () => {} }, +} as unknown as Partial); + +export const Default: Story = () => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx new file mode 100644 index 0000000000000..39afea8a11a0e --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { TimeRange } from '@kbn/es-query'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; +import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; +import { DEFAULT_TIME_RANGE } from '../../hooks/use_filters/utils'; + +const mockIndexPatterns: DataView[] = [ + { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], + } as DataView, +]; +const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; + +describe('', () => { + it('should render barchart and field selector dropdown', () => { + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx new file mode 100644 index 0000000000000..0988e50dc0007 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { TimeRange } from '@kbn/es-query'; +import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; +import { useAggregatedIndicators } from '../../hooks/use_aggregated_indicators'; +import { IndicatorsFieldSelector } from '../indicators_field_selector/indicators_field_selector'; +import { IndicatorsBarChart } from '../indicators_barchart/indicators_barchart'; + +const DEFAULT_FIELD = RawIndicatorFieldId.Feed; + +export interface IndicatorsBarChartWrapperProps { + timeRange?: TimeRange; + indexPatterns: DataView[]; +} + +export const IndicatorsBarChartWrapper = memo( + ({ timeRange, indexPatterns }) => { + const { dateRange, indicators, onFieldChange } = useAggregatedIndicators({ timeRange }); + + return ( + <> + + + +

+ +

+
+
+ + + +
+ {timeRange ? : <>} + + ); + } +); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap new file mode 100644 index 0000000000000..735a1c34718c0 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap @@ -0,0 +1,269 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should display all unique fields from a DataView[] 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+ +
+ +
+ + +
+
+
+
+ , + "container":
+
+ +
+ +
+ + +
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should handle empty array of indexPatterns 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+ +
+ +
+ + +
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx new file mode 100644 index 0000000000000..f7bb3300334a5 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Story } from '@storybook/react'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; +import { IndicatorsFieldSelector } from './indicators_field_selector'; + +const mockIndexPatterns: DataView[] = [ + { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], + } as DataView, +]; + +export default { + component: IndicatorsFieldSelector, + title: 'IndicatorsFieldSelector', +}; + +export const Default: Story = () => { + return ( + console.log(value)} + /> + ); +}; + +export const WithDefaultValue: Story = () => { + return ( + console.log(value)} + defaultStackByValue={RawIndicatorFieldId.LastSeen} + /> + ); +}; + +export const NoData: Story = () => { + return ( + console.log(value)} + /> + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx new file mode 100644 index 0000000000000..21a0f56dfb36a --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; +import { DROPDOWN_TEST_ID, IndicatorsFieldSelector } from './indicators_field_selector'; + +const mockIndexPatterns: DataView[] = [ + { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], + } as DataView, +]; + +describe('', () => { + it('should handle empty array of indexPatterns', () => { + const component = render( + + console.log(value)} + /> + + ); + + expect(component).toMatchSnapshot(); + }); + + it('should display all unique fields from a DataView[]', () => { + const component = render( + + console.log(value)} + /> + + ); + + expect(component).toMatchSnapshot(); + + const dropdownOptions: string = component.getByTestId(DROPDOWN_TEST_ID).innerHTML; + const optionsCount: number = (dropdownOptions.match(/