diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy deleted file mode 100644 index f1095f8035b6c..0000000000000 --- a/.ci/end2end.groovy +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env groovy - -library identifier: 'apm@current', -retriever: modernSCM( - [$class: 'GitSCMSource', - credentialsId: 'f94e9298-83ae-417e-ba91-85c279771570', - id: '37cf2c00-2cc7-482e-8c62-7bbffef475e2', - remote: 'git@github.com:elastic/apm-pipeline-library.git']) - -pipeline { - agent { label 'linux && immutable' } - environment { - BASE_DIR = 'src/github.com/elastic/kibana' - HOME = "${env.WORKSPACE}" - E2E_DIR = 'x-pack/plugins/apm/e2e' - PIPELINE_LOG_LEVEL = 'INFO' - KBN_OPTIMIZER_THEMES = 'v7light' - } - options { - timeout(time: 1, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10', daysToKeepStr: '30')) - timestamps() - ansiColor('xterm') - disableResume() - durabilityHint('PERFORMANCE_OPTIMIZED') - } - triggers { - issueCommentTrigger('(?i)(retest|.*jenkins\\W+run\\W+(?:the\\W+)?e2e?.*)') - } - parameters { - booleanParam(name: 'FORCE', defaultValue: false, description: 'Whether to force the run.') - } - stages { - stage('Checkout') { - options { skipDefaultCheckout() } - steps { - deleteDir() - gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: false, - shallow: false, reference: "/var/lib/jenkins/.git-references/kibana.git") - - // Filter when to run based on the below reasons: - // - On a PRs when: - // - There are changes related to the APM UI project - // - only when the owners of those changes are members of the given GitHub teams - // - On merges to branches when: - // - There are changes related to the APM UI project - // - FORCE parameter is set to true. - script { - def apm_updated = false - dir("${BASE_DIR}"){ - apm_updated = isGitRegionMatch(patterns: [ "^x-pack/plugins/apm/.*" ]) - } - if (isPR()) { - def isMember = isMemberOf(user: env.CHANGE_AUTHOR, team: ['apm-ui', 'uptime']) - setEnvVar('RUN_APM_E2E', params.FORCE || (apm_updated && isMember)) - } else { - setEnvVar('RUN_APM_E2E', params.FORCE || apm_updated) - } - } - } - } - stage('Prepare Kibana') { - options { skipDefaultCheckout() } - when { expression { return env.RUN_APM_E2E != "false" } } - environment { - JENKINS_NODE_COOKIE = 'dontKillMe' - } - steps { - notifyStatus('Preparing kibana', 'PENDING') - dir("${BASE_DIR}"){ - sh "${E2E_DIR}/ci/prepare-kibana.sh" - } - } - post { - unsuccessful { - notifyStatus('Kibana warm up failed', 'FAILURE') - } - } - } - stage('Smoke Tests'){ - options { skipDefaultCheckout() } - when { expression { return env.RUN_APM_E2E != "false" } } - steps{ - notifyTestStatus('Running smoke tests', 'PENDING') - dir("${BASE_DIR}"){ - sh "${E2E_DIR}/ci/run-e2e.sh" - } - } - post { - always { - dir("${BASE_DIR}/${E2E_DIR}"){ - archiveArtifacts(allowEmptyArchive: false, artifacts: 'cypress/screenshots/**,cypress/videos/**,cypress/test-results/*e2e-tests.xml') - junit(allowEmptyResults: true, testResults: 'cypress/test-results/*e2e-tests.xml') - dir('tmp/apm-integration-testing'){ - sh 'docker-compose logs > apm-its-docker.log || true' - sh 'docker-compose down -v || true' - archiveArtifacts(allowEmptyArchive: true, artifacts: 'apm-its-docker.log') - } - archiveArtifacts(allowEmptyArchive: true, artifacts: 'tmp/*.log') - } - } - unsuccessful { - notifyTestStatus('Test failures', 'FAILURE') - } - success { - notifyTestStatus('Tests passed', 'SUCCESS') - } - } - } - } - post { - always { - dir("${BASE_DIR}"){ - archiveArtifacts(allowEmptyArchive: true, artifacts: "${E2E_DIR}/kibana.log") - } - } - cleanup { - notifyBuildResult(prComment: false, analyzeFlakey: false, shouldNotify: false) - } - } -} - -def notifyStatus(String description, String status) { - withGithubStatus.notify('end2end-for-apm-ui', description, status, getBlueoceanTabURL('pipeline')) -} - -def notifyTestStatus(String description, String status) { - withGithubStatus.notify('end2end-for-apm-ui', description, status, getBlueoceanTabURL('tests')) -} diff --git a/.eslintrc.js b/.eslintrc.js index 2c55dd2528ef1..06fd805a02aa7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1567,7 +1567,6 @@ module.exports = { { files: [ 'src/plugins/security_oss/**/*.{js,mjs,ts,tsx}', - 'src/plugins/spaces_oss/**/*.{js,mjs,ts,tsx}', 'src/plugins/interactive_setup/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/encrypted_saved_objects/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security/**/*.{js,mjs,ts,tsx}', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b47c3b09cce30..a457ae426d3e0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,11 +29,12 @@ /src/plugins/vis_type_timelion/ @elastic/kibana-app /src/plugins/vis_type_timeseries/ @elastic/kibana-app /src/plugins/vis_type_vega/ @elastic/kibana-app -/src/plugins/vis_type_vislib/ @elastic/kibana-app -/src/plugins/vis_type_xy/ @elastic/kibana-app -/src/plugins/vis_type_pie/ @elastic/kibana-app +/src/plugins/vis_types/vislib/ @elastic/kibana-app +/src/plugins/vis_types/xy/ @elastic/kibana-app +/src/plugins/vis_types/pie/ @elastic/kibana-app /src/plugins/visualize/ @elastic/kibana-app /src/plugins/visualizations/ @elastic/kibana-app +/src/plugins/chart_expressions/expression_tagcloud/ @elastic/kibana-app /src/plugins/url_forwarding/ @elastic/kibana-app /packages/kbn-tinymath/ @elastic/kibana-app @@ -144,7 +145,6 @@ /x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation /x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation #CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation -#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation # Machine Learning /x-pack/plugins/ml/ @elastic/ml-ui @@ -280,7 +280,6 @@ # Security /src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core /src/plugins/security_oss/ @elastic/kibana-security -/src/plugins/spaces_oss/ @elastic/kibana-security /src/plugins/interactive_setup/ @elastic/kibana-security /test/security_functional/ @elastic/kibana-security /x-pack/plugins/spaces/ @elastic/kibana-security diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml index f2359846504bf..8070b07a829d2 100644 --- a/.github/workflows/project-assigner.yml +++ b/.github/workflows/project-assigner.yml @@ -19,6 +19,7 @@ jobs: {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Input Controls", "projectNumber": 72, "columnName": "Inbox"}, - {"label": "Team:Security", "projectNumber": 320, "columnName": "Awaiting triage", "projectScope": "org"} + {"label": "Team:Security", "projectNumber": 320, "columnName": "Awaiting triage", "projectScope": "org"}, + {"label": "Team:Operations", "projectNumber": 314, "columnName": "Triage", "projectScope": "org"} ] ghToken: ${{ secrets.PROJECT_ASSIGNER_TOKEN }} diff --git a/.i18nrc.json b/.i18nrc.json index d19226e6b6f8c..3301cd04ad06c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -25,6 +25,7 @@ "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", + "expressionTagcloud": "src/plugins/chart_expressions/expression_tagcloud", "inputControl": "src/plugins/input_control_vis", "inspector": "src/plugins/inspector", "inspectorViews": "src/legacy/core_plugins/inspector_views", @@ -63,9 +64,9 @@ "visTypeTagCloud": "src/plugins/vis_type_tagcloud", "visTypeTimeseries": "src/plugins/vis_type_timeseries", "visTypeVega": "src/plugins/vis_type_vega", - "visTypeVislib": "src/plugins/vis_type_vislib", - "visTypeXy": "src/plugins/vis_type_xy", - "visTypePie": "src/plugins/vis_type_pie", + "visTypeVislib": "src/plugins/vis_types/vislib", + "visTypeXy": "src/plugins/vis_types/xy", + "visTypePie": "src/plugins/vis_types/pie", "visualizations": "src/plugins/visualizations", "visualize": "src/plugins/visualize", "apmOss": "src/plugins/apm_oss", diff --git a/api_docs/dashboard_mode.json b/api_docs/dashboard_mode.json deleted file mode 100644 index eb2927e351578..0000000000000 --- a/api_docs/dashboard_mode.json +++ /dev/null @@ -1,238 +0,0 @@ -{ - "id": "dashboardMode", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [ - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin", - "type": "Class", - "tags": [], - "label": "DashboardModeServerPlugin", - "description": [], - "signature": [ - { - "pluginId": "dashboardMode", - "scope": "server", - "docId": "kibDashboardModePluginApi", - "section": "def-server.DashboardModeServerPlugin", - "text": "DashboardModeServerPlugin" - }, - " implements ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.Plugin", - "text": "Plugin" - }, - "" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], - "signature": [ - "any" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "initializerContext", - "description": [], - "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.PluginInitializerContext", - "text": "PluginInitializerContext" - }, - "" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.setup", - "type": "Function", - "tags": [], - "label": "setup", - "description": [], - "signature": [ - "(core: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreSetup", - "text": "CoreSetup" - }, - ", { security }: DashboardModeServerSetupDependencies) => void" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.setup.$1", - "type": "Object", - "tags": [], - "label": "core", - "description": [], - "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreSetup", - "text": "CoreSetup" - }, - "" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.setup.$2", - "type": "Object", - "tags": [], - "label": "{ security }", - "description": [], - "signature": [ - "DashboardModeServerSetupDependencies" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.start", - "type": "Function", - "tags": [], - "label": "start", - "description": [], - "signature": [ - "(core: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreStart", - "text": "CoreStart" - }, - ") => void" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.start.$1", - "type": "Object", - "tags": [], - "label": "core", - "description": [], - "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreStart", - "text": "CoreStart" - } - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "dashboardMode", - "id": "def-server.DashboardModeServerPlugin.stop", - "type": "Function", - "tags": [], - "label": "stop", - "description": [], - "signature": [ - "() => void" - ], - "path": "x-pack/plugins/dashboard_mode/server/plugin.ts", - "deprecated": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - } - ], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [ - { - "parentPluginId": "dashboardMode", - "id": "def-common.UI_SETTINGS", - "type": "Object", - "tags": [], - "label": "UI_SETTINGS", - "description": [], - "path": "x-pack/plugins/dashboard_mode/common/constants.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboardMode", - "id": "def-common.UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES", - "type": "string", - "tags": [], - "label": "CONFIG_DASHBOARD_ONLY_MODE_ROLES", - "description": [], - "path": "x-pack/plugins/dashboard_mode/common/constants.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ] - } -} \ No newline at end of file diff --git a/api_docs/dashboard_mode.mdx b/api_docs/dashboard_mode.mdx deleted file mode 100644 index 606532e85fe78..0000000000000 --- a/api_docs/dashboard_mode.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: kibDashboardModePluginApi -slug: /kibana-dev-docs/dashboardModePluginApi -title: dashboardMode -image: https://source.unsplash.com/400x175/?github -summary: API docs for the dashboardMode plugin -date: 2020-11-16 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardMode'] -warning: This 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 dashboardModeObj from './dashboard_mode.json'; - - - - - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 11 | 0 | 11 | 0 | - -## Server - -### Classes - - -## Common - -### Objects - - diff --git a/api_docs/spaces_oss.json b/api_docs/spaces_oss.json deleted file mode 100644 index cd59756b548b6..0000000000000 --- a/api_docs/spaces_oss.json +++ /dev/null @@ -1,1320 +0,0 @@ -{ - "id": "spacesOss", - "client": { - "classes": [], - "functions": [], - "interfaces": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.LegacyUrlConflictProps", - "type": "Interface", - "tags": [], - "label": "LegacyUrlConflictProps", - "description": [ - "\nProperties for the LegacyUrlConflict component." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.LegacyUrlConflictProps.objectNoun", - "type": "string", - "tags": [], - "label": "objectNoun", - "description": [ - "\nThe string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different\n**object**_.\n\nDefault value is 'object'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.LegacyUrlConflictProps.currentObjectId", - "type": "string", - "tags": [], - "label": "currentObjectId", - "description": [ - "\nThe ID of the object that is currently shown on the page." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.LegacyUrlConflictProps.otherObjectId", - "type": "string", - "tags": [], - "label": "otherObjectId", - "description": [ - "\nThe ID of the other object that the legacy URL alias points to." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.LegacyUrlConflictProps.otherObjectPath", - "type": "string", - "tags": [], - "label": "otherObjectPath", - "description": [ - "\nThe path to use for the new URL, optionally including `search` and/or `hash` URL components." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps", - "type": "Interface", - "tags": [], - "label": "ShareToSpaceFlyoutProps", - "description": [ - "\nProperties for the ShareToSpaceFlyout." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.savedObjectTarget", - "type": "Object", - "tags": [], - "label": "savedObjectTarget", - "description": [ - "\nThe object to render the flyout for." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.ShareToSpaceSavedObjectTarget", - "text": "ShareToSpaceSavedObjectTarget" - } - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.flyoutIcon", - "type": "string", - "tags": [], - "label": "flyoutIcon", - "description": [ - "\nThe EUI icon that is rendered in the flyout's title.\n\nDefault is 'share'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.flyoutTitle", - "type": "string", - "tags": [], - "label": "flyoutTitle", - "description": [ - "\nThe string that is rendered in the flyout's title.\n\nDefault is 'Edit spaces for object'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.enableCreateCopyCallout", - "type": "CompoundType", - "tags": [], - "label": "enableCreateCopyCallout", - "description": [ - "\nWhen enabled, if the object is not yet shared to multiple spaces, a callout will be displayed that suggests the user might want to\ncreate a copy instead.\n\nDefault value is false." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.enableCreateNewSpaceLink", - "type": "CompoundType", - "tags": [], - "label": "enableCreateNewSpaceLink", - "description": [ - "\nWhen enabled, if no other spaces exist _and_ the user has the appropriate privileges, a sentence will be displayed that suggests the\nuser might want to create a space.\n\nDefault value is false." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.behaviorContext", - "type": "CompoundType", - "tags": [], - "label": "behaviorContext", - "description": [ - "\nWhen set to 'within-space' (default), the flyout behaves like it is running on a page within the active space, and it will prevent the\nuser from removing the object from the active space.\n\nConversely, when set to 'outside-space', the flyout behaves like it is running on a page outside of any space, so it will allow the\nuser to remove the object from the active space." - ], - "signature": [ - "\"within-space\" | \"outside-space\" | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.changeSpacesHandler", - "type": "Function", - "tags": [], - "label": "changeSpacesHandler", - "description": [ - "\nOptional handler that is called when the user has saved changes and there are spaces to be added to and/or removed from the object and\nits relatives. If this is not defined, a default handler will be used that calls `/api/spaces/_update_objects_spaces` and displays a\ntoast indicating what occurred." - ], - "signature": [ - "((objects: { type: string; id: string; }[], spacesToAdd: string[], spacesToRemove: string[]) => Promise) | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.changeSpacesHandler.$1", - "type": "Array", - "tags": [], - "label": "objects", - "description": [], - "signature": [ - "{ type: string; id: string; }[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.changeSpacesHandler.$2", - "type": "Array", - "tags": [], - "label": "spacesToAdd", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.changeSpacesHandler.$3", - "type": "Array", - "tags": [], - "label": "spacesToRemove", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.onUpdate", - "type": "Function", - "tags": [], - "label": "onUpdate", - "description": [ - "\nOptional callback when the target object and its relatives are updated." - ], - "signature": [ - "((updatedObjects: { type: string; id: string; }[]) => void) | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.onUpdate.$1", - "type": "Array", - "tags": [], - "label": "updatedObjects", - "description": [], - "signature": [ - "{ type: string; id: string; }[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceFlyoutProps.onClose", - "type": "Function", - "tags": [], - "label": "onClose", - "description": [ - "\nOptional callback when the flyout is closed." - ], - "signature": [ - "(() => void) | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget", - "type": "Interface", - "tags": [], - "label": "ShareToSpaceSavedObjectTarget", - "description": [ - "\nDescribes the target saved object during a share operation." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.type", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "\nThe object's type." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "\nThe object's ID." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.namespaces", - "type": "Array", - "tags": [], - "label": "namespaces", - "description": [ - "\nThe namespaces that the object currently exists in." - ], - "signature": [ - "string[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.icon", - "type": "string", - "tags": [], - "label": "icon", - "description": [ - "\nThe EUI icon that is rendered in the flyout's subtitle.\n\nDefault is 'empty'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.title", - "type": "string", - "tags": [], - "label": "title", - "description": [ - "\nThe string that is rendered in the flyout's subtitle.\n\nDefault is `${type} [id=${id}]`." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.ShareToSpaceSavedObjectTarget.noun", - "type": "string", - "tags": [], - "label": "noun", - "description": [ - "\nThe string that is used to describe the object in several places, e.g., _Make **object** available in selected spaces only_.\n\nDefault value is 'object'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps", - "type": "Interface", - "tags": [], - "label": "SpaceAvatarProps", - "description": [ - "\nProperties for the SpaceAvatar component." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps.space", - "type": "Object", - "tags": [], - "label": "space", - "description": [ - "The space to represent with an avatar." - ], - "signature": [ - "{ id?: string | undefined; name?: string | undefined; description?: string | undefined; color?: string | undefined; initials?: string | undefined; imageUrl?: string | undefined; disabledFeatures?: string[] | undefined; _reserved?: boolean | undefined; }" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps.size", - "type": "CompoundType", - "tags": [], - "label": "size", - "description": [ - "The size of the avatar." - ], - "signature": [ - "\"m\" | \"s\" | \"l\" | \"xl\" | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps.className", - "type": "string", - "tags": [], - "label": "className", - "description": [ - "Optional CSS class(es) to apply." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps.announceSpaceName", - "type": "CompoundType", - "tags": [], - "label": "announceSpaceName", - "description": [ - "\nWhen enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers.\n\nDefault value is true." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceAvatarProps.isDisabled", - "type": "CompoundType", - "tags": [], - "label": "isDisabled", - "description": [ - "\nWhether or not to render the avatar in a disabled state.\n\nDefault value is false." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceListProps", - "type": "Interface", - "tags": [], - "label": "SpaceListProps", - "description": [ - "\nProperties for the SpaceList component." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceListProps.namespaces", - "type": "Array", - "tags": [], - "label": "namespaces", - "description": [ - "\nThe namespaces of a saved object to render into a corresponding list of spaces." - ], - "signature": [ - "string[]" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceListProps.displayLimit", - "type": "number", - "tags": [], - "label": "displayLimit", - "description": [ - "\nOptional limit to the number of spaces that can be displayed in the list. If the number of spaces exceeds this limit, they will be\nhidden behind a \"show more\" button. Set to 0 to disable.\n\nDefault value is 5." - ], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpaceListProps.behaviorContext", - "type": "CompoundType", - "tags": [], - "label": "behaviorContext", - "description": [ - "\nWhen set to 'within-space' (default), the space list behaves like it is running on a page within the active space, and it will omit the\nactive space (e.g., it displays a list of all the _other_ spaces that an object is shared to).\n\nConversely, when set to 'outside-space', the space list behaves like it is running on a page outside of any space, so it will not omit\nthe active space." - ], - "signature": [ - "\"within-space\" | \"outside-space\" | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApi", - "type": "Interface", - "tags": [], - "label": "SpacesApi", - "description": [ - "\nClient-side Spaces API." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApi.getActiveSpace$", - "type": "Function", - "tags": [], - "label": "getActiveSpace$", - "description": [ - "\nObservable representing the currently active space.\nThe details of the space can change without a full page reload (such as display name, color, etc.)" - ], - "signature": [ - "() => ", - "Observable", - "<", - { - "pluginId": "spacesOss", - "scope": "common", - "docId": "kibSpacesOssPluginApi", - "section": "def-common.Space", - "text": "Space" - }, - ">" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApi.getActiveSpace", - "type": "Function", - "tags": [], - "label": "getActiveSpace", - "description": [ - "\nRetrieve the currently active space." - ], - "signature": [ - "() => Promise<", - { - "pluginId": "spacesOss", - "scope": "common", - "docId": "kibSpacesOssPluginApi", - "section": "def-common.Space", - "text": "Space" - }, - ">" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApi.ui", - "type": "Object", - "tags": [], - "label": "ui", - "description": [ - "\nUI components and services to add spaces capabilities to an application." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesApiUi", - "text": "SpacesApiUi" - } - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUi", - "type": "Interface", - "tags": [], - "label": "SpacesApiUi", - "description": [ - "\nUI components and services to add spaces capabilities to an application." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUi.components", - "type": "Object", - "tags": [], - "label": "components", - "description": [ - "\nLazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesApiUiComponent", - "text": "SpacesApiUiComponent" - } - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUi.redirectLegacyUrl", - "type": "Function", - "tags": [], - "label": "redirectLegacyUrl", - "description": [ - "\nRedirect the user from a legacy URL to a new URL. This needs to be used if a call to `SavedObjectsClient.resolve()` results in an\n`\"aliasMatch\"` outcome, which indicates that the user has loaded the page using a legacy URL. Calling this function will trigger a\nclient-side redirect to the new URL, and it will display a toast to the user.\n\nConsumers need to determine the local path for the new URL on their own, based on the object ID that was used to call\n`SavedObjectsClient.resolve()` (old ID) and the object ID in the result (new ID). For example...\n\nThe old object ID is `workpad-123` and the new object ID is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.\n\nFull legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`\n\nNew URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`\n\nThe protocol, hostname, port, base path, and app path are automatically included.\n" - ], - "signature": [ - "(path: string, objectNoun?: string | undefined) => Promise" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUi.redirectLegacyUrl.$1", - "type": "string", - "tags": [], - "label": "path", - "description": [ - "The path to use for the new URL, optionally including `search` and/or `hash` URL components." - ], - "signature": [ - "string" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUi.redirectLegacyUrl.$2", - "type": "string", - "tags": [], - "label": "objectNoun", - "description": [ - "The string that is used to describe the object in the toast, e.g., _The **object** you're looking for has a new\nlocation_. Default value is 'object'." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent", - "type": "Interface", - "tags": [], - "label": "SpacesApiUiComponent", - "description": [ - "\nReact UI components to be used to display the Spaces feature in any application." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent.getSpacesContextProvider", - "type": "Function", - "tags": [], - "label": "getSpacesContextProvider", - "description": [ - "\nProvides a context that is required to render some Spaces components." - ], - "signature": [ - "(props: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesContextProps", - "text": "SpacesContextProps" - }, - ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent.getShareToSpaceFlyout", - "type": "Function", - "tags": [], - "label": "getShareToSpaceFlyout", - "description": [ - "\nDisplays a flyout to edit the spaces that an object is shared to.\n\nNote: must be rendered inside of a SpacesContext." - ], - "signature": [ - "(props: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.ShareToSpaceFlyoutProps", - "text": "ShareToSpaceFlyoutProps" - }, - ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent.getSpaceList", - "type": "Function", - "tags": [], - "label": "getSpaceList", - "description": [ - "\nDisplays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for\nany number of spaces that the user is not authorized to see) by default. If more than five named spaces would be displayed, the extras\n(along with the unauthorized spaces indicator, if present) are hidden behind a button. If '*' (aka \"All spaces\") is present, it\nsupersedes all of the above and just displays a single badge without a button.\n\nNote: must be rendered inside of a SpacesContext." - ], - "signature": [ - "(props: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpaceListProps", - "text": "SpaceListProps" - }, - ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent.getLegacyUrlConflict", - "type": "Function", - "tags": [], - "label": "getLegacyUrlConflict", - "description": [ - "\nDisplays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `\"conflict\"` outcome, which\nindicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a\ndifferent object (B).\n\nIn this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining\nthat there is a conflict, and it includes a button that will redirect the user to object B when clicked.\n\nConsumers need to determine the local path for the new URL on their own, based on the object ID that was used to call\n`SavedObjectsClient.resolve()` (A) and the `aliasTargetId` value in the response (B). For example...\n\nA is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.\n\nFull legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`\n\nNew URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`" - ], - "signature": [ - "(props: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.LegacyUrlConflictProps", - "text": "LegacyUrlConflictProps" - }, - ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesApiUiComponent.getSpaceAvatar", - "type": "Function", - "tags": [], - "label": "getSpaceAvatar", - "description": [ - "\nDisplays an avatar for the given space." - ], - "signature": [ - "(props: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpaceAvatarProps", - "text": "SpaceAvatarProps" - }, - ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesAvailableStartContract", - "type": "Interface", - "tags": [], - "label": "SpacesAvailableStartContract", - "description": [ - "\nOSS Spaces plugin start contract when the Spaces feature is enabled." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesAvailableStartContract", - "text": "SpacesAvailableStartContract" - }, - " extends ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesApi", - "text": "SpacesApi" - } - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesAvailableStartContract.isSpacesAvailable", - "type": "boolean", - "tags": [], - "label": "isSpacesAvailable", - "description": [ - "Indicates if the Spaces feature is enabled." - ], - "signature": [ - "true" - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesContextProps", - "type": "Interface", - "tags": [], - "label": "SpacesContextProps", - "description": [ - "\nProperties for the SpacesContext." - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesContextProps.feature", - "type": "string", - "tags": [], - "label": "feature", - "description": [ - "\nIf a feature is specified, all Spaces components will treat it appropriately if the feature is disabled in a given Space." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesUnavailableStartContract", - "type": "Interface", - "tags": [ - "deprecated" - ], - "label": "SpacesUnavailableStartContract", - "description": [ - "\nOSS Spaces plugin start contract when the Spaces feature is disabled." - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": true, - "removeBy": "8.0", - "references": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesUnavailableStartContract.isSpacesAvailable", - "type": "boolean", - "tags": [], - "label": "isSpacesAvailable", - "description": [ - "Indicates if the Spaces feature is enabled." - ], - "signature": [ - "false" - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "enums": [], - "misc": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.LazyComponentFn", - "type": "Type", - "tags": [], - "label": "LazyComponentFn", - "description": [ - "\nFunction that returns a promise for a lazy-loadable component." - ], - "signature": [ - "(props: T) => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.props", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/spaces_oss/public/api.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "objects": [], - "setup": { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesOssPluginSetup", - "type": "Interface", - "tags": [], - "label": "SpacesOssPluginSetup", - "description": [ - "\nOSS Spaces plugin setup contract." - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesOssPluginSetup.registerSpacesApi", - "type": "Function", - "tags": [ - "private" - ], - "label": "registerSpacesApi", - "description": [ - "\nRegister a provider for the Spaces API.\n\nOnly one provider can be registered, subsequent calls to this method will fail.\n" - ], - "signature": [ - "(provider: ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesApi", - "text": "SpacesApi" - }, - ") => void" - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesOssPluginSetup.registerSpacesApi.$1", - "type": "Object", - "tags": [], - "label": "provider", - "description": [ - "the API provider." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesApi", - "text": "SpacesApi" - } - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "lifecycle": "setup", - "initialIsOpen": true - }, - "start": { - "parentPluginId": "spacesOss", - "id": "def-public.SpacesOssPluginStart", - "type": "Type", - "tags": [], - "label": "SpacesOssPluginStart", - "description": [ - "\nOSS Spaces plugin start contract." - ], - "signature": [ - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesAvailableStartContract", - "text": "SpacesAvailableStartContract" - }, - " | ", - { - "pluginId": "spacesOss", - "scope": "public", - "docId": "kibSpacesOssPluginApi", - "section": "def-public.SpacesUnavailableStartContract", - "text": "SpacesUnavailableStartContract" - } - ], - "path": "src/plugins/spaces_oss/public/types.ts", - "deprecated": false, - "lifecycle": "start", - "initialIsOpen": true - } - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [ - { - "parentPluginId": "spacesOss", - "id": "def-common.Space", - "type": "Interface", - "tags": [], - "label": "Space", - "description": [ - "\nA Space." - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "\nThe unique identifier for this space.\nThe id becomes part of the \"URL Identifier\" of the space.\n\nExample: an id of `marketing` would result in the URL identifier of `/s/marketing`." - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.name", - "type": "string", - "tags": [], - "label": "name", - "description": [ - "\nDisplay name for this space." - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.description", - "type": "string", - "tags": [], - "label": "description", - "description": [ - "\nOptional description for this space." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.color", - "type": "string", - "tags": [], - "label": "color", - "description": [ - "\nOptional color (hex code) for this space.\nIf neither `color` nor `imageUrl` is specified, then a color will be automatically generated." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.initials", - "type": "string", - "tags": [], - "label": "initials", - "description": [ - "\nOptional display initials for this space's avatar. Supports a maximum of 2 characters.\nIf initials are not provided, then they will be derived from the space name automatically.\n\nInitials are not displayed if an `imageUrl` has been specified." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.imageUrl", - "type": "string", - "tags": [], - "label": "imageUrl", - "description": [ - "\nOptional base-64 encoded data image url to show as this space's avatar.\nThis setting takes precedence over any configured `color` or `initials`." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space.disabledFeatures", - "type": "Array", - "tags": [], - "label": "disabledFeatures", - "description": [ - "\nThe set of feature ids that should be hidden within this space." - ], - "signature": [ - "string[]" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "spacesOss", - "id": "def-common.Space._reserved", - "type": "CompoundType", - "tags": [ - "private" - ], - "label": "_reserved", - "description": [ - "\nIndicates that this space is reserved (system controlled).\nReserved spaces cannot be created or deleted by end-users." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/spaces_oss/common/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/spaces_oss.mdx b/api_docs/spaces_oss.mdx deleted file mode 100644 index d166a37a9373a..0000000000000 --- a/api_docs/spaces_oss.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -id: kibSpacesOssPluginApi -slug: /kibana-dev-docs/spacesOssPluginApi -title: spacesOss -image: https://source.unsplash.com/400x175/?github -summary: API docs for the spacesOss plugin -date: 2020-11-16 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spacesOss'] -warning: This 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 spacesOssObj from './spaces_oss.json'; - -This plugin exposes a limited set of spaces functionality to OSS plugins. - -Contact [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 77 | 0 | 10 | 0 | - -## Client - -### Setup - - -### Start - - -### Interfaces - - -### Consts, variables and types - - -## Common - -### Interfaces - - diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 6431d85ac1a51..e9925014d5a71 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -115,6 +115,10 @@ for use in their own application. |Expression Shape plugin adds a shape function to the expression plugin and an associated renderer. The renderer will display the given shape with selected decorations. +|{kib-repo}blob/{branch}/src/plugins/chart_expressions/expression_tagcloud/README.md[expressionTagcloud] +|Expression Tagcloud plugin adds a tagcloud renderer and function to the expression plugin. The renderer will display the Wordcloud chart. + + |{kib-repo}blob/{branch}/src/plugins/field_formats/README.md[fieldFormats] |Index pattern fields formatters @@ -223,10 +227,6 @@ so they can properly protect the data within their clusters. generating deep links to other apps, and creating short URLs. -|{kib-repo}blob/{branch}/src/plugins/spaces_oss/README.md[spacesOss] -|Bridge plugin for consumption of the Spaces feature from OSS plugins. - - |{kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry] |Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things: @@ -290,7 +290,7 @@ The plugin exposes the static DefaultEditorController class to consume. |WARNING: Missing README. -|{kib-repo}blob/{branch}/src/plugins/vis_type_pie[visTypePie] +|{kib-repo}blob/{branch}/src/plugins/vis_types/pie[visTypePie] |WARNING: Missing README. @@ -314,11 +314,11 @@ The plugin exposes the static DefaultEditorController class to consume. |WARNING: Missing README. -|{kib-repo}blob/{branch}/src/plugins/vis_type_vislib[visTypeVislib] +|{kib-repo}blob/{branch}/src/plugins/vis_types/vislib[visTypeVislib] |WARNING: Missing README. -|{kib-repo}blob/{branch}/src/plugins/vis_type_xy[visTypeXy] +|{kib-repo}blob/{branch}/src/plugins/vis_types/xy[visTypeXy] |WARNING: Missing README. @@ -366,8 +366,7 @@ The plugin exposes the static DefaultEditorController class to consume. |{kib-repo}blob/{branch}/x-pack/plugins/cloud/README.md[cloud] -|The cloud plugin adds cloud specific features to Kibana. -The client-side plugin configures following values: +|The cloud plugin adds Cloud-specific features to Kibana. |{kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication] @@ -378,10 +377,6 @@ The client-side plugin configures following values: |Adds drilldown capabilities to dashboard. Owned by the Kibana App team. -|{kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode/README.md[dashboardMode] -|The deprecated dashboard only mode. - - |{kib-repo}blob/{branch}/x-pack/plugins/data_enhanced/README.md[dataEnhanced] |The data_enhanced plugin is the x-pack counterpart to the src/plguins/data plugin. diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md index c12fb45e6ab42..f71eb03d89d72 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md @@ -22,5 +22,4 @@ export interface ChromeNavLinks | [getForceAppSwitcherNavigation$()](./kibana-plugin-core-public.chromenavlinks.getforceappswitchernavigation_.md) | An observable of the forced app switcher state. | | [getNavLinks$()](./kibana-plugin-core-public.chromenavlinks.getnavlinks_.md) | Get an observable for a sorted list of navlinks. | | [has(id)](./kibana-plugin-core-public.chromenavlinks.has.md) | Check whether or not a navlink exists. | -| [showOnly(id)](./kibana-plugin-core-public.chromenavlinks.showonly.md) | Remove all navlinks except the one matching the given id. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.showonly.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.showonly.md deleted file mode 100644 index 8f188f5fb71c9..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.showonly.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavLinks](./kibana-plugin-core-public.chromenavlinks.md) > [showOnly](./kibana-plugin-core-public.chromenavlinks.showonly.md) - -## ChromeNavLinks.showOnly() method - -Remove all navlinks except the one matching the given id. - -Signature: - -```typescript -showOnly(id: string): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| id | string | | - -Returns: - -`void` - -## Remarks - -NOTE: this is not reversible. - diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md index 208e0e0175d71..0084b0b50c869 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md @@ -14,5 +14,6 @@ export declare type ElasticsearchClientConfig = Pick; keepAlive?: boolean; + caFingerprint?: ClientOptions['caFingerprint']; }; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.getserverinfo.md b/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.getserverinfo.md new file mode 100644 index 0000000000000..0c9636b8eb634 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.getserverinfo.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpServicePreboot](./kibana-plugin-core-server.httpservicepreboot.md) > [getServerInfo](./kibana-plugin-core-server.httpservicepreboot.getserverinfo.md) + +## HttpServicePreboot.getServerInfo property + +Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running preboot http server. + +Signature: + +```typescript +getServerInfo: () => HttpServerInfo; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.md b/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.md index b4adf454a480f..ab0fc365fc651 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicepreboot.md @@ -73,6 +73,7 @@ httpPreboot.registerRoutes('my-plugin', (router) => { | Property | Type | Description | | --- | --- | --- | | [basePath](./kibana-plugin-core-server.httpservicepreboot.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-core-server.ibasepath.md). | +| [getServerInfo](./kibana-plugin-core-server.httpservicepreboot.getserverinfo.md) | () => HttpServerInfo | Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running preboot http server. | ## Methods diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.md index 46516be2329e9..fc825e3bf2937 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.md @@ -8,7 +8,7 @@ Signature: ```typescript -export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOptions +export interface SavedObjectsOpenPointInTimeOptions ``` ## Properties @@ -16,5 +16,6 @@ export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOpti | Property | Type | Description | | --- | --- | --- | | [keepAlive](./kibana-plugin-core-server.savedobjectsopenpointintimeoptions.keepalive.md) | string | Optionally specify how long ES should keep the PIT alive until the next request. Defaults to 5m. | +| [namespaces](./kibana-plugin-core-server.savedobjectsopenpointintimeoptions.namespaces.md) | string[] | An optional list of namespaces to be used when opening the PIT.When the spaces plugin is enabled: - this will default to the user's current space (as determined by the URL) - if specified, the user's current space will be ignored - ['*'] will search across all available spaces | | [preference](./kibana-plugin-core-server.savedobjectsopenpointintimeoptions.preference.md) | string | An optional ES preference value to be used for the query. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.namespaces.md new file mode 100644 index 0000000000000..06fb7519d52c2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsopenpointintimeoptions.namespaces.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsOpenPointInTimeOptions](./kibana-plugin-core-server.savedobjectsopenpointintimeoptions.md) > [namespaces](./kibana-plugin-core-server.savedobjectsopenpointintimeoptions.namespaces.md) + +## SavedObjectsOpenPointInTimeOptions.namespaces property + +An optional list of namespaces to be used when opening the PIT. + +When the spaces plugin is enabled: - this will default to the user's current space (as determined by the URL) - if specified, the user's current space will be ignored - `['*']` will search across all available spaces + +Signature: + +```typescript +namespaces?: string[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.hasuserindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.hasuserindexpattern.md new file mode 100644 index 0000000000000..31d1b9b9c16a9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.hasuserindexpattern.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [hasUserIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.hasuserindexpattern.md) + +## IndexPatternsService.hasUserIndexPattern() method + +Checks if current user has a user created index pattern ignoring fleet's server default index patterns + +Signature: + +```typescript +hasUserIndexPattern(): Promise; +``` +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md index 572a122066868..7b3ad2a379c83 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md @@ -45,5 +45,6 @@ export declare class IndexPatternsService | [createAndSave(spec, override, skipFetchFields)](./kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md) | | Create a new index pattern and save it right away | | [createSavedObject(indexPattern, override)](./kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md) | | Save a new index pattern | | [delete(indexPatternId)](./kibana-plugin-plugins-data-public.indexpatternsservice.delete.md) | | Deletes an index pattern from .kibana index | +| [hasUserIndexPattern()](./kibana-plugin-plugins-data-public.indexpatternsservice.hasuserindexpattern.md) | | Checks if current user has a user created index pattern ignoring fleet's server default index patterns | | [updateSavedObject(indexPattern, saveAttempts, ignoreErrors)](./kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md) | | Save existing index pattern. Will attempt to merge differences if there are conflicts | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md index fba9afd6f6648..b0cafe9ecf853 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md @@ -18,7 +18,6 @@ UI_SETTINGS: { readonly COURIER_SET_REQUEST_PREFERENCE: "courier:setRequestPreference"; readonly COURIER_CUSTOM_REQUEST_PREFERENCE: "courier:customRequestPreference"; readonly COURIER_MAX_CONCURRENT_SHARD_REQUESTS: "courier:maxConcurrentShardRequests"; - readonly COURIER_BATCH_SEARCHES: "courier:batchSearches"; readonly SEARCH_INCLUDE_FROZEN: "search:includeFrozen"; readonly SEARCH_TIMEOUT: "search:timeout"; readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.hasuserindexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.hasuserindexpattern.md new file mode 100644 index 0000000000000..49f365c106040 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.hasuserindexpattern.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) > [hasUserIndexPattern](./kibana-plugin-plugins-data-server.indexpatternsservice.hasuserindexpattern.md) + +## IndexPatternsService.hasUserIndexPattern() method + +Checks if current user has a user created index pattern ignoring fleet's server default index patterns + +Signature: + +```typescript +hasUserIndexPattern(): Promise; +``` +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md index 64c46fe4abbd8..65997e0688b7b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md @@ -45,5 +45,6 @@ export declare class IndexPatternsService | [createAndSave(spec, override, skipFetchFields)](./kibana-plugin-plugins-data-server.indexpatternsservice.createandsave.md) | | Create a new index pattern and save it right away | | [createSavedObject(indexPattern, override)](./kibana-plugin-plugins-data-server.indexpatternsservice.createsavedobject.md) | | Save a new index pattern | | [delete(indexPatternId)](./kibana-plugin-plugins-data-server.indexpatternsservice.delete.md) | | Deletes an index pattern from .kibana index | +| [hasUserIndexPattern()](./kibana-plugin-plugins-data-server.indexpatternsservice.hasuserindexpattern.md) | | Checks if current user has a user created index pattern ignoring fleet's server default index patterns | | [updateSavedObject(indexPattern, saveAttempts, ignoreErrors)](./kibana-plugin-plugins-data-server.indexpatternsservice.updatesavedobject.md) | | Save existing index pattern. Will attempt to merge differences if there are conflicts | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md index e1f002674ef6d..5453f97d73319 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md @@ -18,7 +18,6 @@ UI_SETTINGS: { readonly COURIER_SET_REQUEST_PREFERENCE: "courier:setRequestPreference"; readonly COURIER_CUSTOM_REQUEST_PREFERENCE: "courier:customRequestPreference"; readonly COURIER_MAX_CONCURRENT_SHARD_REQUESTS: "courier:maxConcurrentShardRequests"; - readonly COURIER_BATCH_SEARCHES: "courier:batchSearches"; readonly SEARCH_INCLUDE_FROZEN: "search:includeFrozen"; readonly SEARCH_TIMEOUT: "search:timeout"; readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index a5bdad16fa98f..49adc72bbe346 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -398,14 +398,6 @@ changes. [[kibana-search-settings]] ==== Search -[horizontal] -[[courier-batchsearches]]`courier:batchSearches`:: -**Deprecated in 7.6. Starting in 8.0, this setting will be optimized internally.** -When disabled, dashboard panels will load individually, and search requests will -terminate when users navigate away or update the query. When enabled, dashboard -panels will load together when all of the data is loaded, and searches will not -terminate. - [[courier-customrequestpreference]]`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference] to use when `courier:setRequestPreference` is set to "custom". diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index f168195e10ef5..050d14e4992d6 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -13,59 +13,48 @@ Alerts and actions are enabled by default in {kib}, but require you configure th You can configure the following settings in the `kibana.yml` file. - [float] [[general-alert-action-settings]] ==== General settings -[cols="2*<"] -|=== - -| `xpack.encryptedSavedObjects` -`.encryptionKey` - | A string of 32 or more characters used to encrypt sensitive properties on alerting rules and actions before they're stored in {es}. Third party credentials — such as the username and password used to connect to an SMTP service — are an example of encrypted properties. + - + - {kib} offers a <> to help generate this encryption key. + - + - If not set, {kib} will generate a random key on startup, but all alerting and action functions will be blocked. Generated keys are not allowed for alerting and actions because when a new key is generated on restart, existing encrypted data becomes inaccessible. For the same reason, alerting and actions in high-availability deployments of {kib} will behave unexpectedly if the key isn't the same on all instances of {kib}. + - + - Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <>. - Be sure to back up the encryption key value somewhere safe, as your alerting rules and actions will cease to function due to decryption failures should you lose it. If you want to rotate the encryption key, be sure to follow the instructions on <>. - -|=== +`xpack.encryptedSavedObjects.encryptionKey`:: +A string of 32 or more characters used to encrypt sensitive properties on alerting rules and actions before they're stored in {es}. Third party credentials — such as the username and password used to connect to an SMTP service — are an example of encrypted properties. ++ +{kib} offers a <> to help generate this encryption key. ++ +If not set, {kib} will generate a random key on startup, but all alerting and action functions will be blocked. Generated keys are not allowed for alerting and actions because when a new key is generated on restart, existing encrypted data becomes inaccessible. For the same reason, alerting and actions in high-availability deployments of {kib} will behave unexpectedly if the key isn't the same on all instances of {kib}. ++ +Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <>. +Be sure to back up the encryption key value somewhere safe, as your alerting rules and actions will cease to function due to decryption failures should you lose it. If you want to rotate the encryption key, be sure to follow the instructions on <>. [float] [[action-settings]] ==== Action settings -[cols="2*<"] -|=== -| `xpack.actions.enabled` - | Deprecated. This will be removed in 8.0. Feature toggle that enables Actions in {kib}. - If `false`, all features dependent on Actions are disabled, including the *Observability* and *Security* apps. Default: `true`. - -| `xpack.actions.allowedHosts` {ess-icon} - | A list of hostnames that {kib} is allowed to connect to when built-in actions are triggered. It defaults to `[*]`, allowing any host, but keep in mind the potential for SSRF attacks when hosts are not explicitly added to the allowed hosts. An empty list `[]` can be used to block built-in actions from making any external connections. + - + - Note that hosts associated with built-in actions, such as Slack and PagerDuty, are not automatically added to allowed hosts. If you are not using the default `[*]` setting, you must ensure that the corresponding endpoints are added to the allowed hosts as well. - -| `xpack.actions.customHostSettings` {ess-icon} - | A list of custom host settings to override existing global settings. - Default: an empty list. + - + - Each entry in the list must have a `url` property, to associate a connection - type (mail or https), hostname and port with the remaining options in the - entry. - + - In the following example, two custom host settings - are defined. The first provides a custom host setting for mail server - `mail.example.com` using port 465 that supplies server certificate authorization - data from both a file and inline, and requires TLS for the - connection. The second provides a custom host setting for https server - `webhook.example.com` which turns off server certificate authorization. - -|=== - +`xpack.actions.enabled`:: +Feature toggle that enables Actions in {kib}. +If `false`, all features dependent on Actions are disabled, including the *Observability* and *Security* apps. Default: `true`. + +`xpack.actions.allowedHosts` {ess-icon}:: +A list of hostnames that {kib} is allowed to connect to when built-in actions are triggered. It defaults to `[*]`, allowing any host, but keep in mind the potential for SSRF attacks when hosts are not explicitly added to the allowed hosts. An empty list `[]` can be used to block built-in actions from making any external connections. ++ +Note that hosts associated with built-in actions, such as Slack and PagerDuty, are not automatically added to allowed hosts. If you are not using the default `[*]` setting, you must ensure that the corresponding endpoints are added to the allowed hosts as well. + +`xpack.actions.customHostSettings` {ess-icon}:: +A list of custom host settings to override existing global settings. +Default: an empty list. ++ +Each entry in the list must have a `url` property, to associate a connection +type (mail or https), hostname and port with the remaining options in the +entry. ++ +In the following example, two custom host settings +are defined. The first provides a custom host setting for mail server +`mail.example.com` using port 465 that supplies server certificate authorization +data from both a file and inline, and requires TLS for the +connection. The second provides a custom host setting for https server +`webhook.example.com` which turns off server certificate authorization. ++ [source,yaml] -- xpack.actions.customHostSettings: @@ -86,132 +75,106 @@ xpack.actions.customHostSettings: verificationMode: 'none' -- -[cols="2*<"] -|=== - -| `xpack.actions.customHostSettings[n]` -`.url` {ess-icon} - | A URL associated with this custom host setting. Should be in the form of - `protocol://hostname:port`, where `protocol` is `https` or `smtp`. If the - port is not provided, 443 is used for `https` and 25 is used for - `smtp`. The `smtp` URLs are used for the Email actions that use this - server, and the `https` URLs are used for actions which use `https` to - connect to services. + - + - Entries with `https` URLs can use the `ssl` options, and entries with `smtp` - URLs can use both the `ssl` and `smtp` options. + - + - No other URL values should be part of this URL, including paths, - query strings, and authentication information. When an http or smtp request - is made as part of executing an action, only the protocol, hostname, and - port of the URL for that request are used to look up these configuration - values. - -| `xpack.actions.customHostSettings[n]` -`.smtp.ignoreTLS` {ess-icon} - | A boolean value indicating that TLS must not be used for this connection. - The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. - -| `xpack.actions.customHostSettings[n]` -`.smtp.requireTLS` {ess-icon} - | A boolean value indicating that TLS must be used for this connection. - The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. - -| `xpack.actions.customHostSettings[n]` -`.ssl.rejectUnauthorized` - | Deprecated. Use <> instead. A boolean value indicating whether to bypass server certificate validation. - Overrides the general `xpack.actions.rejectUnauthorized` configuration - for requests made for this hostname/port. - -|[[action-config-custom-host-verification-mode]] `xpack.actions.customHostSettings[n]` -`.ssl.verificationMode` {ess-icon} - | Controls the verification of the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the host server. Valid values are `full`, `certificate`, and `none`. - Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. Overrides the general `xpack.actions.ssl.verificationMode` configuration - for requests made for this hostname/port. - -| `xpack.actions.customHostSettings[n]` -`.ssl.certificateAuthoritiesFiles` - | A file name or list of file names of PEM-encoded certificate files to use - to validate the server. - -| `xpack.actions.customHostSettings[n]` -`.ssl.certificateAuthoritiesData` {ess-icon} - | The contents of a PEM-encoded certificate file, or multiple files appended - into a single string. This configuration can be used for environments where - the files cannot be made available. - -| `xpack.actions.enabledActionTypes` {ess-icon} - | A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. + - + - Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. - -| `xpack.actions` -`.preconfiguredAlertHistoryEsIndex` {ess-icon} - | Enables a preconfigured alert history {es} <> connector. Default: `false`. - -| `xpack.actions.preconfigured` - | Specifies preconfigured connector IDs and configs. Default: {}. - -| `xpack.actions.proxyUrl` {ess-icon} - | Specifies the proxy URL to use, if using a proxy for actions. By default, no proxy is used. - -| `xpack.actions.proxyBypassHosts` {ess-icon} - | Specifies hostnames which should not use the proxy, if using a proxy for actions. The value is an array of hostnames as strings. By default, all hosts will use the proxy, but if an action's hostname is in this list, the proxy will not be used. The settings `xpack.actions.proxyBypassHosts` and `xpack.actions.proxyOnlyHosts` cannot be used at the same time. - -| `xpack.actions.proxyOnlyHosts` {ess-icon} - | Specifies hostnames which should only use the proxy, if using a proxy for actions. The value is an array of hostnames as strings. By default, no hosts will use the proxy, but if an action's hostname is in this list, the proxy will be used. The settings `xpack.actions.proxyBypassHosts` and `xpack.actions.proxyOnlyHosts` cannot be used at the same time. - -| `xpack.actions.proxyHeaders` {ess-icon} - | Specifies HTTP headers for the proxy, if using a proxy for actions. Default: {}. - -a|`xpack.actions.` -`proxyRejectUnauthorizedCertificates` {ess-icon} - | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. - -|[[action-config-proxy-verification-mode]] -`xpack.actions[n]` -`.ssl.proxyVerificationMode` {ess-icon} -| Controls the verification for the proxy server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. +`xpack.actions.customHostSettings[n].url` {ess-icon}:: +A URL associated with this custom host setting. Should be in the form of +`protocol://hostname:port`, where `protocol` is `https` or `smtp`. If the +port is not provided, 443 is used for `https` and 25 is used for +`smtp`. The `smtp` URLs are used for the Email actions that use this +server, and the `https` URLs are used for actions which use `https` to +connect to services. ++ +Entries with `https` URLs can use the `ssl` options, and entries with `smtp` +URLs can use both the `ssl` and `smtp` options. ++ +No other URL values should be part of this URL, including paths, +query strings, and authentication information. When an http or smtp request +is made as part of executing an action, only the protocol, hostname, and +port of the URL for that request are used to look up these configuration +values. + +`xpack.actions.customHostSettings[n].smtp.ignoreTLS` {ess-icon}:: +A boolean value indicating that TLS must not be used for this connection. +The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. + +`xpack.actions.customHostSettings[n].smtp.requireTLS` {ess-icon}:: +A boolean value indicating that TLS must be used for this connection. +The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. + +`xpack.actions.customHostSettings[n].ssl.rejectUnauthorized`:: +Deprecated. Use <> instead. A boolean value indicating whether to bypass server certificate validation. +Overrides the general `xpack.actions.rejectUnauthorized` configuration +for requests made for this hostname/port. + +[[action-config-custom-host-verification-mode]] `xpack.actions.customHostSettings[n].ssl.verificationMode` {ess-icon}:: +Controls the verification of the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the host server. Valid values are `full`, `certificate`, and `none`. +Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. Overrides the general `xpack.actions.ssl.verificationMode` configuration +for requests made for this hostname/port. + +`xpack.actions.customHostSettings[n].ssl.certificateAuthoritiesFiles`:: +A file name or list of file names of PEM-encoded certificate files to use +to validate the server. + +`xpack.actions.customHostSettings[n].ssl.certificateAuthoritiesData` {ess-icon}:: +The contents of a PEM-encoded certificate file, or multiple files appended +into a single string. This configuration can be used for environments where +the files cannot be made available. + +`xpack.actions.enabledActionTypes` {ess-icon}:: +A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. ++ +Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. + +`xpack.actions.preconfiguredAlertHistoryEsIndex` {ess-icon}:: +Enables a preconfigured alert history {es} <> connector. Default: `false`. + +`xpack.actions.preconfigured`:: +Specifies preconfigured connector IDs and configs. Default: {}. + +`xpack.actions.proxyUrl` {ess-icon}:: +Specifies the proxy URL to use, if using a proxy for actions. By default, no proxy is used. + +`xpack.actions.proxyBypassHosts` {ess-icon}:: +Specifies hostnames which should not use the proxy, if using a proxy for actions. The value is an array of hostnames as strings. By default, all hosts will use the proxy, but if an action's hostname is in this list, the proxy will not be used. The settings `xpack.actions.proxyBypassHosts` and `xpack.actions.proxyOnlyHosts` cannot be used at the same time. + +`xpack.actions.proxyOnlyHosts` {ess-icon}:: +Specifies hostnames which should only use the proxy, if using a proxy for actions. The value is an array of hostnames as strings. By default, no hosts will use the proxy, but if an action's hostname is in this list, the proxy will be used. The settings `xpack.actions.proxyBypassHosts` and `xpack.actions.proxyOnlyHosts` cannot be used at the same time. + +`xpack.actions.proxyHeaders` {ess-icon}:: +Specifies HTTP headers for the proxy, if using a proxy for actions. Default: {}. + +`xpack.actions.proxyRejectUnauthorizedCertificates` {ess-icon}:: +Deprecated. Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. + +[[action-config-proxy-verification-mode]]`xpack.actions[n].ssl.proxyVerificationMode` {ess-icon}:: +Controls the verification for the proxy server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. -| `xpack.actions.rejectUnauthorized` {ess-icon} - | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for actions. Default: `true`. + - + - As an alternative to setting `xpack.actions.rejectUnauthorized`, you can use the setting - `xpack.actions.customHostSettings` to set SSL options for specific servers. - -|[[action-config-verification-mode]] -`xpack.actions[n]` -`.ssl.verificationMode` {ess-icon} -| Controls the verification for the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection for actions. Valid values are `full`, `certificate`, and `none`. - Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. + - + - As an alternative to setting `xpack.actions.ssl.verificationMode`, you can use the setting - `xpack.actions.customHostSettings` to set SSL options for specific servers. - +`xpack.actions.rejectUnauthorized` {ess-icon}:: +Deprecated. Use <> instead. Set to `false` to bypass certificate validation for actions. Default: `true`. ++ +As an alternative to setting `xpack.actions.rejectUnauthorized`, you can use the setting +`xpack.actions.customHostSettings` to set SSL options for specific servers. +[[action-config-verification-mode]] `xpack.actions[n].ssl.verificationMode` {ess-icon}:: +Controls the verification for the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection for actions. Valid values are `full`, `certificate`, and `none`. +Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. ++ +As an alternative to setting `xpack.actions.ssl.verificationMode`, you can use the setting +`xpack.actions.customHostSettings` to set SSL options for specific servers. -| `xpack.actions.maxResponseContentLength` {ess-icon} - | Specifies the max number of bytes of the http response for requests to external resources. Default: 1000000 (1MB). - -| `xpack.actions.responseTimeout` {ess-icon} - | Specifies the time allowed for requests to external resources. Requests that take longer are aborted. The time is formatted as: + - + - `[ms,s,m,h,d,w,M,Y]` + - + - For example, `20m`, `24h`, `7d`, `1w`. Default: `60s`. - +`xpack.actions.maxResponseContentLength` {ess-icon}:: +Specifies the max number of bytes of the http response for requests to external resources. Default: 1000000 (1MB). -|=== +`xpack.actions.responseTimeout` {ess-icon}:: +Specifies the time allowed for requests to external resources. Requests that take longer are aborted. The time is formatted as: ++ +`[ms,s,m,h,d,w,M,Y]` ++ +For example, `20m`, `24h`, `7d`, `1w`. Default: `60s`. [float] [[alert-settings]] ==== Alerting settings -[cols="2*<"] -|=== - -| `xpack.alerting.maxEphemeralActionsPerAlert` - | Sets the number of actions that will be executed ephemerally. To use this, enable ephemeral tasks in task manager first with <> - -|=== +`xpack.alerting.maxEphemeralActionsPerAlert`:: +Sets the number of actions that will be executed ephemerally. To use this, enable ephemeral tasks in task manager first with <> diff --git a/docs/settings/banners-settings.asciidoc b/docs/settings/banners-settings.asciidoc index ce56d4dbe7a4d..43f1724403595 100644 --- a/docs/settings/banners-settings.asciidoc +++ b/docs/settings/banners-settings.asciidoc @@ -14,25 +14,17 @@ You can configure the `xpack.banners` settings in your `kibana.yml` file. Banners are a https://www.elastic.co/subscriptions[subscription feature]. ==== -[[general-banners-settings-kb]] -==== General banner settings +`xpack.banners.placement`:: +Set to `top` to display a banner above the Elastic header. Defaults to `disabled`. -[cols="2*<"] -|=== +`xpack.banners.textContent`:: +The text to display inside the banner, either plain text or Markdown. -| `xpack.banners.placement` -| Set to `top` to display a banner above the Elastic header. Defaults to `disabled`. +`xpack.banners.textColor`:: +The color for the banner text. Defaults to `#8A6A0A`. -| `xpack.banners.textContent` -| The text to display inside the banner, either plain text or Markdown. +`xpack.banners.backgroundColor`:: +The color of the banner background. Defaults to `#FFF9E8`. -| `xpack.banners.textColor` -| The color for the banner text. Defaults to `#8A6A0A`. - -| `xpack.banners.backgroundColor` -| The color of the banner background. Defaults to `#FFF9E8`. - -| `xpack.banners.disableSpaceBanners` -| If true, per-space banner overrides will be disabled. Defaults to `false`. - -|=== +`xpack.banners.disableSpaceBanners`:: +If true, per-space banner overrides will be disabled. Defaults to `false`. diff --git a/docs/settings/dev-settings.asciidoc b/docs/settings/dev-settings.asciidoc index 810694f46b317..b7edf36851d91 100644 --- a/docs/settings/dev-settings.asciidoc +++ b/docs/settings/dev-settings.asciidoc @@ -12,31 +12,20 @@ They are enabled by default. [[grok-settings]] ==== Grok Debugger settings -[cols="2*<"] -|=== -| `xpack.grokdebugger.enabled` {ess-icon} - | Set to `true` to enable the <>. Defaults to `true`. +`xpack.grokdebugger.enabled` {ess-icon}:: +Set to `true` to enable the <>. Defaults to `true`. -|=== [float] [[profiler-settings]] ==== {searchprofiler} settings -[cols="2*<"] -|=== -| `xpack.searchprofiler.enabled` - | Set to `true` to enable the <>. Defaults to `true`. - -|=== +`xpack.searchprofiler.enabled`:: +Set to `true` to enable the <>. Defaults to `true`. [float] [[painless_lab-settings]] ==== Painless Lab settings -[cols="2*<"] -|=== -| `xpack.painless_lab.enabled` - | When set to `true`, enables the <>. Defaults to `true`. - -|=== +`xpack.painless_lab.enabled`:: +When set to `true`, enables the <>. Defaults to `true`. diff --git a/docs/settings/graph-settings.asciidoc b/docs/settings/graph-settings.asciidoc index 876e3dc936ccf..093edb0d08547 100644 --- a/docs/settings/graph-settings.asciidoc +++ b/docs/settings/graph-settings.asciidoc @@ -7,13 +7,5 @@ You do not need to configure any settings to use the {graph-features}. -[float] -[[general-graph-settings]] -==== General graph settings - -[cols="2*<"] -|=== -| `xpack.graph.enabled` {ess-icon} - | Set to `false` to disable the {graph-features}. - -|=== +`xpack.graph.enabled` {ess-icon}:: +Set to `false` to disable the {graph-features}. diff --git a/docs/user/monitoring/kibana-alerts.asciidoc b/docs/user/monitoring/kibana-alerts.asciidoc index f00a3999ab277..64ba8bf044e4f 100644 --- a/docs/user/monitoring/kibana-alerts.asciidoc +++ b/docs/user/monitoring/kibana-alerts.asciidoc @@ -124,7 +124,7 @@ valid for 30 days. == Alerts and rules [discrete] === Create default rules -This option can be used to create default rules in this kibana spaces. This is +This option can be used to create default rules in this kibana space. This is useful for scenarios when you didn't choose to create these default rules initially or anytime later if the rules were accidentally deleted. diff --git a/jest.config.js b/jest.config.js index bd1e865a7e64a..09532dc28bbb2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,6 +13,8 @@ module.exports = { '/packages/*/jest.config.js', '/src/*/jest.config.js', '/src/plugins/*/jest.config.js', + '/src/plugins/chart_expressions/*/jest.config.js', + '/src/plugins/vis_types/*/jest.config.js', '/test/*/jest.config.js', '/x-pack/plugins/*/jest.config.js', ], diff --git a/package.json b/package.json index 6d604bc20cadd..ecf7c7a446409 100644 --- a/package.json +++ b/package.json @@ -95,11 +95,11 @@ "dependencies": { "@elastic/apm-rum": "^5.8.0", "@elastic/apm-rum-react": "^1.2.11", - "@elastic/charts": "34.0.0", + "@elastic/charts": "34.1.1", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", - "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.17", + "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.18", "@elastic/ems-client": "7.15.0", - "@elastic/eui": "37.1.1", + "@elastic/eui": "37.3.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/maki": "6.3.0", diff --git a/packages/kbn-alerts/.babelrc b/packages/kbn-alerts/.babelrc new file mode 100644 index 0000000000000..40a198521b903 --- /dev/null +++ b/packages/kbn-alerts/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-alerts/.babelrc.browser b/packages/kbn-alerts/.babelrc.browser new file mode 100644 index 0000000000000..71bbfbcd6eb2f --- /dev/null +++ b/packages/kbn-alerts/.babelrc.browser @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/webpack_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-alerts/BUILD.bazel b/packages/kbn-alerts/BUILD.bazel index c585b4430bfcb..a571380202cd6 100644 --- a/packages/kbn-alerts/BUILD.bazel +++ b/packages/kbn-alerts/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-alerts" @@ -12,8 +13,7 @@ SOURCE_FILES = glob( ], exclude = [ "**/*.test.*", - "**/*.mock.*", - "**/*.mocks.*", + "**/__snapshots__" ], ) @@ -25,32 +25,40 @@ filegroup( ) NPM_MODULE_EXTRA_FILES = [ - "react/package.json", "package.json", "README.md", ] -SRC_DEPS = [ - "//packages/kbn-babel-preset", - "//packages/kbn-dev-utils", +RUNTIME_DEPS = [ "//packages/kbn-i18n", - "@npm//@babel/core", - "@npm//babel-loader", "@npm//@elastic/eui", + "@npm//enzyme", "@npm//react", "@npm//resize-observer-polyfill", - "@npm//rxjs", - "@npm//tslib", ] TYPES_DEPS = [ - "@npm//typescript", + "//packages/kbn-i18n", + "@npm//@elastic/eui", + "@npm//resize-observer-polyfill", + "@npm//@types/enzyme", "@npm//@types/jest", "@npm//@types/node", "@npm//@types/react", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + config_file = ".babelrc.browser" +) ts_config( name = "tsconfig", @@ -61,50 +69,26 @@ ts_config( ], ) -ts_config( - name = "tsconfig_browser", - src = "tsconfig.browser.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.browser.json", - "//:tsconfig.browser_bazel.json", - ], -) - ts_project( - name = "tsc", + name = "tsc_types", args = ["--pretty"], srcs = SRCS, - deps = DEPS, - allow_js = True, + deps = TYPES_DEPS, declaration = True, - declaration_dir = "target_types", declaration_map = True, - out_dir = "target_node", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", ) -ts_project( - name = "tsc_browser", - args = ['--pretty'], - srcs = SRCS, - deps = DEPS, - allow_js = True, - declaration = False, - out_dir = "target_web", - source_map = True, - root_dir = "src", - tsconfig = ":tsconfig_browser", -) - js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = [":tsc", ":tsc_browser"] + DEPS, ) pkg_npm( @@ -120,4 +104,4 @@ filegroup( ":npm_module", ], visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/packages/kbn-alerts/babel.config.js b/packages/kbn-alerts/babel.config.js deleted file mode 100644 index b4a118df51af5..0000000000000 --- a/packages/kbn-alerts/babel.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - env: { - web: { - presets: ['@kbn/babel-preset/webpack_preset'], - }, - node: { - presets: ['@kbn/babel-preset/node_preset'], - }, - }, - ignore: ['**/*.test.ts', '**/*.test.tsx'], -}; diff --git a/packages/kbn-alerts/react/package.json b/packages/kbn-alerts/react/package.json deleted file mode 100644 index c5f222b5843ac..0000000000000 --- a/packages/kbn-alerts/react/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "browser": "../target_web/react", - "main": "../target_node/react", - "types": "../target_types/react/index.d.ts" -} diff --git a/packages/kbn-alerts/tsconfig.browser.json b/packages/kbn-alerts/tsconfig.browser.json deleted file mode 100644 index bb58f529eb0bb..0000000000000 --- a/packages/kbn-alerts/tsconfig.browser.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../tsconfig.browser_bazel.json", - "compilerOptions": { - "allowJs": true, - "outDir": "./target_web", - "declaration": false, - "isolatedModules": true, - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-alerts/src", - "types": [ - "jest", - "node" - ], - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - ], - "exclude": [ - "**/__fixtures__/**/*" - ] -} \ No newline at end of file diff --git a/packages/kbn-alerts/tsconfig.json b/packages/kbn-alerts/tsconfig.json index 6a791ca2e5844..fa18a40744354 100644 --- a/packages/kbn-alerts/tsconfig.json +++ b/packages/kbn-alerts/tsconfig.json @@ -1,15 +1,14 @@ { "extends": "../../tsconfig.bazel.json", "compilerOptions": { - "allowJs": true, - "declarationDir": "./target_types", - "outDir": "target_node", "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-alerts/src", - "rootDir": "src", "types": ["jest", "node", "resize-observer-polyfill"] }, - "include": ["src/**/*"] -} \ No newline at end of file + "include": ["src/**/*"], +} diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index d09c61a1c2110..aa520e7189e54 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -13,7 +13,7 @@ import { mockApplyDeprecations, mockedChangedPaths } from './config_service.test import { rawConfigServiceMock } from './raw/raw_config_service.mock'; import { schema } from '@kbn/config-schema'; -import { MockedLogger, loggerMock } from '@kbn/logging/target/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging/mocks'; import { ConfigService, Env, RawPackageInfo } from '.'; diff --git a/packages/kbn-interpreter/.babelrc b/packages/kbn-interpreter/.babelrc new file mode 100644 index 0000000000000..7da72d1779128 --- /dev/null +++ b/packages/kbn-interpreter/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-interpreter/BUILD.bazel b/packages/kbn-interpreter/BUILD.bazel index 903f892b64f3f..52df0f0aa8d85 100644 --- a/packages/kbn-interpreter/BUILD.bazel +++ b/packages/kbn-interpreter/BUILD.bazel @@ -1,6 +1,7 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@npm//peggy:index.bzl", "peggy") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-interpreter" PKG_REQUIRE_NAME = "@kbn/interpreter" @@ -25,7 +26,7 @@ NPM_MODULE_EXTRA_FILES = [ "package.json", ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "@npm//lodash", ] @@ -35,7 +36,11 @@ TYPES_DEPS = [ "@npm//@types/node", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) peggy( name = "grammar", @@ -62,14 +67,15 @@ ts_config( ) ts_project( - name = "tsc", + name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, allow_js = True, declaration = True, declaration_map = True, - out_dir = "target", + emit_declaration_only = True, + out_dir = "target_types", source_map = True, root_dir = "src", tsconfig = ":tsconfig", @@ -78,7 +84,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES + [":grammar"], - deps = DEPS + [":tsc"], + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-interpreter/common/package.json b/packages/kbn-interpreter/common/package.json index 2f5277a8e8652..6d03f2e1c6236 100644 --- a/packages/kbn-interpreter/common/package.json +++ b/packages/kbn-interpreter/common/package.json @@ -1,5 +1,5 @@ { "private": true, - "main": "../target/common/index.js", - "types": "../target/common/index.d.ts" + "main": "../target_node/common/index.js", + "types": "../target_types/common/index.d.ts" } \ No newline at end of file diff --git a/packages/kbn-interpreter/src/common/lib/ast.from_expression.test.js b/packages/kbn-interpreter/src/common/lib/ast.from_expression.test.js index a098a3fdce0f6..4011292178cfa 100644 --- a/packages/kbn-interpreter/src/common/lib/ast.from_expression.test.js +++ b/packages/kbn-interpreter/src/common/lib/ast.from_expression.test.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { fromExpression } from '@kbn/interpreter/target/common/lib/ast'; +import { fromExpression } from '@kbn/interpreter/common'; import { getType } from './get_type'; describe('ast fromExpression', () => { diff --git a/packages/kbn-interpreter/tsconfig.json b/packages/kbn-interpreter/tsconfig.json index 74ec484ea63e9..60f8c76cf8809 100644 --- a/packages/kbn-interpreter/tsconfig.json +++ b/packages/kbn-interpreter/tsconfig.json @@ -2,9 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "allowJs": true, - "outDir": "./target/types", "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "./target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-interpreter/src", diff --git a/packages/kbn-logging/.babelrc b/packages/kbn-logging/.babelrc new file mode 100644 index 0000000000000..7da72d1779128 --- /dev/null +++ b/packages/kbn-logging/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-logging/BUILD.bazel b/packages/kbn-logging/BUILD.bazel index 1a3fa851a3957..71a7ece15aa73 100644 --- a/packages/kbn-logging/BUILD.bazel +++ b/packages/kbn-logging/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-logging" PKG_REQUIRE_NAME = "@kbn/logging" @@ -21,20 +22,26 @@ filegroup( ) NPM_MODULE_EXTRA_FILES = [ + "mocks/package.json", "package.json", "README.md" ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "//packages/kbn-std" ] TYPES_DEPS = [ + "//packages/kbn-std", "@npm//@types/jest", "@npm//@types/node", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -46,13 +53,14 @@ ts_config( ) ts_project( - name = "tsc", + name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - out_dir = "target", + emit_declaration_only = True, + out_dir = "target_types", source_map = True, root_dir = "src", tsconfig = ":tsconfig", @@ -61,7 +69,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = DEPS + [":tsc"], + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-logging/mocks/package.json b/packages/kbn-logging/mocks/package.json new file mode 100644 index 0000000000000..8410f557e9524 --- /dev/null +++ b/packages/kbn-logging/mocks/package.json @@ -0,0 +1,5 @@ +{ + "private": true, + "main": "../target_node/mocks/index.js", + "types": "../target_types/mocks/index.d.ts" +} \ No newline at end of file diff --git a/packages/kbn-logging/package.json b/packages/kbn-logging/package.json index d80cc1c40d7e1..c35c2f5d06095 100644 --- a/packages/kbn-logging/package.json +++ b/packages/kbn-logging/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts" + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts" } \ No newline at end of file diff --git a/packages/kbn-logging/tsconfig.json b/packages/kbn-logging/tsconfig.json index aaf79da229a86..a6fb0f2f73187 100644 --- a/packages/kbn-logging/tsconfig.json +++ b/packages/kbn-logging/tsconfig.json @@ -1,13 +1,14 @@ { "extends": "../../tsconfig.bazel.json", "compilerOptions": { - "outDir": "target", - "stripInternal": false, "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-logging/src", + "stripInternal": false, "types": [ "jest", "node" diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index b11458d6539e8..3f846eaff2855 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -12,7 +12,6 @@ pageLoadAssetSize: crossClusterReplication: 65408 dashboard: 374194 dashboardEnhanced: 65646 - dashboardMode: 22716 data: 824229 dataEnhanced: 50420 devTools: 38637 @@ -99,7 +98,6 @@ pageLoadAssetSize: runtimeFields: 41752 stackAlerts: 29684 presentationUtil: 94301 - spacesOss: 18817 indexPatternFieldEditor: 50000 osquery: 107090 fileUpload: 25664 @@ -117,3 +115,4 @@ pageLoadAssetSize: expressionMetric: 22238 expressionShape: 34008 interactiveSetup: 18532 + expressionTagcloud: 27505 diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index 97a7f33be673d..4ffc4486faa03 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -132,7 +132,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { expect(foo.cache.getModuleCount()).toBe(6); expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(` Array [ - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target_node/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, @@ -155,7 +155,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { /node_modules/@kbn/optimizer/postcss.config.js, /node_modules/css-loader/package.json, /node_modules/style-loader/package.json, - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target_node/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts, @@ -175,7 +175,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { expect(baz.cache.getReferencedFiles()).toMatchInlineSnapshot(` Array [ - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target_node/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/x-pack/baz/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/x-pack/baz/public/index.ts, /packages/kbn-optimizer/src/worker/entry_point_creator.ts, diff --git a/packages/kbn-securitysolution-autocomplete/.babelrc b/packages/kbn-securitysolution-autocomplete/.babelrc new file mode 100644 index 0000000000000..40a198521b903 --- /dev/null +++ b/packages/kbn-securitysolution-autocomplete/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-autocomplete/.babelrc.browser b/packages/kbn-securitysolution-autocomplete/.babelrc.browser new file mode 100644 index 0000000000000..71bbfbcd6eb2f --- /dev/null +++ b/packages/kbn-securitysolution-autocomplete/.babelrc.browser @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/webpack_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-autocomplete/BUILD.bazel b/packages/kbn-securitysolution-autocomplete/BUILD.bazel index 18c3b8f3ae3bb..53cd7b4f8d3e1 100644 --- a/packages/kbn-securitysolution-autocomplete/BUILD.bazel +++ b/packages/kbn-securitysolution-autocomplete/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-securitysolution-autocomplete" @@ -25,35 +26,54 @@ filegroup( ) NPM_MODULE_EXTRA_FILES = [ - "react/package.json", "package.json", "README.md", ] -SRC_DEPS = [ - "//packages/kbn-babel-preset", - "//packages/kbn-dev-utils", +RUNTIME_DEPS = [ + "//packages/kbn-es-query", "//packages/kbn-i18n", - "//packages/kbn-securitysolution-io-ts-list-types", "//packages/kbn-securitysolution-list-hooks", - "//packages/kbn-es-query", - "@npm//@babel/core", - "@npm//babel-loader", + "//packages/kbn-securitysolution-list-utils", + "//packages/kbn-securitysolution-io-ts-list-types", "@npm//@elastic/eui", + "@npm//@testing-library/react", + "@npm//@testing-library/react-hooks", + "@npm//enzyme", + "@npm//moment", "@npm//react", "@npm//resize-observer-polyfill", - "@npm//rxjs", - "@npm//tslib", ] TYPES_DEPS = [ - "@npm//typescript", + "//packages/kbn-es-query", + "//packages/kbn-i18n", + "//packages/kbn-securitysolution-list-hooks", + "//packages/kbn-securitysolution-list-utils", + "//packages/kbn-securitysolution-io-ts-list-types", + "@npm//@elastic/eui", + "@npm//@testing-library/react", + "@npm//@testing-library/react-hooks", + "@npm//moment", + "@npm//resize-observer-polyfill", + "@npm//@types/enzyme", "@npm//@types/jest", "@npm//@types/node", "@npm//@types/react", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + config_file = ".babelrc.browser" +) ts_config( name = "tsconfig", @@ -64,50 +84,26 @@ ts_config( ], ) -ts_config( - name = "tsconfig_browser", - src = "tsconfig.browser.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.browser.json", - "//:tsconfig.browser_bazel.json", - ], -) - ts_project( - name = "tsc", + name = "tsc_types", args = ["--pretty"], srcs = SRCS, - deps = DEPS, - allow_js = True, + deps = TYPES_DEPS, declaration = True, - declaration_dir = "target_types", declaration_map = True, - out_dir = "target_node", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", ) -ts_project( - name = "tsc_browser", - args = ['--pretty'], - srcs = SRCS, - deps = DEPS, - allow_js = True, - declaration = False, - out_dir = "target_web", - source_map = True, - root_dir = "src", - tsconfig = ":tsconfig_browser", -) - js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = [":tsc", ":tsc_browser"] + DEPS, ) pkg_npm( diff --git a/packages/kbn-securitysolution-autocomplete/babel.config.js b/packages/kbn-securitysolution-autocomplete/babel.config.js deleted file mode 100644 index b4a118df51af5..0000000000000 --- a/packages/kbn-securitysolution-autocomplete/babel.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - env: { - web: { - presets: ['@kbn/babel-preset/webpack_preset'], - }, - node: { - presets: ['@kbn/babel-preset/node_preset'], - }, - }, - ignore: ['**/*.test.ts', '**/*.test.tsx'], -}; diff --git a/packages/kbn-securitysolution-autocomplete/react/package.json b/packages/kbn-securitysolution-autocomplete/react/package.json deleted file mode 100644 index c5f222b5843ac..0000000000000 --- a/packages/kbn-securitysolution-autocomplete/react/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "browser": "../target_web/react", - "main": "../target_node/react", - "types": "../target_types/react/index.d.ts" -} diff --git a/packages/kbn-securitysolution-autocomplete/tsconfig.browser.json b/packages/kbn-securitysolution-autocomplete/tsconfig.browser.json deleted file mode 100644 index 404043569aa92..0000000000000 --- a/packages/kbn-securitysolution-autocomplete/tsconfig.browser.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../tsconfig.browser_bazel.json", - "compilerOptions": { - "allowJs": true, - "outDir": "./target_web", - "declaration": false, - "isolatedModules": true, - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-securitysolution-autocomplete/src", - "types": [ - "jest", - "node" - ], - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - ], - "exclude": [ - "**/__fixtures__/**/*" - ] -} diff --git a/packages/kbn-securitysolution-autocomplete/tsconfig.json b/packages/kbn-securitysolution-autocomplete/tsconfig.json index 484b639f94332..fa7eff8234011 100644 --- a/packages/kbn-securitysolution-autocomplete/tsconfig.json +++ b/packages/kbn-securitysolution-autocomplete/tsconfig.json @@ -1,15 +1,14 @@ { "extends": "../../tsconfig.bazel.json", "compilerOptions": { - "allowJs": true, - "declarationDir": "./target_types", - "outDir": "target_node", "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-autocomplete/src", "rootDir": "src", "types": ["jest", "node", "resize-observer-polyfill"] }, - "include": ["src/**/*"] + "include": ["src/**/*"], } diff --git a/packages/kbn-securitysolution-es-utils/.babelrc b/packages/kbn-securitysolution-es-utils/.babelrc new file mode 100644 index 0000000000000..40a198521b903 --- /dev/null +++ b/packages/kbn-securitysolution-es-utils/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-es-utils/BUILD.bazel b/packages/kbn-securitysolution-es-utils/BUILD.bazel index 97f2d9a41cd78..49c23488b3fe3 100644 --- a/packages/kbn-securitysolution-es-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-es-utils/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-securitysolution-es-utils" @@ -27,18 +28,27 @@ NPM_MODULE_EXTRA_FILES = [ "README.md", ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "@npm//@elastic/elasticsearch", + "@npm//@hapi/boom", "@npm//@hapi/hapi", "@npm//tslib", ] TYPES_DEPS = [ + "@npm//@elastic/elasticsearch", + "@npm//@hapi/boom", + "@npm//tslib", + "@npm//@types/hapi__hapi", "@npm//@types/jest", "@npm//@types/node", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -50,24 +60,25 @@ ts_config( ) ts_project( - name = "tsc", - srcs = SRCS, + name = "tsc_types", args = ["--pretty"], + srcs = SRCS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - out_dir = "target", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", - deps = DEPS, ) js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-es-utils/package.json b/packages/kbn-securitysolution-es-utils/package.json index 7d0c0993c6c32..84c16ad0b7992 100644 --- a/packages/kbn-securitysolution-es-utils/package.json +++ b/packages/kbn-securitysolution-es-utils/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc...", "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", "private": true } diff --git a/packages/kbn-securitysolution-es-utils/tsconfig.json b/packages/kbn-securitysolution-es-utils/tsconfig.json index 906b01c943ab7..e5b1c99d3f598 100644 --- a/packages/kbn-securitysolution-es-utils/tsconfig.json +++ b/packages/kbn-securitysolution-es-utils/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "declaration": true, "declarationMap": true, - "outDir": "target", + "emitDeclarationOnly": true, + "outDir": "target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-es-utils/src", diff --git a/packages/kbn-securitysolution-hook-utils/.babelrc b/packages/kbn-securitysolution-hook-utils/.babelrc new file mode 100644 index 0000000000000..b17a19d6faf3f --- /dev/null +++ b/packages/kbn-securitysolution-hook-utils/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-hook-utils/BUILD.bazel b/packages/kbn-securitysolution-hook-utils/BUILD.bazel index b5f3e0df2e0a7..fd57bc852d683 100644 --- a/packages/kbn-securitysolution-hook-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-hook-utils/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-securitysolution-hook-utils" @@ -27,19 +28,27 @@ NPM_MODULE_EXTRA_FILES = [ "README.md", ] -SRC_DEPS = [ +RUNTIME_DEPS = [ + "@npm//@testing-library/react-hooks", "@npm//react", "@npm//rxjs", "@npm//tslib", ] TYPES_DEPS = [ + "@npm//@testing-library/react-hooks", + "@npm//rxjs", + "@npm//tslib", "@npm//@types/jest", "@npm//@types/node", "@npm//@types/react", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -51,24 +60,25 @@ ts_config( ) ts_project( - name = "tsc", - srcs = SRCS, + name = "tsc_types", args = ["--pretty"], + srcs = SRCS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - out_dir = "target", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", - deps = DEPS, ) js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-hook-utils/package.json b/packages/kbn-securitysolution-hook-utils/package.json index 6da17ab00f31c..d33fd1b79b328 100644 --- a/packages/kbn-securitysolution-hook-utils/package.json +++ b/packages/kbn-securitysolution-hook-utils/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "Security Solution utilities for React hooks", "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", "private": true } diff --git a/packages/kbn-securitysolution-hook-utils/tsconfig.json b/packages/kbn-securitysolution-hook-utils/tsconfig.json index 7e2cae223b14d..4782331e31c44 100644 --- a/packages/kbn-securitysolution-hook-utils/tsconfig.json +++ b/packages/kbn-securitysolution-hook-utils/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "declaration": true, "declarationMap": true, - "outDir": "target", + "emitDeclarationOnly": true, + "outDir": "target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-hook-utils/src", diff --git a/packages/kbn-securitysolution-t-grid/.babelrc b/packages/kbn-securitysolution-t-grid/.babelrc new file mode 100644 index 0000000000000..40a198521b903 --- /dev/null +++ b/packages/kbn-securitysolution-t-grid/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-t-grid/.babelrc.browser b/packages/kbn-securitysolution-t-grid/.babelrc.browser new file mode 100644 index 0000000000000..71bbfbcd6eb2f --- /dev/null +++ b/packages/kbn-securitysolution-t-grid/.babelrc.browser @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/webpack_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-t-grid/BUILD.bazel b/packages/kbn-securitysolution-t-grid/BUILD.bazel index 0c5ef200fc965..f9a6a5d7934ad 100644 --- a/packages/kbn-securitysolution-t-grid/BUILD.bazel +++ b/packages/kbn-securitysolution-t-grid/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-securitysolution-t-grid" @@ -24,36 +25,37 @@ filegroup( ) NPM_MODULE_EXTRA_FILES = [ - "react/package.json", "package.json", "README.md", ] -SRC_DEPS = [ - "//packages/kbn-babel-preset", - "//packages/kbn-dev-utils", - "//packages/kbn-i18n", - "@npm//@babel/core", - "@npm//babel-loader", - "@npm//enzyme", +RUNTIME_DEPS = [ "@npm//jest", "@npm//lodash", - "@npm//react", "@npm//react-beautiful-dnd", "@npm//tslib", ] TYPES_DEPS = [ - "@npm//typescript", - "@npm//@types/enzyme", + "@npm//tslib", "@npm//@types/jest", "@npm//@types/lodash", "@npm//@types/node", - "@npm//@types/react", "@npm//@types/react-beautiful-dnd", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + config_file = ".babelrc.browser" +) ts_config( name = "tsconfig", @@ -64,49 +66,26 @@ ts_config( ], ) -ts_config( - name = "tsconfig_browser", - src = "tsconfig.browser.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.browser.json", - "//:tsconfig.browser_bazel.json", - ], -) - ts_project( - name = "tsc", + name = "tsc_types", args = ["--pretty"], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, declaration = True, - declaration_dir = "target_types", declaration_map = True, - out_dir = "target_node", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", ) -ts_project( - name = "tsc_browser", - args = ['--pretty'], - srcs = SRCS, - deps = DEPS, - allow_js = True, - declaration = False, - out_dir = "target_web", - source_map = True, - root_dir = "src", - tsconfig = ":tsconfig_browser", -) - js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = [":tsc", ":tsc_browser"] + DEPS, ) pkg_npm( diff --git a/packages/kbn-securitysolution-t-grid/package.json b/packages/kbn-securitysolution-t-grid/package.json index 68d3a8c71e7ca..92464f3246ecd 100644 --- a/packages/kbn-securitysolution-t-grid/package.json +++ b/packages/kbn-securitysolution-t-grid/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "security solution t-grid packages will allow sharing components between timelines and security_solution plugin until we transfer all functionality to timelines plugin", "license": "SSPL-1.0 OR Elastic License 2.0", - "browser": "./target_web/browser.js", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "types": "./target_types/index.d.ts", "private": true diff --git a/packages/kbn-securitysolution-t-grid/react/package.json b/packages/kbn-securitysolution-t-grid/react/package.json deleted file mode 100644 index c29ddd45f084d..0000000000000 --- a/packages/kbn-securitysolution-t-grid/react/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "browser": "../target_web/react", - "main": "../target_node/react", - "types": "../target_types/react/index.d.ts" -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-t-grid/tsconfig.browser.json b/packages/kbn-securitysolution-t-grid/tsconfig.browser.json deleted file mode 100644 index e951765c4b991..0000000000000 --- a/packages/kbn-securitysolution-t-grid/tsconfig.browser.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../tsconfig.browser_bazel.json", - "compilerOptions": { - "allowJs": true, - "outDir": "./target_web", - "declaration": false, - "isolatedModules": true, - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-securitysolution-t-grid/src", - "types": [ - "jest", - "node" - ], - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - ], - "exclude": [ - "**/__fixtures__/**/*" - ] -} diff --git a/packages/kbn-securitysolution-t-grid/tsconfig.json b/packages/kbn-securitysolution-t-grid/tsconfig.json index 1d5957e516e00..3c701d149ab2e 100644 --- a/packages/kbn-securitysolution-t-grid/tsconfig.json +++ b/packages/kbn-securitysolution-t-grid/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "declaration": true, "declarationMap": true, - "outDir": "target", + "emitDeclarationOnly": true, + "outDir": "target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-t-grid/src", diff --git a/packages/kbn-securitysolution-utils/.babelrc b/packages/kbn-securitysolution-utils/.babelrc new file mode 100644 index 0000000000000..40a198521b903 --- /dev/null +++ b/packages/kbn-securitysolution-utils/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"], + "ignore": ["**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/kbn-securitysolution-utils/BUILD.bazel b/packages/kbn-securitysolution-utils/BUILD.bazel index 41fb97bc6079e..c3d6b92044ef6 100644 --- a/packages/kbn-securitysolution-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-utils/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-securitysolution-utils" @@ -27,18 +28,23 @@ NPM_MODULE_EXTRA_FILES = [ "README.md", ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "@npm//tslib", "@npm//uuid", ] TYPES_DEPS = [ + "@npm//tslib", "@npm//@types/jest", "@npm//@types/node", "@npm//@types/uuid" ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -50,24 +56,25 @@ ts_config( ) ts_project( - name = "tsc", - srcs = SRCS, + name = "tsc_types", args = ["--pretty"], + srcs = SRCS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - out_dir = "target", + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", source_map = True, tsconfig = ":tsconfig", - deps = DEPS, ) js_library( name = PKG_BASE_NAME, - package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], - deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-utils/package.json b/packages/kbn-securitysolution-utils/package.json index d4b46ed07bfdd..98f19e33d379b 100644 --- a/packages/kbn-securitysolution-utils/package.json +++ b/packages/kbn-securitysolution-utils/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", "private": true } diff --git a/packages/kbn-securitysolution-utils/tsconfig.json b/packages/kbn-securitysolution-utils/tsconfig.json index 3894b53d6cff3..23fdf3178e174 100644 --- a/packages/kbn-securitysolution-utils/tsconfig.json +++ b/packages/kbn-securitysolution-utils/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "declaration": true, "declarationMap": true, - "outDir": "target", + "emitDeclarationOnly": true, + "outDir": "target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-securitysolution-utils/src", diff --git a/packages/kbn-typed-react-router-config/src/create_router.test.tsx b/packages/kbn-typed-react-router-config/src/create_router.test.tsx index d8f42c8714e8b..3fb37f813e2e1 100644 --- a/packages/kbn-typed-react-router-config/src/create_router.test.tsx +++ b/packages/kbn-typed-react-router-config/src/create_router.test.tsx @@ -201,6 +201,21 @@ describe('createRouter', () => { }, }); }); + + it('supports multiple paths', () => { + history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3'); + + const params = router.getParams('/services', '/service-map', history.location); + + expect(params).toEqual({ + path: {}, + query: { + maxNumNodes: 3, + rangeFrom: 'now-15m', + rangeTo: 'now', + }, + }); + }); }); describe('matchRoutes', () => { diff --git a/packages/kbn-typed-react-router-config/src/create_router.ts b/packages/kbn-typed-react-router-config/src/create_router.ts index 28f9e2774eb74..370d8b48e53b4 100644 --- a/packages/kbn-typed-react-router-config/src/create_router.ts +++ b/packages/kbn-typed-react-router-config/src/create_router.ts @@ -9,6 +9,7 @@ import { isLeft } from 'fp-ts/lib/Either'; import { Location } from 'history'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { + MatchedRoute, matchRoutes as matchRoutesConfig, RouteConfig as ReactRouterConfig, } from 'react-router-config'; @@ -49,33 +50,44 @@ export function createRouter(routes: TRoutes): Router { - let path: string = args[0]; - let location: Location = args[1]; - let optional: boolean = args[2]; - - if (args.length === 1) { - location = args[0] as Location; - path = location.pathname; - optional = args[1]; + let optional: boolean = false; + + if (typeof args[args.length - 1] === 'boolean') { + optional = args[args.length - 1]; + args.pop(); } - const greedy = path.endsWith('/*') || args.length === 1; + const location: Location = args[args.length - 1]; + args.pop(); + + let paths: string[] = args; - if (!path) { - path = '/'; + if (paths.length === 0) { + paths = [location.pathname || '/']; } - const matches = matchRoutesConfig(reactRouterConfigs, location.pathname); + let matches: Array> = []; + let matchIndex: number = -1; - const matchIndex = greedy - ? matches.length - 1 - : findLastIndex(matches, (match) => match.route.path === path); + for (const path of paths) { + const greedy = path.endsWith('/*') || args.length === 0; + matches = matchRoutesConfig(reactRouterConfigs, location.pathname); + + matchIndex = greedy + ? matches.length - 1 + : findLastIndex(matches, (match) => match.route.path === path); + + if (matchIndex !== -1) { + break; + } + matchIndex = -1; + } if (matchIndex === -1) { if (optional) { return []; } - throw new Error(`No matching route found for ${path}`); + throw new Error(`No matching route found for ${paths}`); } return matches.slice(0, matchIndex + 1).map((matchedRoute) => { diff --git a/packages/kbn-typed-react-router-config/src/types/index.ts b/packages/kbn-typed-react-router-config/src/types/index.ts index 0e02318c50aad..4d26d2879d5e7 100644 --- a/packages/kbn-typed-react-router-config/src/types/index.ts +++ b/packages/kbn-typed-react-router-config/src/types/index.ts @@ -134,6 +134,22 @@ export interface Router { location: Location, optional: TOptional ): TOptional extends true ? TypeOf | undefined : TypeOf; + getParams, T2 extends PathsOf>( + path1: T1, + path2: T2, + location: Location + ): TypeOf | TypeOf; + getParams, T2 extends PathsOf, T3 extends PathsOf>( + path1: T1, + path2: T2, + path3: T3, + location: Location + ): TypeOf | TypeOf | TypeOf; + getParams, TOptional extends boolean>( + path: TPath, + location: Location, + optional: TOptional + ): TOptional extends true ? TypeOf | undefined : TypeOf; link>( path: TPath, ...args: TypeAsArgs> diff --git a/packages/kbn-typed-react-router-config/src/use_params.ts b/packages/kbn-typed-react-router-config/src/use_params.ts index 94a5cf401c569..0468eb9566236 100644 --- a/packages/kbn-typed-react-router-config/src/use_params.ts +++ b/packages/kbn-typed-react-router-config/src/use_params.ts @@ -6,12 +6,26 @@ * Side Public License, v 1. */ +import { Location } from 'history'; import { useLocation } from 'react-router-dom'; import { useRouter } from './use_router'; -export function useParams(path: string, optional: boolean = false) { +export function useParams(...args: any[]) { const router = useRouter(); const location = useLocation(); - return router.getParams(path as never, location, optional); + let optional: boolean = false; + + const last: boolean | string | undefined = args[args.length - 1]; + + if (typeof last === 'boolean') { + optional = last; + args.pop(); + } + + const paths = args as string[]; + + const getParamsArgs = [...paths, location, optional] as [never, Location, boolean]; + + return router.getParams(...getParamsArgs); } diff --git a/packages/kbn-ui-shared-deps/.babelrc b/packages/kbn-ui-shared-deps/.babelrc new file mode 100644 index 0000000000000..7da72d1779128 --- /dev/null +++ b/packages/kbn-ui-shared-deps/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-ui-shared-deps/BUILD.bazel b/packages/kbn-ui-shared-deps/BUILD.bazel index 352fd48907345..8bc9555e640b5 100644 --- a/packages/kbn-ui-shared-deps/BUILD.bazel +++ b/packages/kbn-ui-shared-deps/BUILD.bazel @@ -1,6 +1,7 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") load("@npm//webpack-cli:index.bzl", webpack = "webpack_cli") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-ui-shared-deps" PKG_REQUIRE_NAME = "@kbn/ui-shared-deps" @@ -28,7 +29,7 @@ NPM_MODULE_EXTRA_FILES = [ "README.md" ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "//packages/elastic-datemath", "//packages/elastic-safer-lodash-set", "//packages/kbn-analytics", @@ -71,10 +72,53 @@ SRC_DEPS = [ ] TYPES_DEPS = [ + "//packages/elastic-datemath", + "//packages/elastic-safer-lodash-set", + "//packages/kbn-analytics", + "//packages/kbn-babel-preset", + "//packages/kbn-i18n", + "//packages/kbn-monaco", + "//packages/kbn-std", + "//packages/kbn-utils", + "@npm//@elastic/charts", + "@npm//@elastic/eui", + "@npm//@elastic/numeral", + "@npm//@emotion/react", + "@npm//abortcontroller-polyfill", + "@npm//angular", + "@npm//babel-loader", + "@npm//core-js", + "@npm//css-loader", + "@npm//fflate", + "@npm//jquery", + "@npm//loader-utils", + "@npm//mini-css-extract-plugin", + "@npm//moment", + "@npm//moment-timezone", + "@npm//raw-loader", + "@npm//react", + "@npm//react-dom", + "@npm//react-intl", + "@npm//react-is", + "@npm//react-router", + "@npm//react-router-dom", + "@npm//regenerator-runtime", + "@npm//resize-observer-polyfill", + "@npm//rison-node", + "@npm//rxjs", + "@npm//styled-components", + "@npm//symbol-observable", + "@npm//url-loader", + "@npm//val-loader", + "@npm//whatwg-fetch", "@npm//@types/node", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -86,22 +130,23 @@ ts_config( ) ts_project( - name = "tsc", + name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, allow_js = True, declaration = True, declaration_map = True, - out_dir = "target", - source_map = True, + emit_declaration_only = True, + out_dir = "target_types", root_dir = "src", + source_map = True, tsconfig = ":tsconfig", ) webpack( name = "shared_built_assets", - data = DEPS + [ + data = RUNTIME_DEPS + [ "//:package.json", ":srcs", ":tsconfig", @@ -120,7 +165,7 @@ webpack( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = DEPS + [":tsc", ":shared_built_assets"], + deps = RUNTIME_DEPS + [":target_node", ":tsc_types", ":shared_built_assets"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ui-shared-deps/flot_charts/package.json b/packages/kbn-ui-shared-deps/flot_charts/package.json index 03d7ac348fcb9..6c2f62447daf5 100644 --- a/packages/kbn-ui-shared-deps/flot_charts/package.json +++ b/packages/kbn-ui-shared-deps/flot_charts/package.json @@ -1,4 +1,4 @@ { - "main": "../target/flot_charts/index.js", - "types": "../target/flot_charts/index.d.ts" + "main": "../target_node/flot_charts/index.js", + "types": "../target_types/flot_charts/index.d.ts" } \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 5ec32ca059aa1..f360d37db11c8 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "target/index.js", - "types": "target/index.d.ts" + "main": "target_node/index.js", + "types": "target_types/index.d.ts" } \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/theme/package.json b/packages/kbn-ui-shared-deps/theme/package.json index 2d41937701a29..37d60f83b18e9 100644 --- a/packages/kbn-ui-shared-deps/theme/package.json +++ b/packages/kbn-ui-shared-deps/theme/package.json @@ -1,4 +1,4 @@ { - "main": "../target/theme.js", - "types": "../target/theme.d.ts" + "main": "../target_node/theme.js", + "types": "../target_types/theme.d.ts" } \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/tsconfig.json b/packages/kbn-ui-shared-deps/tsconfig.json index 90a89ac580a40..81a8a6b200ada 100644 --- a/packages/kbn-ui-shared-deps/tsconfig.json +++ b/packages/kbn-ui-shared-deps/tsconfig.json @@ -2,9 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "allowJs": true, - "outDir": "./target/types", "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "./target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-ui-shared-deps/src", diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index 5e29218250fb9..347b81abf6d51 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -19,7 +19,6 @@ const createStartContractMock = () => { has: jest.fn(), get: jest.fn(), getAll: jest.fn(), - showOnly: jest.fn(), enableForcedAppSwitcherNavigation: jest.fn(), getForceAppSwitcherNavigation$: jest.fn(), }, diff --git a/src/core/public/chrome/nav_links/nav_links_service.test.ts b/src/core/public/chrome/nav_links/nav_links_service.test.ts index e1d537da6959b..294a6de914a42 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.test.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.test.ts @@ -89,10 +89,8 @@ describe('NavLinksService', () => { const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id))); const emittedLinks: string[][] = []; navLinkIds$.subscribe((r) => emittedLinks.push(r)); - start.showOnly('app1'); - service.stop(); - expect(emittedLinks).toEqual([['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1'], ['app1']]); + expect(emittedLinks).toEqual([['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']]); }); it('completes when service is stopped', async () => { @@ -133,74 +131,6 @@ describe('NavLinksService', () => { }); }); - describe('#showOnly()', () => { - it('does nothing if link does not exist', async () => { - start.showOnly('fake'); - expect( - await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.map((l) => l.id)) - ) - .toPromise() - ).toEqual(['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']); - }); - - it('does nothing on chromeless applications', async () => { - start.showOnly('chromelessApp'); - expect( - await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.map((l) => l.id)) - ) - .toPromise() - ).toEqual(['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']); - }); - - it('removes all other links', async () => { - start.showOnly('app2'); - expect( - await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.map((l) => l.id)) - ) - .toPromise() - ).toEqual(['app2']); - }); - - it('show only deep link', async () => { - start.showOnly('app2:deepApp1'); - expect( - await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.map((l) => l.id)) - ) - .toPromise() - ).toEqual(['app2:deepApp1']); - }); - - it('still removes all other links when availableApps are re-emitted', async () => { - start.showOnly('app2'); - mockAppService.applications$.next(mockAppService.applications$.value); - expect( - await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.map((l) => l.id)) - ) - .toPromise() - ).toEqual(['app2']); - }); - }); - describe('#enableForcedAppSwitcherNavigation()', () => { it('flips #getForceAppSwitcherNavigation$()', async () => { await expect(start.getForceAppSwitcherNavigation$().pipe(take(1)).toPromise()).resolves.toBe( diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index af961987a6309..6a91d4d179076 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -7,7 +7,7 @@ */ import { sortBy } from 'lodash'; -import { BehaviorSubject, combineLatest, Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { InternalApplicationStart, PublicAppDeepLinkInfo, PublicAppInfo } from '../../application'; @@ -48,16 +48,6 @@ export interface ChromeNavLinks { */ has(id: string): boolean; - /** - * Remove all navlinks except the one matching the given id. - * - * @remarks - * NOTE: this is not reversible. - * - * @param id - */ - showOnly(id: string): void; - /** * Enable forced navigation mode, which will trigger a page refresh * when a nav link is clicked and only the hash is updated. @@ -78,39 +68,25 @@ export interface ChromeNavLinks { getForceAppSwitcherNavigation$(): Observable; } -type LinksUpdater = (navLinks: Map) => Map; - export class NavLinksService { private readonly stop$ = new ReplaySubject(1); public start({ application, http }: StartDeps): ChromeNavLinks { - const appLinks$ = application.applications$.pipe( - map((apps) => { - return new Map( - [...apps] - .filter(([, app]) => !app.chromeless) - .reduce((navLinks: Array<[string, NavLinkWrapper]>, [appId, app]) => { - navLinks.push( - [appId, toNavLink(app, http.basePath)], - ...toNavDeepLinks(app, app.deepLinks, http.basePath) - ); - return navLinks; - }, []) - ); - }) - ); - - // now that availableApps$ is an observable, we need to keep record of all - // manual link modifications to be able to re-apply then after every - // availableApps$ changes. - // Only in use by `showOnly` API, can be removed once dashboard_mode is removed in 8.0 - const linkUpdaters$ = new BehaviorSubject([]); const navLinks$ = new BehaviorSubject>(new Map()); - - combineLatest([appLinks$, linkUpdaters$]) + application.applications$ .pipe( - map(([appLinks, linkUpdaters]) => { - return linkUpdaters.reduce((links, updater) => updater(links), appLinks); + map((apps) => { + return new Map( + [...apps] + .filter(([, app]) => !app.chromeless) + .reduce((navLinks: Array<[string, NavLinkWrapper]>, [appId, app]) => { + navLinks.push( + [appId, toNavLink(app, http.basePath)], + ...toNavDeepLinks(app, app.deepLinks, http.basePath) + ); + return navLinks; + }, []) + ); }) ) .subscribe((navlinks) => { @@ -137,17 +113,6 @@ export class NavLinksService { return navLinks$.value.has(id); }, - showOnly(id: string) { - if (!this.has(id)) { - return; - } - - const updater: LinksUpdater = (navLinks) => - new Map([...navLinks.entries()].filter(([linkId]) => linkId === id)); - - linkUpdaters$.next([...linkUpdaters$.value, updater]); - }, - enableForcedAppSwitcherNavigation() { forceAppSwitcherNavigation$.next(true); }, diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index 1f932d62c94b9..4ef5eb8f56d2f 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -62,11 +62,11 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiColumnSorting.emptySorting": "Currently no fields are sorted", "euiColumnSorting.pickFields": "Pick fields to sort by", "euiColumnSorting.sortFieldAriaLabel": "Sort by:", - "euiColumnSortingDraggable.activeSortLabel": "is sorting this data grid", + "euiColumnSortingDraggable.activeSortLabel": [Function], "euiColumnSortingDraggable.defaultSortAsc": "A-Z", "euiColumnSortingDraggable.defaultSortDesc": "Z-A", - "euiColumnSortingDraggable.removeSortLabel": "Remove from data grid sort:", - "euiColumnSortingDraggable.toggleLegend": "Select sorting method for field:", + "euiColumnSortingDraggable.removeSortLabel": [Function], + "euiColumnSortingDraggable.toggleLegend": [Function], "euiComboBoxOptionsList.allOptionsSelected": "You've selected all available options", "euiComboBoxOptionsList.alreadyAdded": [Function], "euiComboBoxOptionsList.createCustomOption": [Function], @@ -80,16 +80,13 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiControlBar.screenReaderAnnouncement": "There is a new region landmark with page level controls at the end of the document.", "euiControlBar.screenReaderHeading": "Page level controls", "euiDataGrid.ariaLabel": [Function], - "euiDataGrid.ariaLabelGridPagination": [Function], "euiDataGrid.ariaLabelledBy": [Function], - "euiDataGrid.ariaLabelledByGridPagination": "Pagination for preceding grid", - "euiDataGrid.fullScreenButton": "Full screen", - "euiDataGrid.fullScreenButtonActive": "Exit full screen", "euiDataGrid.screenReaderNotice": "Cell contains interactive content.", - "euiDataGridCell.column": "Column", - "euiDataGridCell.row": "Row", + "euiDataGridCell.position": [Function], "euiDataGridCellButtons.expandButtonTitle": "Click or hit enter to interact with cell content", "euiDataGridHeaderCell.headerActions": "Header actions", + "euiDataGridPagination.detailedPaginationLabel": [Function], + "euiDataGridPagination.paginationLabel": "Pagination for preceding grid", "euiDataGridSchema.booleanSortTextAsc": "False-True", "euiDataGridSchema.booleanSortTextDesc": "True-False", "euiDataGridSchema.currencySortTextAsc": "Low-High", @@ -100,6 +97,8 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiDataGridSchema.jsonSortTextDesc": "Large-Small", "euiDataGridSchema.numberSortTextAsc": "Low-High", "euiDataGridSchema.numberSortTextDesc": "High-Low", + "euiDataGridToolbar.fullScreenButton": "Full screen", + "euiDataGridToolbar.fullScreenButtonActive": "Exit full screen", "euiDatePopoverButton.invalidTitle": [Function], "euiDatePopoverButton.outdatedTitle": [Function], "euiFieldPassword.maskPassword": "Mask password", diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 98b3fa8f81211..133a2155f7430 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -275,12 +275,11 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiColumnSorting.buttonActive': i18n.translate('core.euiColumnSorting.buttonActive', { defaultMessage: 'fields sorted', }), - 'euiColumnSortingDraggable.activeSortLabel': i18n.translate( - 'core.euiColumnSortingDraggable.activeSortLabel', - { - defaultMessage: 'is sorting this data grid', - } - ), + 'euiColumnSortingDraggable.activeSortLabel': ({ display }: EuiValues) => + i18n.translate('core.euiColumnSortingDraggable.activeSortLabel', { + defaultMessage: '{display} is sorting this data grid', + values: { display }, + }), 'euiColumnSortingDraggable.defaultSortAsc': i18n.translate( 'core.euiColumnSortingDraggable.defaultSortAsc', { @@ -295,18 +294,16 @@ export const getEuiContextMapping = (): EuiTokensObject => { description: 'Descending sort label', } ), - 'euiColumnSortingDraggable.removeSortLabel': i18n.translate( - 'core.euiColumnSortingDraggable.removeSortLabel', - { - defaultMessage: 'Remove from data grid sort:', - } - ), - 'euiColumnSortingDraggable.toggleLegend': i18n.translate( - 'core.euiColumnSortingDraggable.toggleLegend', - { - defaultMessage: 'Select sorting method for field:', - } - ), + 'euiColumnSortingDraggable.removeSortLabel': ({ display }: EuiValues) => + i18n.translate('core.euiColumnSortingDraggable.removeSortLabel', { + defaultMessage: 'Remove {display} from data grid sort', + values: { display }, + }), + 'euiColumnSortingDraggable.toggleLegend': ({ display }: EuiValues) => + i18n.translate('core.euiColumnSortingDraggable.toggleLegend', { + defaultMessage: 'Select sorting method for {display}', + values: { display }, + }), 'euiComboBoxOptionsList.allOptionsSelected': i18n.translate( 'core.euiComboBoxOptionsList.allOptionsSelected', { @@ -381,19 +378,6 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiDataGrid.screenReaderNotice': i18n.translate('core.euiDataGrid.screenReaderNotice', { defaultMessage: 'Cell contains interactive content.', }), - 'euiDataGrid.ariaLabelGridPagination': ({ label }: EuiValues) => - i18n.translate('core.euiDataGrid.ariaLabelGridPagination', { - defaultMessage: 'Pagination for preceding grid: {label}', - values: { label }, - description: 'Screen reader text to describe the pagination controls', - }), - 'euiDataGrid.ariaLabelledByGridPagination': i18n.translate( - 'core.euiDataGrid.ariaLabelledByGridPagination', - { - defaultMessage: 'Pagination for preceding grid', - description: 'Screen reader text to describe the pagination controls', - } - ), 'euiDataGrid.ariaLabel': ({ label, page, pageCount }: EuiValues) => i18n.translate('core.euiDataGrid.ariaLabel', { defaultMessage: '{label}; Page {page} of {pageCount}.', @@ -406,21 +390,11 @@ export const getEuiContextMapping = (): EuiTokensObject => { values: { page, pageCount }, description: 'Screen reader text to describe the size of the data grid', }), - 'euiDataGrid.fullScreenButton': i18n.translate('core.euiDataGrid.fullScreenButton', { - defaultMessage: 'Full screen', - }), - 'euiDataGrid.fullScreenButtonActive': i18n.translate( - 'core.euiDataGrid.fullScreenButtonActive', - { - defaultMessage: 'Exit full screen', - } - ), - 'euiDataGridCell.row': i18n.translate('core.euiDataGridCell.row', { - defaultMessage: 'Row', - }), - 'euiDataGridCell.column': i18n.translate('core.euiDataGridCell.column', { - defaultMessage: 'Column', - }), + 'euiDataGridCell.position': ({ row, col }: EuiValues) => + i18n.translate('core.euiDataGridCell.position', { + defaultMessage: 'Row: {row}; Column: {col}', + values: { row, col }, + }), 'euiDataGridCellButtons.expandButtonTitle': i18n.translate( 'core.euiDataGridCellButtons.expandButtonTitle', { @@ -433,6 +407,17 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Header actions', } ), + 'euiDataGridPagination.detailedPaginationLabel': ({ label }: EuiValues) => + i18n.translate('core.euiDataGridPagination.detailedPaginationLabel', { + defaultMessage: 'Pagination for preceding grid: {label}', + values: { label }, + }), + 'euiDataGridPagination.paginationLabel': i18n.translate( + 'core.euiDataGridPagination.paginationLabel', + { + defaultMessage: 'Pagination for preceding grid', + } + ), 'euiDataGridSchema.booleanSortTextAsc': i18n.translate( 'core.euiDataGridSchema.booleanSortTextAsc', { @@ -497,6 +482,18 @@ export const getEuiContextMapping = (): EuiTokensObject => { description: 'Descending size label', } ), + 'euiDataGridToolbar.fullScreenButton': i18n.translate( + 'core.euiDataGridToolbar.fullScreenButton', + { + defaultMessage: 'Full screen', + } + ), + 'euiDataGridToolbar.fullScreenButtonActive': i18n.translate( + 'core.euiDataGridToolbar.fullScreenButtonActive', + { + defaultMessage: 'Exit full screen', + } + ), 'euiDatePopoverButton.invalidTitle': ({ title }: EuiValues) => i18n.translate('core.euiDatePopoverButton.invalidTitle', { defaultMessage: 'Invalid date: {title}', diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 2217b71d2f1a3..043759378faa3 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -324,7 +324,6 @@ export interface ChromeNavLinks { getForceAppSwitcherNavigation$(): Observable; getNavLinks$(): Observable>>; has(id: string): boolean; - showOnly(id: string): void; } // @public diff --git a/src/core/server/core_app/core_app.test.ts b/src/core/server/core_app/core_app.test.ts index f6a9b653ec034..e5c3a592a72c7 100644 --- a/src/core/server/core_app/core_app.test.ts +++ b/src/core/server/core_app/core_app.test.ts @@ -137,7 +137,7 @@ describe('CoreApp', () => { mockResponseFactory ); - expect(mockResponseFactory.renderAnonymousCoreApp).toHaveBeenCalled(); + expect(mockResponseFactory.renderCoreApp).toHaveBeenCalled(); }); }); diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts index 35a7c57b67610..23ad78ca46d45 100644 --- a/src/core/server/core_app/core_app.ts +++ b/src/core/server/core_app/core_app.ts @@ -64,7 +64,7 @@ export class CoreApp { httpResources: corePreboot.httpResources.createRegistrar(router), router, uiPlugins, - onResourceNotFound: (res) => res.renderAnonymousCoreApp(), + onResourceNotFound: (res) => res.renderCoreApp(), }); }); } diff --git a/src/core/server/elasticsearch/client/client_config.test.ts b/src/core/server/elasticsearch/client/client_config.test.ts index 7e16339b40235..7956bcc64ea2f 100644 --- a/src/core/server/elasticsearch/client/client_config.test.ts +++ b/src/core/server/elasticsearch/client/client_config.test.ts @@ -163,6 +163,12 @@ describe('parseClientOptions', () => { ] `); }); + + it('`caFingerprint` option', () => { + const options = parseClientOptions(createConfig({ caFingerprint: 'ab:cd:ef' }), false); + + expect(options.caFingerprint).toBe('ab:cd:ef'); + }); }); describe('authorization', () => { diff --git a/src/core/server/elasticsearch/client/client_config.ts b/src/core/server/elasticsearch/client/client_config.ts index bbbb1ac247b3b..27d6f877a5572 100644 --- a/src/core/server/elasticsearch/client/client_config.ts +++ b/src/core/server/elasticsearch/client/client_config.ts @@ -35,6 +35,7 @@ export type ElasticsearchClientConfig = Pick< requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout']; ssl?: Partial; keepAlive?: boolean; + caFingerprint?: ClientOptions['caFingerprint']; }; /** @@ -96,6 +97,10 @@ export function parseClientOptions( ); } + if (config.caFingerprint != null) { + clientOptions.caFingerprint = config.caFingerprint; + } + return clientOptions; } diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index ef5e151083780..4cb1bc9867d2c 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -88,6 +88,7 @@ const createInternalPrebootContractMock = () => { csp: CspConfig.DEFAULT, externalUrl: ExternalUrlConfig.DEFAULT, auth: createAuthMock(), + getServerInfo: jest.fn(), }; return mock; }; @@ -98,6 +99,7 @@ const createPrebootContractMock = () => { const mock: HttpServicePrebootMock = { registerRoutes: internalMock.registerRoutes, basePath: createBasePathMock(), + getServerInfo: jest.fn(), }; return mock; diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index 8d29e3221a2ca..4955d19668580 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -379,6 +379,7 @@ test('returns `preboot` http server contract on preboot', async () => { auth: Symbol('auth'), basePath: Symbol('basePath'), csp: Symbol('csp'), + getServerInfo: jest.fn(), }; mockHttpServer.mockImplementation(() => ({ @@ -397,6 +398,7 @@ test('returns `preboot` http server contract on preboot', async () => { registerRouteHandlerContext: expect.any(Function), registerRoutes: expect.any(Function), registerStaticDir: expect.any(Function), + getServerInfo: expect.any(Function), }); }); diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 4b9e45e271be2..538a4c065e997 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -128,6 +128,7 @@ export class HttpService prebootSetup.registerRouterAfterListening(router); }, + getServerInfo: prebootSetup.getServerInfo, }; return this.internalPreboot; diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 5bea371d479ae..a3e872ee3ea87 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -17,7 +17,7 @@ import { loggingSystemMock } from '../../logging/logging_system.mock'; import { createHttpServer } from '../test_utils'; import { HttpService } from '../http_service'; import { Router } from '../router'; -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; let server: HttpService; let logger: ReturnType; diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 7353f48b47194..89d0d72017082 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -142,6 +142,11 @@ export interface HttpServicePreboot { * See {@link IBasePath}. */ basePath: IBasePath; + + /** + * Provides common {@link HttpServerInfo | information} about the running preboot http server. + */ + getServerInfo: () => HttpServerInfo; } /** @internal */ @@ -155,6 +160,7 @@ export interface InternalHttpServicePreboot | 'registerStaticDir' | 'registerRouteHandlerContext' | 'server' + | 'getServerInfo' > { registerRoutes(path: string, callback: (router: IRouter) => void): void; } diff --git a/src/core/server/logging/logger.mock.ts b/src/core/server/logging/logger.mock.ts index efab15b7bf5f4..cfabaeb72adf7 100644 --- a/src/core/server/logging/logger.mock.ts +++ b/src/core/server/logging/logger.mock.ts @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -export { loggerMock } from '@kbn/logging/target/mocks'; -export type { MockedLogger } from '@kbn/logging/target/mocks'; +export { loggerMock } from '@kbn/logging/mocks'; +export type { MockedLogger } from '@kbn/logging/mocks'; diff --git a/src/core/server/metrics/collectors/cgroup.test.ts b/src/core/server/metrics/collectors/cgroup.test.ts index 298a143717d84..269437f026f2f 100644 --- a/src/core/server/metrics/collectors/cgroup.test.ts +++ b/src/core/server/metrics/collectors/cgroup.test.ts @@ -7,7 +7,7 @@ */ import mockFs from 'mock-fs'; -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { OsCgroupMetricsCollector } from './cgroup'; describe('OsCgroupMetricsCollector', () => { diff --git a/src/core/server/metrics/collectors/os.test.ts b/src/core/server/metrics/collectors/os.test.ts index 37373ea14c339..5592038f1416a 100644 --- a/src/core/server/metrics/collectors/os.test.ts +++ b/src/core/server/metrics/collectors/os.test.ts @@ -8,7 +8,7 @@ jest.mock('getos', () => (cb: Function) => cb(null, { dist: 'distrib', release: 'release' })); -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import os from 'os'; import { cgroupCollectorMock } from './os.test.mocks'; import { OsMetricsCollector } from './os'; diff --git a/src/core/server/metrics/ops_metrics_collector.test.ts b/src/core/server/metrics/ops_metrics_collector.test.ts index e966c7e0a8095..3faa771db1dae 100644 --- a/src/core/server/metrics/ops_metrics_collector.test.ts +++ b/src/core/server/metrics/ops_metrics_collector.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { mockOsCollector, mockProcessCollector, diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index b972c6078ca2b..cbefdae525180 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -115,6 +115,7 @@ export function createPluginPrebootSetupContext( http: { registerRoutes: deps.http.registerRoutes, basePath: deps.http.basePath, + getServerInfo: deps.http.getServerInfo, }, preboot: { isSetupOnHold: deps.preboot.isSetupOnHold, diff --git a/src/core/server/saved_objects/service/lib/point_in_time_finder.test.ts b/src/core/server/saved_objects/service/lib/point_in_time_finder.test.ts index 044bb45269538..160852f9160b7 100644 --- a/src/core/server/saved_objects/service/lib/point_in_time_finder.test.ts +++ b/src/core/server/saved_objects/service/lib/point_in_time_finder.test.ts @@ -7,7 +7,6 @@ */ import { loggerMock, MockedLogger } from '../../../logging/logger.mock'; -import type { SavedObjectsClientContract } from '../../types'; import type { SavedObjectsFindResult } from '../'; import { savedObjectsRepositoryMock } from './repository.mock'; @@ -43,37 +42,67 @@ const mockHits = [ describe('createPointInTimeFinder()', () => { let logger: MockedLogger; - let find: jest.Mocked['find']; - let openPointInTimeForType: jest.Mocked['openPointInTimeForType']; - let closePointInTime: jest.Mocked['closePointInTime']; + let repository: ReturnType; beforeEach(() => { logger = loggerMock.create(); - const mockRepository = savedObjectsRepositoryMock.create(); - find = mockRepository.find; - openPointInTimeForType = mockRepository.openPointInTimeForType; - closePointInTime = mockRepository.closePointInTime; + repository = savedObjectsRepositoryMock.create(); }); describe('#find', () => { - test('throws if a PIT is already open', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + test('opens a PIT with the correct parameters', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValue({ total: 2, saved_objects: mockHits, pit_id: 'abc123', per_page: 1, page: 0, }); - find.mockResolvedValueOnce({ - total: 2, - saved_objects: mockHits, - pit_id: 'abc123', - per_page: 1, - page: 1, + + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { + type: ['visualization'], + search: 'foo*', + perPage: 1, + namespaces: ['ns1', 'ns2'], + }; + + const finder = new PointInTimeFinder(findOptions, { + logger, + client: repository, + }); + + expect(repository.openPointInTimeForType).not.toHaveBeenCalled(); + + await finder.find().next(); + + expect(repository.openPointInTimeForType).toHaveBeenCalledTimes(1); + expect(repository.openPointInTimeForType).toHaveBeenCalledWith(findOptions.type, { + namespaces: findOptions.namespaces, }); + }); + + test('throws if a PIT is already open', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ + id: 'abc123', + }); + repository.find + .mockResolvedValueOnce({ + total: 2, + saved_objects: mockHits, + pit_id: 'abc123', + per_page: 1, + page: 0, + }) + .mockResolvedValueOnce({ + total: 2, + saved_objects: mockHits, + pit_id: 'abc123', + per_page: 1, + page: 1, + }); const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { type: ['visualization'], @@ -83,30 +112,25 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); await finder.find().next(); - expect(find).toHaveBeenCalledTimes(1); - find.mockClear(); + expect(repository.find).toHaveBeenCalledTimes(1); expect(async () => { await finder.find().next(); }).rejects.toThrowErrorMatchingInlineSnapshot( `"Point In Time has already been opened for this finder instance. Please call \`close()\` before calling \`find()\` again."` ); - expect(find).toHaveBeenCalledTimes(0); + expect(repository.find).toHaveBeenCalledTimes(1); }); test('works with a single page of results', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValueOnce({ total: 2, saved_objects: mockHits, pit_id: 'abc123', @@ -121,11 +145,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const hits: SavedObjectsFindResult[] = []; for await (const result of finder.find()) { @@ -133,10 +153,10 @@ describe('createPointInTimeFinder()', () => { } expect(hits.length).toBe(2); - expect(openPointInTimeForType).toHaveBeenCalledTimes(1); - expect(closePointInTime).toHaveBeenCalledTimes(1); - expect(find).toHaveBeenCalledTimes(1); - expect(find).toHaveBeenCalledWith( + expect(repository.openPointInTimeForType).toHaveBeenCalledTimes(1); + expect(repository.closePointInTime).toHaveBeenCalledTimes(1); + expect(repository.find).toHaveBeenCalledTimes(1); + expect(repository.find).toHaveBeenCalledWith( expect.objectContaining({ pit: expect.objectContaining({ id: 'abc123', keepAlive: '2m' }), sortField: 'updated_at', @@ -147,24 +167,25 @@ describe('createPointInTimeFinder()', () => { }); test('works with multiple pages of results', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', }); - find.mockResolvedValueOnce({ - total: 2, - saved_objects: [mockHits[0]], - pit_id: 'abc123', - per_page: 1, - page: 0, - }); - find.mockResolvedValueOnce({ - total: 2, - saved_objects: [mockHits[1]], - pit_id: 'abc123', - per_page: 1, - page: 0, - }); - find.mockResolvedValueOnce({ + repository.find + .mockResolvedValueOnce({ + total: 2, + saved_objects: [mockHits[0]], + pit_id: 'abc123', + per_page: 1, + page: 0, + }) + .mockResolvedValueOnce({ + total: 2, + saved_objects: [mockHits[1]], + pit_id: 'abc123', + per_page: 1, + page: 0, + }); + repository.find.mockResolvedValueOnce({ total: 2, saved_objects: [], per_page: 1, @@ -180,11 +201,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const hits: SavedObjectsFindResult[] = []; for await (const result of finder.find()) { @@ -192,12 +209,12 @@ describe('createPointInTimeFinder()', () => { } expect(hits.length).toBe(2); - expect(openPointInTimeForType).toHaveBeenCalledTimes(1); - expect(closePointInTime).toHaveBeenCalledTimes(1); + expect(repository.openPointInTimeForType).toHaveBeenCalledTimes(1); + expect(repository.closePointInTime).toHaveBeenCalledTimes(1); // called 3 times since we need a 3rd request to check if we // are done paginating through results. - expect(find).toHaveBeenCalledTimes(3); - expect(find).toHaveBeenCalledWith( + expect(repository.find).toHaveBeenCalledTimes(3); + expect(repository.find).toHaveBeenCalledWith( expect.objectContaining({ pit: expect.objectContaining({ id: 'abc123', keepAlive: '2m' }), sortField: 'updated_at', @@ -210,10 +227,10 @@ describe('createPointInTimeFinder()', () => { describe('#close', () => { test('calls closePointInTime with correct ID', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'test', }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValueOnce({ total: 1, saved_objects: [mockHits[0]], pit_id: 'test', @@ -229,11 +246,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const hits: SavedObjectsFindResult[] = []; for await (const result of finder.find()) { @@ -241,28 +254,28 @@ describe('createPointInTimeFinder()', () => { await finder.close(); } - expect(closePointInTime).toHaveBeenCalledWith('test'); + expect(repository.closePointInTime).toHaveBeenCalledWith('test'); }); test('causes generator to stop', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'test', }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValueOnce({ total: 2, saved_objects: [mockHits[0]], pit_id: 'test', per_page: 1, page: 0, }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValueOnce({ total: 2, saved_objects: [mockHits[1]], pit_id: 'test', per_page: 1, page: 0, }); - find.mockResolvedValueOnce({ + repository.find.mockResolvedValueOnce({ total: 2, saved_objects: [], per_page: 1, @@ -278,11 +291,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const hits: SavedObjectsFindResult[] = []; for await (const result of finder.find()) { @@ -290,15 +299,15 @@ describe('createPointInTimeFinder()', () => { await finder.close(); } - expect(closePointInTime).toHaveBeenCalledTimes(1); + expect(repository.closePointInTime).toHaveBeenCalledTimes(1); expect(hits.length).toBe(1); }); test('is called if `find` throws an error', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'test', }); - find.mockRejectedValueOnce(new Error('oops')); + repository.find.mockRejectedValueOnce(new Error('oops')); const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { type: ['visualization'], @@ -308,11 +317,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const hits: SavedObjectsFindResult[] = []; try { @@ -323,27 +328,28 @@ describe('createPointInTimeFinder()', () => { // intentionally empty } - expect(closePointInTime).toHaveBeenCalledWith('test'); + expect(repository.closePointInTime).toHaveBeenCalledWith('test'); }); test('finder can be reused after closing', async () => { - openPointInTimeForType.mockResolvedValueOnce({ + repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', }); - find.mockResolvedValueOnce({ - total: 2, - saved_objects: mockHits, - pit_id: 'abc123', - per_page: 1, - page: 0, - }); - find.mockResolvedValueOnce({ - total: 2, - saved_objects: mockHits, - pit_id: 'abc123', - per_page: 1, - page: 1, - }); + repository.find + .mockResolvedValueOnce({ + total: 2, + saved_objects: mockHits, + pit_id: 'abc123', + per_page: 1, + page: 0, + }) + .mockResolvedValueOnce({ + total: 2, + saved_objects: mockHits, + pit_id: 'abc123', + per_page: 1, + page: 1, + }); const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { type: ['visualization'], @@ -353,11 +359,7 @@ describe('createPointInTimeFinder()', () => { const finder = new PointInTimeFinder(findOptions, { logger, - client: { - find, - openPointInTimeForType, - closePointInTime, - }, + client: repository, }); const findA = finder.find(); @@ -370,9 +372,9 @@ describe('createPointInTimeFinder()', () => { expect((await findA.next()).done).toBe(true); expect((await findB.next()).done).toBe(true); - expect(openPointInTimeForType).toHaveBeenCalledTimes(2); - expect(find).toHaveBeenCalledTimes(2); - expect(closePointInTime).toHaveBeenCalledTimes(2); + expect(repository.openPointInTimeForType).toHaveBeenCalledTimes(2); + expect(repository.find).toHaveBeenCalledTimes(2); + expect(repository.closePointInTime).toHaveBeenCalledTimes(2); }); }); }); diff --git a/src/core/server/saved_objects/service/lib/point_in_time_finder.ts b/src/core/server/saved_objects/service/lib/point_in_time_finder.ts index f0ed943c585e5..d11be250ad0a9 100644 --- a/src/core/server/saved_objects/service/lib/point_in_time_finder.ts +++ b/src/core/server/saved_objects/service/lib/point_in_time_finder.ts @@ -139,7 +139,9 @@ export class PointInTimeFinder private async open() { try { - const { id } = await this.#client.openPointInTimeForType(this.#findOptions.type); + const { id } = await this.#client.openPointInTimeForType(this.#findOptions.type, { + namespaces: this.#findOptions.namespaces, + }); this.#pitId = id; this.#open = true; } catch (e) { diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index 00d47d8d1fb03..1564df2969ecc 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -334,7 +334,7 @@ export interface SavedObjectsResolveResponse { /** * @public */ -export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOptions { +export interface SavedObjectsOpenPointInTimeOptions { /** * Optionally specify how long ES should keep the PIT alive until the next request. Defaults to `5m`. */ @@ -343,6 +343,15 @@ export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOpti * An optional ES preference value to be used for the query. */ preference?: string; + /** + * An optional list of namespaces to be used when opening the PIT. + * + * When the spaces plugin is enabled: + * - this will default to the user's current space (as determined by the URL) + * - if specified, the user's current space will be ignored + * - `['*']` will search across all available spaces + */ + namespaces?: string[]; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 47455e0c14316..67b08f4c0d9b7 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -807,6 +807,7 @@ export type ElasticsearchClientConfig = Pick; keepAlive?: boolean; + caFingerprint?: ClientOptions['caFingerprint']; }; // @public @@ -1003,6 +1004,7 @@ export interface HttpServerInfo { // @public export interface HttpServicePreboot { basePath: IBasePath; + getServerInfo: () => HttpServerInfo; registerRoutes(path: string, callback: (router: IRouter) => void): void; } @@ -2460,8 +2462,9 @@ export interface SavedObjectsMigrationVersion { export type SavedObjectsNamespaceType = 'single' | 'multiple' | 'multiple-isolated' | 'agnostic'; // @public (undocumented) -export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOptions { +export interface SavedObjectsOpenPointInTimeOptions { keepAlive?: string; + namespaces?: string[]; preference?: string; } diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 9c81d077b5b1b..cb7e3781e2511 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -75,7 +75,7 @@ export const LICENSE_OVERRIDES = { '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint 'node-sql-parser@3.6.1': ['(GPL-2.0 OR MIT)'], // GPL-2.0* https://github.com/taozhi8833998/node-sql-parser '@elastic/ems-client@7.15.0': ['Elastic License 2.0'], - '@elastic/eui@37.1.1': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@37.3.0': ['SSPL-1.0 OR Elastic License 2.0'], // TODO can be removed if the https://github.com/jindw/xmldom/issues/239 is released 'xmldom@0.1.27': ['MIT'], diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 0244cb2cd9115..e3d8185e73e55 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -70,6 +70,8 @@ export const PROJECTS = [ ...findProjects('packages/*/tsconfig.json'), ...findProjects('src/plugins/*/tsconfig.json'), + ...findProjects('src/plugins/chart_expressions/*/tsconfig.json'), + ...findProjects('src/plugins/vis_types/*/tsconfig.json'), ...findProjects('x-pack/plugins/*/tsconfig.json'), ...findProjects('examples/*/tsconfig.json'), ...findProjects('x-pack/examples/*/tsconfig.json'), diff --git a/src/plugins/chart_expressions/expression_tagcloud/.i18nrc.json b/src/plugins/chart_expressions/expression_tagcloud/.i18nrc.json new file mode 100755 index 0000000000000..df4e39309f98e --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/.i18nrc.json @@ -0,0 +1,6 @@ +{ + "prefix": "expressionTagcloud", + "paths": { + "expressionTagcloud": "." + } +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/README.md b/src/plugins/chart_expressions/expression_tagcloud/README.md new file mode 100755 index 0000000000000..ae7635ffe0173 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/README.md @@ -0,0 +1,9 @@ +# expressionTagcloud + +Expression Tagcloud plugin adds a `tagcloud` renderer and function to the expression plugin. The renderer will display the `Wordcloud` chart. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/constants.ts b/src/plugins/chart_expressions/expression_tagcloud/common/constants.ts new file mode 100644 index 0000000000000..3d834448a94ef --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 PLUGIN_ID = 'expressionTagcloud'; +export const PLUGIN_NAME = 'expressionTagcloud'; + +export const EXPRESSION_NAME = 'tagcloud'; diff --git a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap similarity index 98% rename from src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap rename to src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap index 2888d7637546c..56b24f0ae004f 100644 --- a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap @@ -22,7 +22,7 @@ Object { exports[`interpreter/functions#tagcloud returns an object with the correct structure 1`] = ` Object { - "as": "tagloud_vis", + "as": "tagcloud", "type": "render", "value": Object { "syncColors": false, diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/index.ts new file mode 100644 index 0000000000000..5df32e3991edc --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { tagcloudFunction } from './tagcloud_function'; + +export const functions = [tagcloudFunction]; + +export { tagcloudFunction }; diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts similarity index 82% rename from src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts rename to src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts index 1671c0b01a666..2c6e021b5107a 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { createTagCloudFn } from './tag_cloud_fn'; +import { tagcloudFunction } from './tagcloud_function'; -import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; -import { Datatable } from '../../expressions/common/expression_types/specs'; +import { functionWrapper } from '../../../../expressions/common/expression_functions/specs/tests/utils'; +import { Datatable } from '../../../../expressions/common/expression_types/specs'; describe('interpreter/functions#tagcloud', () => { - const fn = functionWrapper(createTagCloudFn()); + const fn = functionWrapper(tagcloudFunction()); const context = { type: 'datatable', rows: [{ 'col-0-1': 0 }], diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts new file mode 100644 index 0000000000000..c3553c4660ce9 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { i18n } from '@kbn/i18n'; + +import { prepareLogTable, Dimension } from '../../../../visualizations/common/prepare_log_table'; +import { TagCloudVisParams } from '../types'; +import { ExpressionTagcloudFunction } from '../types'; +import { EXPRESSION_NAME } from '../constants'; + +const strings = { + help: i18n.translate('expressionTagcloud.functions.tagcloudHelpText', { + defaultMessage: 'Tagcloud visualization.', + }), + args: { + scale: i18n.translate('expressionTagcloud.functions.tagcloud.args.scaleHelpText', { + defaultMessage: 'Scale to determine font size of a word', + }), + orientation: i18n.translate('expressionTagcloud.functions.tagcloud.args.orientationHelpText', { + defaultMessage: 'Orientation of words inside tagcloud', + }), + minFontSize: i18n.translate('expressionTagcloud.functions.tagcloud.args.minFontSizeHelpText', { + defaultMessage: 'Min font size', + }), + maxFontSize: i18n.translate('expressionTagcloud.functions.tagcloud.args.maxFontSizeHelpText', { + defaultMessage: 'Max font size', + }), + showLabel: i18n.translate('expressionTagcloud.functions.tagcloud.args.showLabelHelpText', { + defaultMessage: 'Show chart label', + }), + palette: i18n.translate('expressionTagcloud.functions.tagcloud.args.paletteHelpText', { + defaultMessage: 'Defines the chart palette name', + }), + metric: i18n.translate('expressionTagcloud.functions.tagcloud.args.metricHelpText', { + defaultMessage: 'metric dimension configuration', + }), + bucket: i18n.translate('expressionTagcloud.functions.tagcloud.args.bucketHelpText', { + defaultMessage: 'bucket dimension configuration', + }), + }, + dimension: { + tags: i18n.translate('expressionTagcloud.functions.tagcloud.dimension.tags', { + defaultMessage: 'Tags', + }), + tagSize: i18n.translate('expressionTagcloud.functions.tagcloud.dimension.tagSize', { + defaultMessage: 'Tag size', + }), + }, +}; + +export const errors = { + invalidPercent: (percent: number) => + new Error( + i18n.translate('expressionTagcloud.functions.tagcloud.invalidPercentErrorMessage', { + defaultMessage: "Invalid value: '{percent}'. Percentage must be between 0 and 1", + values: { + percent, + }, + }) + ), + invalidImageUrl: (imageUrl: string) => + new Error( + i18n.translate('expressionTagcloud.functions.tagcloud.invalidImageUrl', { + defaultMessage: "Invalid image url: '{imageUrl}'.", + values: { + imageUrl, + }, + }) + ), +}; + +export const tagcloudFunction: ExpressionTagcloudFunction = () => { + const { help, args: argHelp, dimension } = strings; + + return { + name: EXPRESSION_NAME, + type: 'render', + inputTypes: ['datatable'], + help, + args: { + scale: { + types: ['string'], + default: 'linear', + options: ['linear', 'log', 'square root'], + help: argHelp.scale, + }, + orientation: { + types: ['string'], + default: 'single', + options: ['single', 'right angled', 'multiple'], + help: argHelp.orientation, + }, + minFontSize: { + types: ['number'], + default: 18, + help: argHelp.minFontSize, + }, + maxFontSize: { + types: ['number'], + default: 72, + help: argHelp.maxFontSize, + }, + showLabel: { + types: ['boolean'], + default: true, + help: argHelp.showLabel, + }, + palette: { + types: ['string'], + help: argHelp.palette, + default: 'default', + }, + metric: { + types: ['vis_dimension'], + help: argHelp.metric, + required: true, + }, + bucket: { + types: ['vis_dimension'], + help: argHelp.bucket, + }, + }, + fn(input, args, handlers) { + const visParams = { + scale: args.scale, + orientation: args.orientation, + minFontSize: args.minFontSize, + maxFontSize: args.maxFontSize, + showLabel: args.showLabel, + metric: args.metric, + ...(args.bucket && { + bucket: args.bucket, + }), + palette: { + type: 'palette', + name: args.palette, + }, + } as TagCloudVisParams; + + if (handlers?.inspectorAdapters?.tables) { + const argsTable: Dimension[] = [[[args.metric], dimension.tagSize]]; + if (args.bucket) { + argsTable.push([[args.bucket], dimension.tags]); + } + const logTable = prepareLogTable(input, argsTable); + handlers.inspectorAdapters.tables.logDatatable('default', logTable); + } + return { + type: 'render', + as: EXPRESSION_NAME, + value: { + visData: input, + visType: EXPRESSION_NAME, + visParams, + syncColors: handlers?.isSyncColorsEnabled?.() ?? false, + }, + }; + }, + }; +}; diff --git a/src/plugins/vis_type_pie/public/types/index.ts b/src/plugins/chart_expressions/expression_tagcloud/common/index.ts old mode 100644 new mode 100755 similarity index 92% rename from src/plugins/vis_type_pie/public/types/index.ts rename to src/plugins/chart_expressions/expression_tagcloud/common/index.ts index 12594660136d8..d8989abcc3d6f --- a/src/plugins/vis_type_pie/public/types/index.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export * from './types'; +export * from './constants'; diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts new file mode 100644 index 0000000000000..b1aba30380b59 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { PaletteOutput } from '../../../../charts/common'; +import { + Datatable, + ExpressionFunctionDefinition, + ExpressionValueRender, + SerializedFieldFormat, +} from '../../../../expressions'; +import { ExpressionValueVisDimension } from '../../../../visualizations/common'; +import { EXPRESSION_NAME } from '../constants'; + +interface Dimension { + accessor: number; + format: { + id?: string; + params?: SerializedFieldFormat; + }; +} + +interface TagCloudCommonParams { + scale: 'linear' | 'log' | 'square root'; + orientation: 'single' | 'right angled' | 'multiple'; + minFontSize: number; + maxFontSize: number; + showLabel: boolean; +} + +export interface TagCloudVisConfig extends TagCloudCommonParams { + metric: ExpressionValueVisDimension; + bucket?: ExpressionValueVisDimension; +} + +export interface TagCloudVisParams extends TagCloudCommonParams { + palette: PaletteOutput; + metric: Dimension; + bucket?: Dimension; +} + +export interface TagcloudRendererConfig { + visType: typeof EXPRESSION_NAME; + visData: Datatable; + visParams: TagCloudVisParams; + syncColors: boolean; +} + +interface Arguments extends TagCloudVisConfig { + palette: string; +} + +export type ExpressionTagcloudFunction = () => ExpressionFunctionDefinition< + 'tagcloud', + Datatable, + Arguments, + ExpressionValueRender +>; diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_renderers.ts new file mode 100644 index 0000000000000..d426aa061a2ab --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_renderers.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ChartsPluginSetup } from '../../../../charts/public'; + +export interface TagCloudTypeProps { + palettes: ChartsPluginSetup['palettes']; +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/index.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/index.ts new file mode 100644 index 0000000000000..ec934e7affe88 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/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 * from './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/vis_type_xy/jest.config.js b/src/plugins/chart_expressions/expression_tagcloud/jest.config.js similarity index 79% rename from src/plugins/vis_type_xy/jest.config.js rename to src/plugins/chart_expressions/expression_tagcloud/jest.config.js index a14203b7b757f..c88c150d6f649 100644 --- a/src/plugins/vis_type_xy/jest.config.js +++ b/src/plugins/chart_expressions/expression_tagcloud/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/vis_type_xy'], + rootDir: '../../../../', + roots: ['/src/plugins/chart_expressions/expression_tagcloud'], }; diff --git a/src/plugins/chart_expressions/expression_tagcloud/kibana.json b/src/plugins/chart_expressions/expression_tagcloud/kibana.json new file mode 100755 index 0000000000000..26d5ef9750e60 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "expressionTagcloud", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "visualizations", "charts", "presentationUtil", "fieldFormats"], + "requiredBundles": ["kibanaUtils"], + "optionalPlugins": [], + "owner": { + "name": "Kibana App", + "githubTeam": "kibana-app" + }, + "description": "Expression Tagcloud plugin adds a `tagcloud` renderer and function to the expression plugin. The renderer will display the `Wordcloud` chart." +} diff --git a/src/plugins/spaces_oss/common/index.ts b/src/plugins/chart_expressions/expression_tagcloud/public/components/index.ts similarity index 90% rename from src/plugins/spaces_oss/common/index.ts rename to src/plugins/chart_expressions/expression_tagcloud/public/components/index.ts index a499a06983e63..2ebc3d586d903 100644 --- a/src/plugins/spaces_oss/common/index.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Space } from './types'; +export * from './tagcloud_component'; diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud.scss b/src/plugins/chart_expressions/expression_tagcloud/public/components/tag_cloud.scss similarity index 100% rename from src/plugins/vis_type_tagcloud/public/components/tag_cloud.scss rename to src/plugins/chart_expressions/expression_tagcloud/public/components/tag_cloud.scss diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.test.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx similarity index 93% rename from src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.test.tsx rename to src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx index b4d4e70d5ffe3..542a9c1cd9bf7 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx @@ -7,12 +7,12 @@ */ import React from 'react'; import { Wordcloud, Settings } from '@elastic/charts'; -import { chartPluginMock } from '../../../charts/public/mocks'; -import type { Datatable } from '../../../expressions/public'; +import { chartPluginMock } from '../../../../charts/public/mocks'; +import type { Datatable } from '../../../../expressions/public'; import { mount } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; -import TagCloudChart, { TagCloudChartProps } from './tag_cloud_chart'; -import { TagCloudVisParams } from '../types'; +import TagCloudChart, { TagCloudChartProps } from './tagcloud_component'; +import { TagCloudVisParams } from '../../common/types'; jest.mock('../services', () => ({ getFormatService: jest.fn(() => { diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx similarity index 93% rename from src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx rename to src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index b89fe2fa90ede..163a2e8ce38ac 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -11,16 +11,16 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { throttle } from 'lodash'; import { EuiIconTip, EuiResizeObserver } from '@elastic/eui'; import { Chart, Settings, Wordcloud, RenderChangeListener } from '@elastic/charts'; -import type { PaletteRegistry } from '../../../charts/public'; -import type { IInterpreterRenderHandlers } from '../../../expressions/public'; +import type { PaletteRegistry } from '../../../../charts/public'; +import type { IInterpreterRenderHandlers } from '../../../../expressions/public'; import { getFormatService } from '../services'; -import { TagCloudVisRenderValue } from '../tag_cloud_fn'; +import { TagcloudRendererConfig } from '../../common/types'; import './tag_cloud.scss'; const MAX_TAG_COUNT = 200; -export type TagCloudChartProps = TagCloudVisRenderValue & { +export type TagCloudChartProps = TagcloudRendererConfig & { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; palettesRegistry: PaletteRegistry; @@ -204,7 +204,7 @@ export const TagCloudChart = ({ color="warning" content={ } @@ -218,7 +218,7 @@ export const TagCloudChart = ({ color="warning" content={ } @@ -231,6 +231,5 @@ export const TagCloudChart = ({ ); }; -// default export required for React.Lazy // eslint-disable-next-line import/no-default-export export { TagCloudChart as default }; diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/index.ts b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/index.ts new file mode 100644 index 0000000000000..4819430cda791 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/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 { tagcloudRenderer } from './tagcloud_renderer'; diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx new file mode 100644 index 0000000000000..58e177dac6775 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { lazy } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { ExpressionRenderDefinition } from '../../../../expressions/common'; +import { VisualizationContainer } from '../../../../visualizations/public'; +import { withSuspense } from '../../../../presentation_util/public'; +import { TagcloudRendererConfig } from '../../common/types'; +import { ExpressioTagcloudRendererDependencies } from '../plugin'; +import { EXPRESSION_NAME } from '../../common'; + +export const strings = { + getDisplayName: () => + i18n.translate('expressionTagcloud.renderer.tagcloud.displayName', { + defaultMessage: 'Tag Cloud visualization', + }), + getHelpDescription: () => + i18n.translate('expressionTagcloud.renderer.tagcloud.helpDescription', { + defaultMessage: 'Render a tag cloud', + }), +}; + +const LazyTagcloudComponent = lazy(() => import('../components/tagcloud_component')); +const TagcloudComponent = withSuspense(LazyTagcloudComponent); + +export const tagcloudRenderer: ( + deps: ExpressioTagcloudRendererDependencies +) => ExpressionRenderDefinition = ({ palettes }) => ({ + name: EXPRESSION_NAME, + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async (domNode, config, handlers) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + const palettesRegistry = await palettes.getPalettes(); + + render( + + + + + , + domNode + ); + }, +}); diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/index.ts b/src/plugins/chart_expressions/expression_tagcloud/public/index.ts new file mode 100644 index 0000000000000..8d170c49b6633 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/public/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExpressionTagcloudPlugin } from './plugin'; + +export type { ExpressionTagcloudPluginSetup, ExpressionTagcloudPluginStart } from './plugin'; + +export function plugin() { + return new ExpressionTagcloudPlugin(); +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/plugin.ts b/src/plugins/chart_expressions/expression_tagcloud/public/plugin.ts new file mode 100644 index 0000000000000..7cbc9ac7c6706 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/public/plugin.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { ExpressionsStart, ExpressionsSetup } from '../../../expressions/public'; +import { ChartsPluginSetup } from '../../../charts/public'; +import { tagcloudRenderer } from './expression_renderers'; +import { tagcloudFunction } from '../common/expression_functions'; +import { FieldFormatsStart } from '../../../field_formats/public'; +import { setFormatService } from './services'; + +interface SetupDeps { + expressions: ExpressionsSetup; + charts: ChartsPluginSetup; +} + +/** @internal */ +export interface ExpressioTagcloudRendererDependencies { + palettes: ChartsPluginSetup['palettes']; +} + +interface StartDeps { + expression: ExpressionsStart; + fieldFormats: FieldFormatsStart; +} + +export type ExpressionTagcloudPluginSetup = void; +export type ExpressionTagcloudPluginStart = void; + +export class ExpressionTagcloudPlugin + implements + Plugin { + public setup(core: CoreSetup, { expressions, charts }: SetupDeps): ExpressionTagcloudPluginSetup { + const rendererDependencies: ExpressioTagcloudRendererDependencies = { + palettes: charts.palettes, + }; + expressions.registerFunction(tagcloudFunction); + expressions.registerRenderer(tagcloudRenderer(rendererDependencies)); + } + + public start(core: CoreStart, { fieldFormats }: StartDeps): ExpressionTagcloudPluginStart { + setFormatService(fieldFormats); + } + + public stop() {} +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/services.ts b/src/plugins/chart_expressions/expression_tagcloud/public/services.ts new file mode 100644 index 0000000000000..541e46a847260 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/public/services.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createGetterSetter } from '../../../kibana_utils/public'; +import { FieldFormatsStart } from '../../../field_formats/public'; + +export const [getFormatService, setFormatService] = createGetterSetter( + 'fieldFormats' +); diff --git a/src/plugins/chart_expressions/expression_tagcloud/server/index.ts b/src/plugins/chart_expressions/expression_tagcloud/server/index.ts new file mode 100644 index 0000000000000..c944168271314 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/server/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExpressionTagcloudPlugin } from './plugin'; + +export function plugin() { + return new ExpressionTagcloudPlugin(); +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/server/plugin.ts b/src/plugins/chart_expressions/expression_tagcloud/server/plugin.ts new file mode 100644 index 0000000000000..03dd05db25fa7 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/server/plugin.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 { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../../expressions/server'; +import { tagcloudFunction } from '../common/expression_functions'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionTagcloudPluginSetup = void; +export type ExpressionTagcloudPluginStart = void; + +export class ExpressionTagcloudPlugin + implements + Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionTagcloudPluginSetup { + expressions.registerFunction(tagcloudFunction); + } + + public start(core: CoreStart): ExpressionTagcloudPluginStart {} + + public stop() {} +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json b/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json new file mode 100644 index 0000000000000..c2d50e4cd4e13 --- /dev/null +++ b/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../presentation_util/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../field_formats/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, + ] +} diff --git a/src/plugins/dashboard/kibana.json b/src/plugins/dashboard/kibana.json index d270b7dad3c7c..164be971d22b7 100644 --- a/src/plugins/dashboard/kibana.json +++ b/src/plugins/dashboard/kibana.json @@ -19,7 +19,7 @@ "presentationUtil", "visualizations" ], - "optionalPlugins": ["home", "spacesOss", "savedObjectsTaggingOss", "usageCollection"], + "optionalPlugins": ["home", "spaces", "savedObjectsTaggingOss", "usageCollection"], "server": true, "ui": true, "requiredBundles": ["home", "kibanaReact", "kibanaUtils", "presentationUtil"] diff --git a/src/plugins/dashboard/public/application/dashboard_router.tsx b/src/plugins/dashboard/public/application/dashboard_router.tsx index cb40b30542869..073160b698d96 100644 --- a/src/plugins/dashboard/public/application/dashboard_router.tsx +++ b/src/plugins/dashboard/public/application/dashboard_router.tsx @@ -79,14 +79,13 @@ export async function mountApp({ urlForwarding, data: dataStart, share: shareStart, + spaces: spacesApi, embeddable: embeddableStart, - kibanaLegacy: { dashboardConfig }, savedObjectsTaggingOss, visualizations, presentationUtil, } = pluginsStart; - const spacesApi = pluginsStart.spacesOss?.isSpacesAvailable ? pluginsStart.spacesOss : undefined; const activeSpaceId = spacesApi && (await spacesApi.getActiveSpace$().pipe(first()).toPromise())?.id; let globalEmbedSettings: DashboardEmbedSettings | undefined; @@ -117,12 +116,12 @@ export async function mountApp({ allowByValueEmbeddables: initializerContext.config.get() .allowByValueEmbeddables, dashboardCapabilities: { - hideWriteControls: dashboardConfig.getHideWriteControls(), show: Boolean(coreStart.application.capabilities.dashboard.show), saveQuery: Boolean(coreStart.application.capabilities.dashboard.saveQuery), createNew: Boolean(coreStart.application.capabilities.dashboard.createNew), mapsCapabilities: { save: Boolean(coreStart.application.capabilities.maps?.save) }, createShortUrl: Boolean(coreStart.application.capabilities.dashboard.createShortUrl), + showWriteControls: Boolean(coreStart.application.capabilities.dashboard.showWriteControls), visualizeCapabilities: { save: Boolean(coreStart.application.capabilities.visualize?.save) }, storeSearchSession: Boolean(coreStart.application.capabilities.dashboard.storeSearchSession), }, @@ -251,7 +250,7 @@ export async function mountApp({ ); addHelpMenuToAppChrome(dashboardServices.chrome, coreStart.docLinks); - if (dashboardServices.dashboardCapabilities.hideWriteControls) { + if (!dashboardServices.dashboardCapabilities.showWriteControls) { coreStart.chrome.setBadge({ text: dashboardReadonlyBadge.getText(), tooltip: dashboardReadonlyBadge.getTooltip(), diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 953bd619c444b..86f81aa1ee10d 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -79,7 +79,7 @@ const defaultCapabilities: DashboardAppCapabilities = { createNew: false, saveQuery: false, createShortUrl: false, - hideWriteControls: true, + showWriteControls: false, mapsCapabilities: { save: false }, visualizeCapabilities: { save: false }, storeSearchSession: true, 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 cbe10438e578a..35b304f7cc65b 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -117,7 +117,7 @@ export class DashboardViewport extends React.Component { setLastSavedState( savedObjectToDashboardState({ - hideWriteControls: dashboardBuildContext.dashboardCapabilities.hideWriteControls, + showWriteControls: dashboardBuildContext.dashboardCapabilities.showWriteControls, version: dashboardBuildContext.kibanaVersion, savedObjectsTagging, usageCollection, @@ -341,7 +341,12 @@ export const useDashboardAppState = ({ if (from && to) timefilter.setTime({ from, to }); if (refreshInterval) timefilter.setRefreshInterval(refreshInterval); } - dispatchDashboardStateChange(setDashboardState(lastSavedState)); + dispatchDashboardStateChange( + setDashboardState({ + ...lastSavedState, + viewMode: ViewMode.VIEW, + }) + ); }, [lastSavedState, dashboardAppState, data.query.timefilter, dispatchDashboardStateChange]); /** diff --git a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts index 2e6290ec920c0..d17f8405d734f 100644 --- a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts @@ -27,7 +27,7 @@ import { interface SavedObjectToDashboardStateProps { version: string; - hideWriteControls: boolean; + showWriteControls: boolean; savedDashboard: DashboardSavedObject; usageCollection: DashboardAppServices['usageCollection']; savedObjectsTagging: DashboardAppServices['savedObjectsTagging']; @@ -55,9 +55,9 @@ interface StateToRawDashboardStateProps { */ export const savedObjectToDashboardState = ({ version, - hideWriteControls, savedDashboard, usageCollection, + showWriteControls, savedObjectsTagging, }: SavedObjectToDashboardStateProps): DashboardState => { const rawState = migrateAppState( @@ -70,7 +70,7 @@ export const savedObjectToDashboardState = ({ description: savedDashboard.description || '', tags: getTagsFromSavedDashboard(savedDashboard, savedObjectsTagging), panels: savedDashboard.panelsJSON ? JSON.parse(savedDashboard.panelsJSON) : [], - viewMode: savedDashboard.id || hideWriteControls ? ViewMode.VIEW : ViewMode.EDIT, + viewMode: savedDashboard.id || showWriteControls ? ViewMode.EDIT : ViewMode.VIEW, options: savedDashboard.optionsJSON ? JSON.parse(savedDashboard.optionsJSON) : {}, }, version, diff --git a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts index 9069173c15e8f..04461a46ad0da 100644 --- a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts @@ -38,7 +38,7 @@ export const loadSavedDashboardState = async ({ }: DashboardBuildContext & { savedDashboardId?: string }): Promise< LoadSavedDashboardStateReturn | undefined > => { - const { hideWriteControls } = dashboardCapabilities; + const { showWriteControls } = dashboardCapabilities; const { queryString } = query; // BWC - remove for 8.0 @@ -66,12 +66,12 @@ export const loadSavedDashboardState = async ({ const savedDashboardState = savedObjectToDashboardState({ savedDashboard, usageCollection, - hideWriteControls, + showWriteControls, savedObjectsTagging, version: initializerContext.env.packageInfo.version, }); - const isViewMode = hideWriteControls || Boolean(savedDashboard.id); + const isViewMode = !showWriteControls || Boolean(savedDashboard.id); savedDashboardState.viewMode = isViewMode ? ViewMode.VIEW : ViewMode.EDIT; savedDashboardState.filters = cleanFiltersForSerialize(savedDashboardState.filters); savedDashboardState.query = migrateLegacyQuery( diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts index c836638747145..571dfb0a8beeb 100644 --- a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts +++ b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts @@ -19,7 +19,7 @@ describe('createSessionRestorationDataProvider', () => { getAppState: () => savedObjectToDashboardState({ version, - hideWriteControls: false, + showWriteControls: true, usageCollection: undefined, savedObjectsTagging: undefined, savedDashboard: getSavedDashboardMock(), 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 e78583be56569..2e37dc61fe851 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 @@ -112,8 +112,9 @@ exports[`after fetch When given a title that matches multiple dashboards, filter `; -exports[`after fetch hideWriteControls 1`] = ` +exports[`after fetch initialFilter 1`] = ` + Create new dashboard + + } body={ -

- There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator. -

+ +

+ You can combine data views from any Kibana app into one dashboard and see everything in one place. +

+

+ + Install some sample data + , + } + } + /> +

+
} - iconType="glasses" + iconType="dashboardApp" title={

- No dashboards to view + Create your first dashboard

} /> @@ -154,7 +185,7 @@ exports[`after fetch hideWriteControls 1`] = ` entityNamePlural="dashboards" findItems={[Function]} headingId="dashboardListingHeading" - initialFilter="" + initialFilter="testFilter" initialPageSize={20} listingLimit={100} rowHeader="title" @@ -193,9 +224,8 @@ exports[`after fetch hideWriteControls 1`] = `
`; -exports[`after fetch initialFilter 1`] = ` +exports[`after fetch renders all table rows 1`] = ` `; -exports[`after fetch renders all table rows 1`] = ` +exports[`after fetch renders call to action when no dashboards exist 1`] = ` `; -exports[`after fetch renders call to action when no dashboards exist 1`] = ` +exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` `; -exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` +exports[`after fetch showWriteControls 1`] = ` - Create new dashboard - - } body={ - -

- You can combine data views from any Kibana app into one dashboard and see everything in one place. -

-

- - Install some sample data - , - } - } - /> -

-
+

+ There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator. +

} - iconType="dashboardApp" + iconType="glasses" title={

- Create your first dashboard + No dashboards to view

} /> @@ -601,7 +601,7 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` headingId="dashboardListingHeading" initialFilter="" initialPageSize={20} - listingLimit={1} + listingLimit={100} rowHeader="title" searchFilters={Array []} tableCaption="Dashboards" diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx index 7602b2ed68b62..37ee0ec13d7c9 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx @@ -133,9 +133,9 @@ describe('after fetch', () => { }); }); - test('hideWriteControls', async () => { + test('showWriteControls', async () => { const services = makeDefaultServices(); - services.dashboardCapabilities.hideWriteControls = true; + services.dashboardCapabilities.showWriteControls = false; const { component } = mountWith({ services }); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index 7f72c77009cb9..5f5923cb78696 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -87,7 +87,7 @@ export const DashboardListing = ({ }; }, [title, savedObjectsClient, redirectTo, data.query, kbnUrlStateStorage]); - const hideWriteControls = dashboardCapabilities.hideWriteControls; + const { showWriteControls } = dashboardCapabilities; const listingLimit = savedObjects.settings.getListingLimit(); const defaultFilter = title ? `"${title}"` : ''; @@ -118,8 +118,8 @@ export const DashboardListing = ({ }, [dashboardSessionStorage, redirectTo, core.overlays]); const emptyPrompt = useMemo( - () => getNoItemsMessage(hideWriteControls, core.application, createItem), - [createItem, core.application, hideWriteControls] + () => getNoItemsMessage(showWriteControls, core.application, createItem), + [createItem, core.application, showWriteControls] ); const fetchItems = useCallback( @@ -171,10 +171,10 @@ export const DashboardListing = ({ } = dashboardListingTable; return ( void ) => { - if (hideWriteControls) { + if (!showWriteControls) { return ( = 1) { + // If there is any user index pattern created, set the first as default + // if there is 0 patterns, then don't even call `hasUserIndexPattern()` + if (patterns.length >= 1 && (await this.hasUserIndexPattern().catch(() => true))) { defaultId = patterns[0]; await uiSettings.set('defaultIndex', defaultId); } else { diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index d20cfc98ba059..74f11badbb411 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -232,6 +232,13 @@ export class IndexPatternsService { } }; + /** + * Checks if current user has a user created index pattern ignoring fleet's server default index patterns + */ + async hasUserIndexPattern(): Promise { + return this.apiClient.hasUserIndexPattern(); + } + /** * Get field list by providing { pattern } * @param options diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 0e088d7aa8a8d..c79dc17e9fe84 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -136,6 +136,7 @@ export interface GetFieldsOptionsTimePattern { export interface IIndexPatternsApiClient { getFieldsForTimePattern: (options: GetFieldsOptionsTimePattern) => Promise; getFieldsForWildcard: (options: GetFieldsOptions) => Promise; + hasUserIndexPattern: () => Promise; } export type { SavedObject }; diff --git a/src/plugins/data/common/search/search_source/index.ts b/src/plugins/data/common/search/search_source/index.ts index 757e0de6ecb49..189538415ab53 100644 --- a/src/plugins/data/common/search/search_source/index.ts +++ b/src/plugins/data/common/search/search_source/index.ts @@ -12,7 +12,6 @@ export { extractReferences } from './extract_references'; export { parseSearchSourceJSON } from './parse_json'; export { getResponseInspectorStats } from './inspect'; export * from './fetch'; -export * from './legacy'; export * from './search_source'; export * from './search_source_service'; export * from './types'; diff --git a/src/plugins/data/common/search/search_source/legacy/types.ts b/src/plugins/data/common/search/search_source/legacy/types.ts deleted file mode 100644 index 6778be77c21c5..0000000000000 --- a/src/plugins/data/common/search/search_source/legacy/types.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 type { estypes, ApiResponse } from '@elastic/elasticsearch'; - -interface MsearchHeaders { - index: string; - preference?: number | string; -} - -interface MsearchRequest { - header: MsearchHeaders; - body: any; -} - -// @internal -export interface MsearchRequestBody { - searches: MsearchRequest[]; -} - -// @internal -export interface MsearchResponse { - body: ApiResponse<{ responses: Array> }>; -} diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 90a13c076839b..02144def7acc1 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -8,9 +8,8 @@ import { of } from 'rxjs'; import { IndexPattern } from '../../index_patterns'; -import { GetConfigFn } from '../../types'; import { SearchSource, SearchSourceDependencies, SortDirection } from './'; -import { AggConfigs, AggTypesRegistryStart, ES_SEARCH_STRATEGY } from '../../'; +import { AggConfigs, AggTypesRegistryStart } from '../../'; import { mockAggTypesRegistry } from '../aggs/test_helpers'; import { RequestResponder } from 'src/plugins/inspector/common'; import { switchMap } from 'rxjs/operators'; @@ -953,45 +952,6 @@ describe('SearchSource', () => { }); describe('fetch$', () => { - describe('#legacy COURIER_BATCH_SEARCHES', () => { - beforeEach(() => { - searchSourceDependencies = { - ...searchSourceDependencies, - getConfig: jest.fn(() => { - return true; // batchSearches = true - }) as GetConfigFn, - }; - }); - - test('should override to use sync search if not set', async () => { - searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); - const options = {}; - await searchSource.fetch$(options).toPromise(); - - const [, callOptions] = mockSearchMethod.mock.calls[0]; - expect(callOptions.strategy).toBe(ES_SEARCH_STRATEGY); - }); - - test('should remove searchSessionId when forcing ES_SEARCH_STRATEGY', async () => { - searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); - const options = { sessionId: 'test' }; - await searchSource.fetch$(options).toPromise(); - - const [, callOptions] = mockSearchMethod.mock.calls[0]; - expect(callOptions.strategy).toBe(ES_SEARCH_STRATEGY); - expect(callOptions.sessionId).toBeUndefined(); - }); - - test('should not override strategy if set ', async () => { - searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); - const options = { strategy: 'banana' }; - await searchSource.fetch$(options).toPromise(); - - const [, callOptions] = mockSearchMethod.mock.calls[0]; - expect(callOptions.strategy).toBe('banana'); - }); - }); - describe('responses', () => { test('should return partial results', async () => { searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 16ec3d5c6c6e0..56a74994c71ca 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -59,7 +59,7 @@ */ import { setWith } from '@elastic/safer-lodash-set'; -import { uniqueId, keyBy, pick, difference, isFunction, isEqual, uniqWith, isObject } from 'lodash'; +import { difference, isEqual, isFunction, isObject, keyBy, pick, uniqueId, uniqWith } from 'lodash'; import { catchError, finalize, @@ -78,7 +78,6 @@ import { fieldWildcardFilter } from '../../../../kibana_utils/common'; import { IIndexPattern, IndexPattern, IndexPatternField } from '../../index_patterns'; import { AggConfigs, - ES_SEARCH_STRATEGY, EsQuerySortValue, IEsSearchResponse, ISearchGeneric, @@ -87,18 +86,18 @@ import { import type { ISearchSource, SearchFieldValue, - SearchSourceOptions, SearchSourceFields, + SearchSourceOptions, } from './types'; -import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch'; +import { FetchHandlers, getSearchParamsFromRequest, RequestFailure, SearchRequest } from './fetch'; import { getRequestInspectorStats, getResponseInspectorStats } from './inspect'; import { getEsQueryConfig, - UI_SETTINGS, + IKibanaSearchResponse, isErrorResponse, isPartialResponse, - IKibanaSearchResponse, + UI_SETTINGS, } from '../../../common'; import { getHighlightRequest } from '../../../../field_formats/common'; import { extractReferences } from './extract_references'; @@ -106,7 +105,6 @@ import { extractReferences } from './extract_references'; /** @internal */ export const searchSourceRequiredUiSettings = [ 'dateFormat:tz', - UI_SETTINGS.COURIER_BATCH_SEARCHES, UI_SETTINGS.COURIER_CUSTOM_REQUEST_PREFERENCE, UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX, UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS, @@ -280,17 +278,6 @@ export class SearchSource { fetch$( options: ISearchOptions = {} ): Observable>> { - const { getConfig } = this.dependencies; - const syncSearchByDefault = getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES); - - // Use the sync search strategy if legacy search is enabled. - // This still uses bfetch for batching. - if (!options?.strategy && syncSearchByDefault) { - options.strategy = ES_SEARCH_STRATEGY; - // `ES_SEARCH_STRATEGY` doesn't support search sessions, hence remove sessionId - options.sessionId = undefined; - } - const s$ = defer(() => this.requestIsStarting(options)).pipe( switchMap(() => { const searchRequest = this.flatten(); diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts index 485f0079a0187..d4e8e06245114 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts @@ -23,7 +23,7 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient { this.http = http; } - private _request(url: string, query: any) { + private _request(url: string, query?: any) { return this.http .fetch(url, { query, @@ -62,4 +62,9 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient { allow_no_index: allowNoIndex, }).then((resp: any) => resp.fields || []); } + + async hasUserIndexPattern(): Promise { + const response = await this._request(this._getUrl(['has_user_index_pattern'])); + return response.result; + } } diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 9dd7dff9e5b66..985964ead0907 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1479,6 +1479,7 @@ export class IndexPatternsService { getIds: (refresh?: boolean) => Promise; getIdsWithTitle: (refresh?: boolean) => Promise; getTitles: (refresh?: boolean) => Promise; + hasUserIndexPattern(): Promise; refreshFields: (indexPattern: IndexPattern) => Promise; savedObjectToSpec: (savedObject: SavedObject) => IndexPatternSpec; setDefault: (id: string | null, force?: boolean) => Promise; @@ -2309,7 +2310,6 @@ export const UI_SETTINGS: { readonly COURIER_SET_REQUEST_PREFERENCE: "courier:setRequestPreference"; readonly COURIER_CUSTOM_REQUEST_PREFERENCE: "courier:customRequestPreference"; readonly COURIER_MAX_CONCURRENT_SHARD_REQUESTS: "courier:maxConcurrentShardRequests"; - readonly COURIER_BATCH_SEARCHES: "courier:batchSearches"; readonly SEARCH_INCLUDE_FROZEN: "search:includeFrozen"; readonly SEARCH_TIMEOUT: "search:timeout"; readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; diff --git a/src/plugins/data/server/index_patterns/has_user_index_pattern.test.ts b/src/plugins/data/server/index_patterns/has_user_index_pattern.test.ts new file mode 100644 index 0000000000000..efc149b409375 --- /dev/null +++ b/src/plugins/data/server/index_patterns/has_user_index_pattern.test.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not 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 '../../../../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); + }); + + it('returns true when there are any index patterns other than metrics-* or logs-*', 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 hasUserIndexPattern({ esClient, soClient })).toEqual(true); + }); + + describe('when only metrics-* and logs-* index patterns exist', () => { + beforeEach(() => { + soClient.find.mockResolvedValue({ + page: 1, + per_page: 100, + total: 2, + saved_objects: [ + { + id: '1', + references: [], + type: 'index-pattern', + score: 99, + attributes: { title: 'metrics-*' }, + }, + { + id: '2', + references: [], + type: 'index-pattern', + score: 99, + attributes: { title: 'logs-*' }, + }, + ], + }); + }); + + it('calls indices.resolveIndex for the index patterns', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [], + data_streams: [], + aliases: [], + }) + ); + await hasUserIndexPattern({ esClient, soClient }); + expect(esClient.indices.resolveIndex).toHaveBeenCalledWith({ + name: 'logs-*,metrics-*', + }); + }); + + it('returns false if no logs or metrics data_streams exist', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [], + data_streams: [], + aliases: [], + }) + ); + expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); + }); + + it('returns true if any index exists', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [{ name: 'logs', attributes: [] }], + data_streams: [], + aliases: [], + }) + ); + expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(true); + }); + + it('returns false if only metrics-elastic_agent data stream exists', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [], + data_streams: [ + { + name: 'metrics-elastic_agent', + timestamp_field: '@timestamp', + backing_indices: ['.ds-metrics-elastic_agent'], + }, + ], + aliases: [], + }) + ); + expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); + }); + + it('returns false if only logs-elastic_agent data stream exists', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + 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 metrics-endpoint.metadata_current_default index exists', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [ + { + name: 'metrics-endpoint.metadata_current_default', + attributes: ['open'], + }, + ], + aliases: [], + data_streams: [], + }) + ); + expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(false); + }); + + it('returns true if any other data stream exists', async () => { + esClient.indices.resolveIndex.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + indices: [], + data_streams: [ + { + name: 'other', + timestamp_field: '@timestamp', + backing_indices: ['.ds-other'], + }, + ], + aliases: [], + }) + ); + expect(await hasUserIndexPattern({ esClient, soClient })).toEqual(true); + }); + }); +}); diff --git a/src/plugins/data/server/index_patterns/has_user_index_pattern.ts b/src/plugins/data/server/index_patterns/has_user_index_pattern.ts new file mode 100644 index 0000000000000..b65983a7fd5d4 --- /dev/null +++ b/src/plugins/data/server/index_patterns/has_user_index_pattern.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ElasticsearchClient, SavedObjectsClientContract } from '../../../../core/server'; +import { IndexPatternSavedObjectAttrs } from '../../common/index_patterns/index_patterns'; +import { FLEET_ASSETS_TO_IGNORE } from '../../common/index_patterns/constants'; + +interface Deps { + esClient: ElasticsearchClient; + soClient: SavedObjectsClientContract; +} + +export const hasUserIndexPattern = async ({ esClient, soClient }: Deps): Promise => { + const indexPatterns = await soClient.find({ + type: 'index-pattern', + fields: ['title'], + search: `*`, + searchFields: ['title'], + perPage: 100, + }); + + if (indexPatterns.total === 0) { + return false; + } + + // If there are any index patterns that are not the default metrics-* and logs-* ones created by Fleet, + // assume there are user created index patterns + if ( + indexPatterns.saved_objects.some( + (ip) => + ip.attributes.title !== FLEET_ASSETS_TO_IGNORE.METRICS_INDEX_PATTERN && + ip.attributes.title !== FLEET_ASSETS_TO_IGNORE.LOGS_INDEX_PATTERN + ) + ) { + return true; + } + + const resolveResponse = await esClient.indices.resolveIndex({ + name: `${FLEET_ASSETS_TO_IGNORE.LOGS_INDEX_PATTERN},${FLEET_ASSETS_TO_IGNORE.METRICS_INDEX_PATTERN}`, + }); + + const hasAnyNonDefaultFleetIndices = resolveResponse.body.indices.some( + (ds) => ds.name !== FLEET_ASSETS_TO_IGNORE.METRICS_ENDPOINT_INDEX_TO_IGNORE + ); + + if (hasAnyNonDefaultFleetIndices) return true; + + const hasAnyNonDefaultFleetDataStreams = resolveResponse.body.data_streams.some( + (ds) => + ds.name !== FLEET_ASSETS_TO_IGNORE.METRICS_DATA_STREAM_TO_IGNORE && + ds.name !== FLEET_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE + ); + + if (hasAnyNonDefaultFleetDataStreams) return true; + + return false; +}; diff --git a/src/plugins/data/server/index_patterns/index_patterns_api_client.ts b/src/plugins/data/server/index_patterns/index_patterns_api_client.ts index 0ed84d4eee3b7..fb76647a945be 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_api_client.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_api_client.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ElasticsearchClient } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { GetFieldsOptions, IIndexPatternsApiClient, @@ -14,10 +14,14 @@ import { } from '../../common/index_patterns/types'; import { IndexPatternMissingIndices } from '../../common/index_patterns/lib'; import { IndexPatternsFetcher } from './fetcher'; +import { hasUserIndexPattern } from './has_user_index_pattern'; export class IndexPatternsApiServer implements IIndexPatternsApiClient { esClient: ElasticsearchClient; - constructor(elasticsearchClient: ElasticsearchClient) { + constructor( + elasticsearchClient: ElasticsearchClient, + private readonly savedObjectsClient: SavedObjectsClientContract + ) { this.esClient = elasticsearchClient; } async getFieldsForWildcard({ @@ -50,4 +54,11 @@ export class IndexPatternsApiServer implements IIndexPatternsApiClient { const indexPatterns = new IndexPatternsFetcher(this.esClient); return await indexPatterns.getFieldsForTimePattern(options); } + + async hasUserIndexPattern() { + return hasUserIndexPattern({ + esClient: this.esClient, + soClient: this.savedObjectsClient, + }); + } } diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index c3cdc65d3fa04..6f0491d91a640 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -66,7 +66,7 @@ export const indexPatternsServiceFactory = ({ return new IndexPatternsCommonService({ uiSettings: new UiSettingsServerToCommon(uiSettingsClient), savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient), - apiClient: new IndexPatternsApiServer(elasticsearchClient), + apiClient: new IndexPatternsApiServer(elasticsearchClient, savedObjectsClient), fieldFormats: formats, onError: (error) => { logger.error(error); diff --git a/src/plugins/data/server/index_patterns/routes.ts b/src/plugins/data/server/index_patterns/routes.ts index d2d8cb82cf646..32fa50940bca7 100644 --- a/src/plugins/data/server/index_patterns/routes.ts +++ b/src/plugins/data/server/index_patterns/routes.ts @@ -26,6 +26,7 @@ import { registerGetRuntimeFieldRoute } from './routes/runtime_fields/get_runtim import { registerDeleteRuntimeFieldRoute } from './routes/runtime_fields/delete_runtime_field'; import { registerPutRuntimeFieldRoute } from './routes/runtime_fields/put_runtime_field'; import { registerUpdateRuntimeFieldRoute } from './routes/runtime_fields/update_runtime_field'; +import { registerHasUserIndexPatternRoute } from './routes/has_user_index_pattern'; export function registerRoutes( http: HttpServiceSetup, @@ -49,6 +50,7 @@ export function registerRoutes( registerDeleteIndexPatternRoute(router, getStartServices); registerUpdateIndexPatternRoute(router, getStartServices); registerManageDefaultIndexPatternRoutes(router, getStartServices); + registerHasUserIndexPatternRoute(router, getStartServices); // Fields API registerUpdateFieldsRoute(router, getStartServices); diff --git a/src/plugins/data/server/index_patterns/routes/has_user_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/has_user_index_pattern.ts new file mode 100644 index 0000000000000..6447f50f88f26 --- /dev/null +++ b/src/plugins/data/server/index_patterns/routes/has_user_index_pattern.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { handleErrors } from './util/handle_errors'; +import { IRouter, StartServicesAccessor } from '../../../../../core/server'; +import type { DataPluginStart, DataPluginStartDependencies } from '../../plugin'; + +export const registerHasUserIndexPatternRoute = ( + router: IRouter, + getStartServices: StartServicesAccessor +) => { + router.get( + { + path: '/api/index_patterns/has_user_index_pattern', + validate: {}, + }, + router.handleLegacyErrors( + handleErrors(async (ctx, req, res) => { + const savedObjectsClient = ctx.core.savedObjects.client; + const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const [, , { indexPatterns }] = await getStartServices(); + const indexPatternsService = await indexPatterns.indexPatternsServiceFactory( + savedObjectsClient, + elasticsearchClient + ); + + return res.ok({ + body: { + result: await indexPatternsService.hasUserIndexPattern(), + }, + }); + }) + ) + ); +}; diff --git a/src/plugins/data/server/search/routes/call_msearch.test.ts b/src/plugins/data/server/search/routes/call_msearch.test.ts deleted file mode 100644 index e9d65ef93ae4e..0000000000000 --- a/src/plugins/data/server/search/routes/call_msearch.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; -import { Observable } from 'rxjs'; -import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; - -import { - coreMock, - pluginInitializerContextConfigMock, -} from '../../../../../../src/core/server/mocks'; -import { convertRequestBody, getCallMsearch } from './call_msearch'; - -describe('callMsearch', () => { - let esClient: DeeplyMockedKeys; - let globalConfig$: Observable; - let uiSettings: IUiSettingsClient; - let callMsearch: ReturnType; - - beforeEach(() => { - const coreContext = coreMock.createRequestHandlerContext(); - esClient = coreContext.elasticsearch.client; - globalConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; - uiSettings = coreContext.uiSettings.client; - - callMsearch = getCallMsearch({ esClient, globalConfig$, uiSettings }); - }); - - it('handler calls msearch with the given request', async () => { - const mockBody = { - searches: [{ header: { index: 'foo' }, body: { query: { match_all: {} } } }], - }; - - await callMsearch({ - body: mockBody, - signal: new AbortController().signal, - }); - - expect(esClient.asCurrentUser.msearch).toHaveBeenCalledTimes(1); - expect(esClient.asCurrentUser.msearch.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "body": "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\"} - {\\"query\\":{\\"match_all\\":{}}} - ", - }, - Object { - "querystring": Object { - "ignore_unavailable": true, - "max_concurrent_shard_requests": undefined, - }, - }, - ] - `); - }); - - describe('convertRequestBody', () => { - it('combines header & body into proper msearch request', () => { - const request = { - searches: [{ header: { index: 'foo', preference: 0 }, body: { test: true } }], - }; - expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` - "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} - {\\"timeout\\":\\"30000ms\\",\\"test\\":true} - " - `); - }); - - it('handles multiple searches', () => { - const request = { - searches: [ - { header: { index: 'foo', preference: 0 }, body: { test: true } }, - { header: { index: 'bar', preference: 1 }, body: { hello: 'world' } }, - ], - }; - expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` - "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} - {\\"timeout\\":\\"30000ms\\",\\"test\\":true} - {\\"ignore_unavailable\\":true,\\"index\\":\\"bar\\",\\"preference\\":1} - {\\"timeout\\":\\"30000ms\\",\\"hello\\":\\"world\\"} - " - `); - }); - }); -}); diff --git a/src/plugins/data/server/search/routes/call_msearch.ts b/src/plugins/data/server/search/routes/call_msearch.ts deleted file mode 100644 index 4a7db9517c688..0000000000000 --- a/src/plugins/data/server/search/routes/call_msearch.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Observable } from 'rxjs'; -import { first } from 'rxjs/operators'; -import type { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; -import type { estypes } from '@elastic/elasticsearch'; - -import type { MsearchRequestBody, MsearchResponse } from '../../../common/search/search_source'; -import { getKbnServerError } from '../../../../kibana_utils/server'; -import { getShardTimeout, getDefaultSearchParams, shimAbortSignal, shimHitsTotal } from '..'; - -/** @internal */ -export function convertRequestBody( - requestBody: MsearchRequestBody, - { timeout }: { timeout?: string } -): string { - return requestBody.searches.reduce((req, curr) => { - const header = JSON.stringify({ - ignore_unavailable: true, - ...curr.header, - }); - const body = JSON.stringify({ - timeout, - ...curr.body, - }); - return `${req}${header}\n${body}\n`; - }, ''); -} - -interface CallMsearchDependencies { - esClient: IScopedClusterClient; - globalConfig$: Observable; - uiSettings: IUiSettingsClient; -} - -/** - * Helper for the `/internal/_msearch` route, exported separately here - * so that it can be reused elsewhere in the data plugin on the server, - * e.g. SearchSource - * - * @internal - */ -export function getCallMsearch(dependencies: CallMsearchDependencies) { - /** - * @throws KbnServerError - */ - return async (params: { - body: MsearchRequestBody; - signal?: AbortSignal; - }): Promise => { - const { esClient, globalConfig$, uiSettings } = dependencies; - - // get shardTimeout - const config = await globalConfig$.pipe(first()).toPromise(); - const timeout = getShardTimeout(config); - - // trackTotalHits is not supported by msearch - const { track_total_hits: _, ...defaultParams } = await getDefaultSearchParams(uiSettings); - - try { - const promise = esClient.asCurrentUser.msearch( - { - // @ts-expect-error @elastic/elasticsearch client types don't support plain string bodies - body: convertRequestBody(params.body, timeout), - }, - { - querystring: defaultParams, - } - ); - const response = await shimAbortSignal(promise, params.signal); - - return { - body: { - ...response, - body: { - responses: response.body.responses?.map((r) => - shimHitsTotal(r as estypes.SearchResponse) - ), - }, - }, - }; - } catch (e) { - throw getKbnServerError(e); - } - }; -} diff --git a/src/plugins/data/server/search/routes/index.ts b/src/plugins/data/server/search/routes/index.ts index 33bd80738bc8d..0a4f5467c8fa9 100644 --- a/src/plugins/data/server/search/routes/index.ts +++ b/src/plugins/data/server/search/routes/index.ts @@ -6,6 +6,4 @@ * Side Public License, v 1. */ -export * from './call_msearch'; -export * from './msearch'; export * from './search'; diff --git a/src/plugins/data/server/search/routes/msearch.test.ts b/src/plugins/data/server/search/routes/msearch.test.ts deleted file mode 100644 index 303f83582f737..0000000000000 --- a/src/plugins/data/server/search/routes/msearch.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 { MockedKeys } from '@kbn/utility-types/jest'; -import { Observable } from 'rxjs'; - -import { - CoreSetup, - RequestHandlerContext, - SharedGlobalConfig, - StartServicesAccessor, -} from 'src/core/server'; -import { - coreMock, - httpServerMock, - pluginInitializerContextConfigMock, -} from '../../../../../../src/core/server/mocks'; -import { convertRequestBody } from './call_msearch'; -import { registerMsearchRoute } from './msearch'; -import { DataPluginStart } from '../../plugin'; -import { dataPluginMock } from '../../mocks'; -import * as jsonEofException from '../../../common/search/test_data/json_e_o_f_exception.json'; -import { ResponseError } from '@elastic/elasticsearch/lib/errors'; - -describe('msearch route', () => { - let mockDataStart: MockedKeys; - let mockCoreSetup: MockedKeys>; - let getStartServices: jest.Mocked>; - let globalConfig$: Observable; - - beforeEach(() => { - mockDataStart = dataPluginMock.createStartContract(); - mockCoreSetup = coreMock.createSetup({ pluginStartContract: mockDataStart }); - getStartServices = mockCoreSetup.getStartServices; - globalConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; - }); - - it('handler calls /_msearch with the given request', async () => { - const response = { id: 'yay', body: { responses: [{ hits: { total: 5 } }] } }; - const mockClient = { msearch: jest.fn().mockResolvedValue(response) }; - const mockContext = { - core: { - elasticsearch: { client: { asCurrentUser: mockClient } }, - uiSettings: { client: { get: jest.fn() } }, - }, - }; - const mockBody = { searches: [{ header: {}, body: {} }] }; - const mockQuery = {}; - const mockRequest = httpServerMock.createKibanaRequest({ - body: mockBody, - query: mockQuery, - }); - const mockResponse = httpServerMock.createResponseFactory(); - - registerMsearchRoute(mockCoreSetup.http.createRouter(), { getStartServices, globalConfig$ }); - - const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; - const handler = mockRouter.post.mock.calls[0][1]; - await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - - expect(mockClient.msearch.mock.calls[0][0].body).toEqual( - convertRequestBody(mockBody as any, {}) - ); - expect(mockClient.msearch.mock.calls[0][1].querystring).toMatchInlineSnapshot(` - Object { - "ignore_unavailable": true, - "max_concurrent_shard_requests": undefined, - } - `); - expect(mockResponse.ok).toBeCalled(); - expect(mockResponse.ok.mock.calls[0][0]).toEqual({ - body: response, - }); - }); - - it('handler returns an error response if the search throws an error', async () => { - const rejectedValue = Promise.reject( - new ResponseError({ - body: jsonEofException, - statusCode: 400, - meta: {} as any, - headers: [], - warnings: [], - }) - ); - const mockClient = { - msearch: jest.fn().mockReturnValue(rejectedValue), - }; - const mockContext = { - core: { - elasticsearch: { client: { asCurrentUser: mockClient } }, - uiSettings: { client: { get: jest.fn() } }, - }, - }; - const mockBody = { searches: [{ header: {}, body: {} }] }; - const mockQuery = {}; - const mockRequest = httpServerMock.createKibanaRequest({ - body: mockBody, - query: mockQuery, - }); - const mockResponse = httpServerMock.createResponseFactory(); - - registerMsearchRoute(mockCoreSetup.http.createRouter(), { getStartServices, globalConfig$ }); - - const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; - const handler = mockRouter.post.mock.calls[0][1]; - await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - - expect(mockClient.msearch).toBeCalledTimes(1); - expect(mockResponse.customError).toBeCalled(); - - const error: any = mockResponse.customError.mock.calls[0][0]; - expect(error.statusCode).toBe(400); - expect(error.body.message).toMatch(/json_e_o_f_exception/); - expect(error.body.attributes).toBe(jsonEofException.error); - }); - - it('handler returns an error response if the search throws a general error', async () => { - const rejectedValue = Promise.reject(new Error('What happened?')); - const mockClient = { - msearch: jest.fn().mockReturnValue(rejectedValue), - }; - const mockContext = { - core: { - elasticsearch: { client: { asCurrentUser: mockClient } }, - uiSettings: { client: { get: jest.fn() } }, - }, - }; - const mockBody = { searches: [{ header: {}, body: {} }] }; - const mockQuery = {}; - const mockRequest = httpServerMock.createKibanaRequest({ - body: mockBody, - query: mockQuery, - }); - const mockResponse = httpServerMock.createResponseFactory(); - - registerMsearchRoute(mockCoreSetup.http.createRouter(), { getStartServices, globalConfig$ }); - - const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; - const handler = mockRouter.post.mock.calls[0][1]; - await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - - expect(mockClient.msearch).toBeCalledTimes(1); - expect(mockResponse.customError).toBeCalled(); - - const error: any = mockResponse.customError.mock.calls[0][0]; - expect(error.statusCode).toBe(500); - expect(error.body.message).toBe('What happened?'); - expect(error.body.attributes).toBe(undefined); - }); -}); diff --git a/src/plugins/data/server/search/routes/msearch.ts b/src/plugins/data/server/search/routes/msearch.ts deleted file mode 100644 index 6a7ecc828bc4c..0000000000000 --- a/src/plugins/data/server/search/routes/msearch.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { schema } from '@kbn/config-schema'; - -import { SearchRouteDependencies } from '../search_service'; - -import { getCallMsearch } from './call_msearch'; -import { reportServerError } from '../../../../kibana_utils/server'; -import type { DataPluginRouter } from '../types'; -/** - * The msearch route takes in an array of searches, each consisting of header - * and body json, and reformts them into a single request for the _msearch API. - * - * The reason for taking requests in a different format is so that we can - * inject values into each request without needing to manually parse each one. - * - * This route is internal and _should not be used_ in any new areas of code. - * It only exists as a means of removing remaining dependencies on the - * legacy ES client. - * - * @deprecated - * @removeBy 8.1 - */ -export function registerMsearchRoute( - router: DataPluginRouter, - deps: SearchRouteDependencies -): void { - router.post( - { - path: '/internal/_msearch', - validate: { - body: schema.object({ - searches: schema.arrayOf( - schema.object({ - header: schema.object( - { - index: schema.string(), - preference: schema.maybe(schema.oneOf([schema.number(), schema.string()])), - }, - { unknowns: 'allow' } - ), - body: schema.object({}, { unknowns: 'allow' }), - }) - ), - }), - }, - }, - async (context, request, res) => { - const callMsearch = getCallMsearch({ - esClient: context.core.elasticsearch.client, - globalConfig$: deps.globalConfig$, - uiSettings: context.core.uiSettings.client, - }); - - try { - const response = await callMsearch({ body: request.body }); - return res.ok(response); - } catch (err) { - return reportServerError(res, err); - } - } - ); -} diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 5b4ff121f3c77..4961f6dc3e959 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -36,7 +36,7 @@ import { AggsService } from './aggs'; import { FieldFormatsStart } from '../../../field_formats/server'; import { IndexPatternsServiceStart } from '../index_patterns'; -import { registerMsearchRoute, registerSearchRoute } from './routes'; +import { registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './strategies/es_search'; import { DataPluginStart, DataPluginStartDependencies } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; @@ -133,12 +133,7 @@ export class SearchService implements Plugin { const usage = usageCollection ? usageProvider(core) : undefined; const router = core.http.createRouter(); - const routeDependencies = { - getStartServices: core.getStartServices, - globalConfig$: this.initializerContext.config.legacy.globalConfig$, - }; registerSearchRoute(router); - registerMsearchRoute(router, routeDependencies); core.http.registerRouteHandlerContext( 'search', diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 7eafad71f4f95..63f8c53b02319 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -528,6 +528,7 @@ class IndexPatternsService { // Warning: (ae-forgotten-export) The symbol "IndexPatternListItem" needs to be exported by the entry point index.d.ts getIdsWithTitle: (refresh?: boolean) => Promise; getTitles: (refresh?: boolean) => Promise; + hasUserIndexPattern(): Promise; refreshFields: (indexPattern: IndexPattern) => Promise; savedObjectToSpec: (savedObject: SavedObject_2) => IndexPatternSpec; setDefault: (id: string | null, force?: boolean) => Promise; @@ -838,7 +839,6 @@ export const UI_SETTINGS: { readonly COURIER_SET_REQUEST_PREFERENCE: "courier:setRequestPreference"; readonly COURIER_CUSTOM_REQUEST_PREFERENCE: "courier:customRequestPreference"; readonly COURIER_MAX_CONCURRENT_SHARD_REQUESTS: "courier:maxConcurrentShardRequests"; - readonly COURIER_BATCH_SEARCHES: "courier:batchSearches"; readonly SEARCH_INCLUDE_FROZEN: "search:includeFrozen"; readonly SEARCH_TIMEOUT: "search:timeout"; readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts index 360529ad5a735..889c27bdf1ce5 100644 --- a/src/plugins/data/server/ui_settings.ts +++ b/src/plugins/data/server/ui_settings.ts @@ -263,25 +263,6 @@ export function getUiSettings(): Record> { category: ['search'], schema: schema.number(), }, - [UI_SETTINGS.COURIER_BATCH_SEARCHES]: { - name: i18n.translate('data.advancedSettings.courier.batchSearchesTitle', { - defaultMessage: 'Use sync search', - }), - value: false, - type: 'boolean', - description: i18n.translate('data.advancedSettings.courier.batchSearchesText', { - defaultMessage: `Kibana uses a new asynchronous search and infrastructure. - Enable this option if you prefer to fallback to the legacy synchronous behavior`, - }), - deprecation: { - message: i18n.translate('data.advancedSettings.courier.batchSearchesTextDeprecation', { - defaultMessage: 'This setting is deprecated and will be removed in Kibana 8.0.', - }), - docLinksKey: 'kibanaSearchSettings', - }, - category: ['search'], - schema: schema.boolean(), - }, [UI_SETTINGS.SEARCH_INCLUDE_FROZEN]: { name: 'Search in frozen indices', description: `Will include { const indexPattern = await indexPatterns.get(indexPatternId); - searchSource - .setParent(undefined) - .setField('index', indexPattern) - .setField('version', true) - .setField('size', 1) - .setField('query', { - query: { - constant_score: { - filter: { - ids: { - values: [anchorId], - }, - }, - }, - }, - language: 'lucene', - }) - .setField('sort', sort); - if (useNewFieldsApi) { - searchSource.removeField('fieldsFromSource'); - searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]); - } + updateSearchSource(searchSource, anchorId, sort, useNewFieldsApi, indexPattern); + const response = await searchSource.fetch(); + const doc = get(response, ['hits', 'hits', 0]); - if (get(response, ['hits', 'total'], 0) < 1) { + if (!doc) { throw new Error( i18n.translate('discover.context.failedToLoadAnchorDocumentErrorDescription', { defaultMessage: 'Failed to load anchor document.', @@ -60,8 +42,41 @@ export function fetchAnchorProvider( } return { - ...get(response, ['hits', 'hits', 0]), + ...doc, isAnchor: true, } as EsHitRecord; }; } + +export function updateSearchSource( + searchSource: ISearchSource, + anchorId: string, + sort: EsQuerySortValue[], + useNewFieldsApi: boolean, + indexPattern: IndexPattern +) { + searchSource + .setParent(undefined) + .setField('index', indexPattern) + .setField('version', true) + .setField('size', 1) + .setField('query', { + query: { + constant_score: { + filter: { + ids: { + values: [anchorId], + }, + }, + }, + }, + language: 'lucene', + }) + .setField('sort', sort) + .setField('trackTotalHits', false); + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]); + } + return searchSource; +} diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts index ca74c77676edb..127616e27fd92 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts @@ -27,7 +27,7 @@ interface Timestamp { lte?: string; } -describe('context app', function () { +describe('context predecessors', function () { let fetchPredecessors: ( indexPatternId: string, timeField: string, @@ -49,7 +49,7 @@ describe('context app', function () { data: { search: { searchSource: { - create: jest.fn().mockImplementation(() => mockSearchSource), + createEmpty: jest.fn().mockImplementation(() => mockSearchSource), }, }, }, @@ -241,7 +241,7 @@ describe('context app', function () { data: { search: { searchSource: { - create: jest.fn().mockImplementation(() => mockSearchSource), + createEmpty: jest.fn().mockImplementation(() => mockSearchSource), }, }, }, diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts index ba61dd15af46b..a6c4a734fdbc4 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts @@ -27,7 +27,7 @@ interface Timestamp { lte?: string; } -describe('context app', function () { +describe('context successors', function () { let fetchSuccessors: ( indexPatternId: string, timeField: string, @@ -49,7 +49,7 @@ describe('context app', function () { data: { search: { searchSource: { - create: jest.fn().mockImplementation(() => mockSearchSource), + createEmpty: jest.fn().mockImplementation(() => mockSearchSource), }, }, }, @@ -244,7 +244,7 @@ describe('context app', function () { data: { search: { searchSource: { - create: jest.fn().mockImplementation(() => mockSearchSource), + createEmpty: jest.fn().mockImplementation(() => mockSearchSource), }, }, }, diff --git a/src/plugins/discover/public/application/angular/context/api/context.test.ts b/src/plugins/discover/public/application/angular/context/api/context.test.ts new file mode 100644 index 0000000000000..5ad9c02871dca --- /dev/null +++ b/src/plugins/discover/public/application/angular/context/api/context.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { updateSearchSource } from './context'; +import { indexPatternMock } from '../../../../__mocks__/index_pattern'; +import { createSearchSourceMock } from '../../../../../../data/public/mocks'; + +describe('context api', function () { + test('createSearchSource when useFieldsApi is true', () => { + const newSearchSource = createSearchSourceMock({ index: indexPatternMock }); + const searchSource = updateSearchSource(newSearchSource, indexPatternMock, [], true); + expect(searchSource.getSearchRequestBody()).toMatchSnapshot(); + }); + test('createSearchSource when useFieldsApi is false', () => { + const newSearchSource = createSearchSourceMock({ index: indexPatternMock }); + const searchSource = updateSearchSource(newSearchSource, indexPatternMock, [], false); + expect(searchSource.getSearchRequestBody()).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts index e9da3a7c4784f..b6ba95fd5e84a 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -7,7 +7,7 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { Filter, IndexPatternsContract, IndexPattern } from 'src/plugins/data/public'; +import { Filter, IndexPatternsContract, IndexPattern, SearchSource } from 'src/plugins/data/public'; import { reverseSortDir, SortDirection } from './utils/sorting'; import { extractNanos, convertIsoToMillis } from './utils/date_conversion'; import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; @@ -46,7 +46,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields * * @param {SurrDocType} type - `successors` or `predecessors` * @param {string} indexPatternId - * @param {AnchorHitRecord} anchor - anchor record + * @param {EsHitRecord} anchor - anchor record * @param {string} timeField - name of the timefield, that's sorted on * @param {string} tieBreakerField - name of the tie breaker, the 2nd sort field * @param {SortDirection} sortDir - direction of sorting @@ -68,7 +68,9 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields return []; } const indexPattern = await indexPatterns.get(indexPatternId); - const searchSource = await createSearchSource(indexPattern, filters); + const { data } = getServices(); + const searchSource = data.search.searchSource.createEmpty() as SearchSource; + updateSearchSource(searchSource, indexPattern, filters, Boolean(useNewFieldsApi)); const sortDirToApply = type === SurrDocType.SUCCESSORS ? sortDir : reverseSortDir(sortDir); const nanos = indexPattern.isTimeNanosBased() ? extractNanos(anchor.fields[timeField][0]) : ''; @@ -116,20 +118,23 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields return documents; } +} - async function createSearchSource(indexPattern: IndexPattern, filters: Filter[]) { - const { data } = getServices(); - - const searchSource = await data.search.searchSource.create(); - if (useNewFieldsApi) { - searchSource.removeField('fieldsFromSource'); - searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]); - } - return searchSource - .setParent(undefined) - .setField('index', indexPattern) - .setField('filter', filters); +export function updateSearchSource( + searchSource: SearchSource, + indexPattern: IndexPattern, + filters: Filter[], + useNewFieldsApi: boolean +) { + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]); } + return searchSource + .setParent(undefined) + .setField('index', indexPattern) + .setField('filter', filters) + .setField('trackTotalHits', false); } export { fetchContextProvider }; diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/_doc_table.scss b/src/plugins/discover/public/application/apps/main/components/doc_table/_doc_table.scss index add2d4e753c60..d19a1fd042069 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/_doc_table.scss +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/_doc_table.scss @@ -5,6 +5,7 @@ .kbnDocTableWrapper { @include euiScrollBar; overflow: auto; + display: flex; flex: 1 1 100%; flex-direction: column; /* 1 */ diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx index 8e9066151b368..778c6c1abe274 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx @@ -14,6 +14,8 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { DocTableProps, DocTableRenderProps, DocTableWrapper } from './doc_table_wrapper'; import { SkipBottomButton } from '../skip_bottom_button'; +const FOOTER_PADDING = { padding: 0 }; + const DocTableInfiniteContent = (props: DocTableRenderProps) => { const [limit, setLimit] = useState(props.minimumVisibleRows); @@ -74,29 +76,38 @@ const DocTableInfiniteContent = (props: DocTableRenderProps) => { {props.renderHeader()}{props.renderRows(props.rows.slice(0, limit))} -
- {props.rows.length === props.sampleSize ? ( -
- + - - - -
- ) : ( - - ​ - - )} + values={{ sampleSize: props.sampleSize }} + /> + + + + + ) : ( + + ​ + + )} + + + + ); }; diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx index c875bf155bd79..4c832d3f6a02c 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx @@ -81,6 +81,7 @@ export interface DocTableProps { } export interface DocTableRenderProps { + columnLength: number; rows: DocTableRow[]; minimumVisibleRows: number; sampleSize: number; @@ -219,6 +220,7 @@ export const DocTableWrapper = ({ > {rows.length !== 0 && render({ + columnLength: columns.length, rows, minimumVisibleRows, sampleSize, diff --git a/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.test.ts b/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.test.ts new file mode 100644 index 0000000000000..9810436aebd90 --- /dev/null +++ b/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { + sendCompleteMsg, + sendErrorMsg, + sendLoadingMsg, + sendPartialMsg, +} from './use_saved_search_messages'; +import { FetchStatus } from '../../../types'; +import { BehaviorSubject } from 'rxjs'; +import { DataMainMsg } from './use_saved_search'; + +describe('test useSavedSearch message generators', () => { + test('sendCompleteMsg', async (done) => { + const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.LOADING }); + main$.subscribe((value) => { + if (value.fetchStatus !== FetchStatus.LOADING) { + expect(value.fetchStatus).toBe(FetchStatus.COMPLETE); + expect(value.foundDocuments).toBe(true); + expect(value.error).toBe(undefined); + done(); + } + }); + sendCompleteMsg(main$, true); + }); + test('sendPartialMessage', async (done) => { + const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.LOADING }); + main$.subscribe((value) => { + if (value.fetchStatus !== FetchStatus.LOADING) { + expect(value.fetchStatus).toBe(FetchStatus.PARTIAL); + done(); + } + }); + sendPartialMsg(main$); + }); + test('sendLoadingMsg', async (done) => { + const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE }); + main$.subscribe((value) => { + if (value.fetchStatus !== FetchStatus.COMPLETE) { + expect(value.fetchStatus).toBe(FetchStatus.LOADING); + done(); + } + }); + sendLoadingMsg(main$); + }); + test('sendErrorMsg', async (done) => { + const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.PARTIAL }); + main$.subscribe((value) => { + if (value.fetchStatus === FetchStatus.ERROR) { + expect(value.fetchStatus).toBe(FetchStatus.ERROR); + expect(value.error).toBeInstanceOf(Error); + done(); + } + }); + sendErrorMsg(main$, new Error('Pls help!')); + }); + + test('sendCompleteMsg cleaning error state message', async (done) => { + const initialState = { + fetchStatus: FetchStatus.ERROR, + error: new Error('Oh noes!'), + }; + const main$ = new BehaviorSubject(initialState); + main$.subscribe((value) => { + if (value.fetchStatus === FetchStatus.COMPLETE) { + const newState = { ...initialState, ...value }; + expect(newState.fetchStatus).toBe(FetchStatus.COMPLETE); + expect(newState.error).toBeUndefined(); + done(); + } + }); + sendCompleteMsg(main$, false); + }); +}); diff --git a/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.ts b/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.ts index b42d699f344cc..ff72a69e65fa8 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_saved_search_messages.ts @@ -27,6 +27,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, + error: undefined, }); } diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx index 50be2473a441e..60841799b1398 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx @@ -144,9 +144,7 @@ describe('Discover flyout', function () { expect(props.setExpandedDoc.mock.calls[0][0]._id).toBe('4'); }); - // EuiFlyout is mocked in Jest environments. - // EUI team to reinstate `onKeyDown`: https://github.com/elastic/eui/issues/4883 - it.skip('allows navigating with arrow keys through documents', () => { + it('allows navigating with arrow keys through documents', () => { const props = getProps(); const component = mountWithIntl(); findTestSubject(component, 'docTableDetailsFlyout').simulate('keydown', { key: 'ArrowRight' }); diff --git a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts new file mode 100644 index 0000000000000..f3edc523f4464 --- /dev/null +++ b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { createSearchSourceMock } from '../../../../../data/common/search/search_source/mocks'; +import { updateSearchSource } from './update_search_source'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { SortOrder } from '../../../saved_searches/types'; + +describe('updateSearchSource', () => { + const defaults = { + sampleSize: 50, + defaultSort: 'asc', + }; + + it('updates a given search source', async () => { + const searchSource = createSearchSourceMock({}); + updateSearchSource(searchSource, indexPatternMock, [] as SortOrder[], false, defaults); + expect(searchSource.getField('fields')).toBe(undefined); + // does not explicitly request fieldsFromSource when not using fields API + expect(searchSource.getField('fieldsFromSource')).toBe(undefined); + }); + + it('updates a given search source with the usage of the new fields api', async () => { + const searchSource = createSearchSourceMock({}); + updateSearchSource(searchSource, indexPatternMock, [] as SortOrder[], true, defaults); + expect(searchSource.getField('fields')).toEqual([{ field: '*', include_unmapped: 'true' }]); + expect(searchSource.getField('fieldsFromSource')).toBe(undefined); + }); +}); diff --git a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.ts b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.ts new file mode 100644 index 0000000000000..1d6c29d65ca85 --- /dev/null +++ b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IndexPattern, ISearchSource } from '../../../../../data/common'; +import { getSortForSearchSource } from '../../apps/main/components/doc_table'; +import { SortPairArr } from '../../apps/main/components/doc_table/lib/get_sort'; + +export const updateSearchSource = ( + searchSource: ISearchSource, + indexPattern: IndexPattern | undefined, + sort: (SortPairArr[] & string[][]) | undefined, + useNewFieldsApi: boolean, + defaults: { + sampleSize: number; + defaultSort: string; + } +) => { + const { sampleSize, defaultSort } = defaults; + searchSource.setField('size', sampleSize); + searchSource.setField('sort', getSortForSearchSource(sort, indexPattern, defaultSort)); + if (useNewFieldsApi) { + searchSource.removeField('fieldsFromSource'); + const fields: Record = { field: '*', include_unmapped: 'true' }; + searchSource.setField('fields', [fields]); + } else { + searchSource.removeField('fields'); + } +}; diff --git a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx index 1981f0228d2c7..362f5b9276c65 100644 --- a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx @@ -42,8 +42,9 @@ import { handleSourceColumnState } from '../angular/helpers'; import { DiscoverGridProps } from '../components/discover_grid/discover_grid'; import { DiscoverGridSettings } from '../components/discover_grid/types'; import { DocTableProps } from '../apps/main/components/doc_table/doc_table_wrapper'; -import { getDefaultSort, getSortForSearchSource } from '../apps/main/components/doc_table'; +import { getDefaultSort } from '../apps/main/components/doc_table'; import { SortOrder } from '../apps/main/components/doc_table/components/table_header/helpers'; +import { updateSearchSource } from './helpers/update_search_source'; export type SearchProps = Partial & Partial & { @@ -143,26 +144,16 @@ export class SavedSearchEmbeddable if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); - searchSource.setField('size', this.services.uiSettings.get(SAMPLE_SIZE_SETTING)); - searchSource.setField( - 'sort', - getSortForSearchSource( - this.searchProps!.sort, - this.searchProps!.indexPattern, - this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING) - ) - ); - if (useNewFieldsApi) { - searchSource.removeField('fieldsFromSource'); - const fields: Record = { field: '*', include_unmapped: 'true' }; - searchSource.setField('fields', [fields]); - } else { - searchSource.removeField('fields'); - if (this.searchProps.indexPattern) { - const fieldNames = this.searchProps.indexPattern.fields.map((field) => field.name); - searchSource.setField('fieldsFromSource', fieldNames); + updateSearchSource( + searchSource, + this.searchProps!.indexPattern, + this.searchProps!.sort, + useNewFieldsApi, + { + sampleSize: this.services.uiSettings.get(SAMPLE_SIZE_SETTING), + defaultSort: this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING), } - } + ); // Log request to inspector this.inspectorAdapters.requests!.reset(); diff --git a/src/plugins/field_formats/common/converters/string.test.ts b/src/plugins/field_formats/common/converters/string.test.ts index d691712b674dd..e5e4023621d3d 100644 --- a/src/plugins/field_formats/common/converters/string.test.ts +++ b/src/plugins/field_formats/common/converters/string.test.ts @@ -111,4 +111,18 @@ describe('String Format', () => { '(empty)' ); }); + + test('does escape value while highlighting', () => { + const string = new StringFormat(); + expect( + stripSpan( + string.convert('', 'html', { + field: { name: 'foo' }, + hit: { + highlight: { foo: ['@kibana-highlighted-field@@/kibana-highlighted-field@'] }, + }, + }) + ) + ).toBe('<img />'); + }); }); diff --git a/src/plugins/field_formats/common/converters/string.ts b/src/plugins/field_formats/common/converters/string.ts index 96cd786147800..a3d571897ef61 100644 --- a/src/plugins/field_formats/common/converters/string.ts +++ b/src/plugins/field_formats/common/converters/string.ts @@ -137,7 +137,7 @@ export class StringFormat extends FieldFormat { } return hit?.highlight?.[field?.name] - ? getHighlightHtml(val, hit.highlight[field.name]) + ? getHighlightHtml(escape(val), hit.highlight[field.name]) : escape(this.textConvert(val)); }; } diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.test.js b/src/plugins/home/public/application/components/tutorial/tutorial.test.js index e9c0b49451e23..c76b20e63ae95 100644 --- a/src/plugins/home/public/application/components/tutorial/tutorial.test.js +++ b/src/plugins/home/public/application/components/tutorial/tutorial.test.js @@ -134,7 +134,7 @@ describe('isCloudEnabled is false', () => { ); await loadTutorialPromise; component.update(); - component.find('#onPremElasticCloud').first().simulate('click'); + component.find('#onPremElasticCloud').first().find('input').simulate('change'); component.update(); expect(component.state('visibleInstructions')).toBe('onPremElasticCloud'); }); diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.test.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.test.tsx new file mode 100644 index 0000000000000..03902792371e7 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.test.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { isUserDataIndex } from './empty_prompts'; +import { MatchedItem, ResolveIndexResponseItemIndexAttrs } from '../../types'; + +describe('isUserDataIndex', () => { + test('system index is not data index', () => { + const systemIndexes: MatchedItem[] = [ + { + name: '.apm-agent-configuration', + tags: [ + { + key: 'index', + name: 'Index', + color: 'default', + }, + ], + item: { + name: '.apm-agent-configuration', + attributes: [ResolveIndexResponseItemIndexAttrs.OPEN], + }, + }, + { + name: '.kibana', + tags: [ + { + key: 'alias', + name: 'Alias', + color: 'default', + }, + ], + item: { + name: '.kibana', + indices: ['.kibana_8.0.0_001'], + }, + }, + ]; + + expect(systemIndexes.some(isUserDataIndex)).toBe(false); + }); + + test('data index is data index', () => { + const dataIndex: MatchedItem = { + name: 'kibana_sample_data_ecommerce', + tags: [ + { + key: 'index', + name: 'Index', + color: 'default', + }, + ], + item: { + name: 'kibana_sample_data_ecommerce', + attributes: [ResolveIndexResponseItemIndexAttrs.OPEN], + }, + }; + + expect(isUserDataIndex(dataIndex)).toBe(true); + }); + + test('fleet asset is not data index', () => { + const fleetAssetIndex: MatchedItem = { + name: 'logs-elastic_agent', + tags: [ + { + key: 'data_stream', + name: 'Data stream', + color: 'primary', + }, + ], + item: { + name: 'logs-elastic_agent', + backing_indices: ['.ds-logs-elastic_agent-2021.08.18-000001'], + timestamp_field: '@timestamp', + }, + }; + + expect(isUserDataIndex(fleetAssetIndex)).toBe(false); + }); + + test('metrics-endpoint.metadata_current_default is not data index', () => { + const fleetAssetIndex: MatchedItem = { + name: 'metrics-endpoint.metadata_current_default', + tags: [{ key: 'index', name: 'Index', color: 'default' }], + item: { + name: 'metrics-endpoint.metadata_current_default', + attributes: [ResolveIndexResponseItemIndexAttrs.OPEN], + }, + }; + expect(isUserDataIndex(fleetAssetIndex)).toBe(false); + }); +}); diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx index 80224dbfb673f..2f1631694e952 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx @@ -7,6 +7,7 @@ */ import React, { useState, useCallback, FC } from 'react'; +import useAsync from 'react-use/lib/useAsync'; import { useKibana } from '../../shared_imports'; @@ -17,6 +18,7 @@ import { getIndices } from '../../lib'; import { EmptyIndexListPrompt } from './empty_index_list_prompt'; import { EmptyIndexPatternPrompt } from './empty_index_pattern_prompt'; import { PromptFooter } from './prompt_footer'; +import { FLEET_ASSETS_TO_IGNORE } from '../../../../data/common'; const removeAliases = (item: MatchedItem) => !((item as unknown) as ResolveIndexResponseItemAlias).indices; @@ -24,25 +26,33 @@ const removeAliases = (item: MatchedItem) => interface Props { onCancel: () => void; allSources: MatchedItem[]; - hasExistingIndexPatterns: boolean; loadSources: () => void; } -export const EmptyPrompts: FC = ({ - hasExistingIndexPatterns, - allSources, - onCancel, - children, - loadSources, -}) => { +export function isUserDataIndex(source: MatchedItem) { + // filter out indices that start with `.` + if (source.name.startsWith('.')) return false; + + // filter out sources from FLEET_ASSETS_TO_IGNORE + if (source.name === FLEET_ASSETS_TO_IGNORE.LOGS_DATA_STREAM_TO_IGNORE) return false; + if (source.name === FLEET_ASSETS_TO_IGNORE.METRICS_DATA_STREAM_TO_IGNORE) return false; + if (source.name === FLEET_ASSETS_TO_IGNORE.METRICS_ENDPOINT_INDEX_TO_IGNORE) return false; + + return true; +} + +export const EmptyPrompts: FC = ({ allSources, onCancel, children, loadSources }) => { const { - services: { docLinks, application, http, searchClient }, + services: { docLinks, application, http, searchClient, indexPatternService }, } = useKibana(); const [remoteClustersExist, setRemoteClustersExist] = useState(false); const [goToForm, setGoToForm] = useState(false); - const hasDataIndices = allSources.some(({ name }: MatchedItem) => !name.startsWith('.')); + const hasDataIndices = allSources.some(isUserDataIndex); + const hasUserIndexPattern = useAsync(() => + indexPatternService.hasUserIndexPattern().catch(() => true) + ); useCallback(() => { let isMounted = true; @@ -63,7 +73,9 @@ export const EmptyPrompts: FC = ({ }; }, [http, hasDataIndices, searchClient]); - if (!hasExistingIndexPatterns && !goToForm) { + if (hasUserIndexPattern.loading) return null; // return null to prevent UI flickering while loading + + if (!hasUserIndexPattern.value && !goToForm) { if (!hasDataIndices && !remoteClustersExist) { // load data return ( diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx index 4f6f7708d90c0..14e1da44c7a35 100644 --- a/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx @@ -338,12 +338,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ ); return ( - + diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx index ef99be4df7cb8..0a436f1541613 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx +++ b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx @@ -81,7 +81,10 @@ export const IndexPatternTable = ({ ); setIndexPatterns(gettedIndexPatterns); setIsLoadingIndexPatterns(false); - if (gettedIndexPatterns.length === 0) { + if ( + gettedIndexPatterns.length === 0 || + !(await data.indexPatterns.hasUserIndexPattern().catch(() => false)) + ) { setShowCreateDialog(true); } })(); diff --git a/src/plugins/interactive_setup/server/elasticsearch_service.test.ts b/src/plugins/interactive_setup/server/elasticsearch_service.test.ts index b8eb7293fd678..546ab7ea8f9c0 100644 --- a/src/plugins/interactive_setup/server/elasticsearch_service.test.ts +++ b/src/plugins/interactive_setup/server/elasticsearch_service.test.ts @@ -308,7 +308,7 @@ describe('ElasticsearchService', () => { mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient); await expect( - setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'] }) + setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'], caFingerprint: 'DE:AD:BE:EF' }) ).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`); expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1); @@ -327,7 +327,11 @@ describe('ElasticsearchService', () => { mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient); await expect( - setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1', 'host2'] }) + setupContract.enroll({ + apiKey: 'apiKey', + hosts: ['host1', 'host2'], + caFingerprint: 'DE:AD:BE:EF', + }) ).rejects.toMatchInlineSnapshot(`[Error: Unable to connect to any of the provided hosts.]`); expect(mockEnrollClient.close).toHaveBeenCalledTimes(2); @@ -351,7 +355,7 @@ describe('ElasticsearchService', () => { ); await expect( - setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'] }) + setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'], caFingerprint: 'DE:AD:BE:EF' }) ).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`); expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1); @@ -404,7 +408,11 @@ some weird+ca/with `; await expect( - setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1', 'host2'] }) + setupContract.enroll({ + apiKey: 'apiKey', + hosts: ['host1', 'host2'], + caFingerprint: 'DE:AD:BE:EF', + }) ).resolves.toEqual({ ca: expectedCa, host: 'host2', @@ -417,14 +425,17 @@ some weird+ca/with // Check that we created clients with the right parameters expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledTimes(3); expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', { + caFingerprint: 'DE:AD:BE:EF', hosts: ['host1'], ssl: { verificationMode: 'none' }, }); expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', { + caFingerprint: 'DE:AD:BE:EF', hosts: ['host2'], ssl: { verificationMode: 'none' }, }); expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('authenticate', { + caFingerprint: 'DE:AD:BE:EF', hosts: ['host2'], serviceAccountToken: 'some-value', ssl: { certificateAuthorities: [expectedCa] }, diff --git a/src/plugins/interactive_setup/server/elasticsearch_service.ts b/src/plugins/interactive_setup/server/elasticsearch_service.ts index cad34e1a4d44a..c88ac0f0798c9 100644 --- a/src/plugins/interactive_setup/server/elasticsearch_service.ts +++ b/src/plugins/interactive_setup/server/elasticsearch_service.ts @@ -34,9 +34,7 @@ import { getDetailedErrorMessage } from './errors'; interface EnrollParameters { apiKey: string; hosts: string[]; - // TODO: Integrate fingerprint check as soon core supports this new option: - // https://github.com/elastic/kibana/pull/108514 - caFingerprint?: string; + caFingerprint: string; } export interface ElasticsearchServiceSetupDeps { @@ -141,10 +139,12 @@ export class ElasticsearchService { * @param apiKey The ApiKey to use to authenticate Kibana enrollment request. * @param hosts The list of Elasticsearch node addresses to enroll with. The addresses are supposed * to point to exactly same Elasticsearch node, potentially available via different network interfaces. + * @param caFingerprint The fingerprint of the root CA certificate that is supposed to sign certificate presented by + * the Elasticsearch node we're enrolling with. Should be in a form of a hex colon-delimited string in upper case. */ private async enroll( elasticsearch: ElasticsearchServicePreboot, - { apiKey, hosts }: EnrollParameters + { apiKey, hosts, caFingerprint }: EnrollParameters ): Promise { const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } }; const elasticsearchConfig: Partial = { @@ -153,10 +153,14 @@ export class ElasticsearchService { // We should iterate through all provided hosts until we find an accessible one. for (const host of hosts) { - this.logger.debug(`Trying to enroll with "${host}" host`); + this.logger.debug( + `Trying to enroll with "${host}" host using "${caFingerprint}" CA fingerprint.` + ); + const enrollClient = elasticsearch.createClient('enroll', { ...elasticsearchConfig, hosts: [host], + caFingerprint, }); let enrollmentResponse; @@ -197,6 +201,7 @@ export class ElasticsearchService { // Now try to use retrieved password and CA certificate to authenticate to this host. const authenticateClient = elasticsearch.createClient('authenticate', { + caFingerprint, hosts: [host], serviceAccountToken: enrollResult.serviceAccountToken.value, ssl: { certificateAuthorities: [enrollResult.ca] }, diff --git a/src/plugins/interactive_setup/server/routes/enroll.test.ts b/src/plugins/interactive_setup/server/routes/enroll.test.ts index 4fc91e5252480..e42248704134a 100644 --- a/src/plugins/interactive_setup/server/routes/enroll.test.ts +++ b/src/plugins/interactive_setup/server/routes/enroll.test.ts @@ -113,7 +113,7 @@ describe('Enroll routes', () => { mockRouteParams.preboot.isSetupOnHold.mockReturnValue(false); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -134,7 +134,7 @@ describe('Enroll routes', () => { ); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -164,7 +164,7 @@ describe('Enroll routes', () => { mockRouteParams.kibanaConfigWriter.isConfigWritable.mockResolvedValue(false); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -203,7 +203,7 @@ describe('Enroll routes', () => { ); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -236,7 +236,7 @@ describe('Enroll routes', () => { ); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -273,7 +273,7 @@ describe('Enroll routes', () => { mockRouteParams.kibanaConfigWriter.writeConfig.mockResolvedValue(); const mockRequest = httpServerMock.createKibanaRequest({ - body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' }, + body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' }, }); await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ @@ -286,7 +286,7 @@ describe('Enroll routes', () => { expect(mockRouteParams.elasticsearch.enroll).toHaveBeenCalledWith({ apiKey: 'some-key', hosts: ['host1', 'host2'], - caFingerprint: 'ab:cd:ef', + caFingerprint: 'DE:AD:BE:EF', }); expect(mockRouteParams.kibanaConfigWriter.writeConfig).toHaveBeenCalledTimes(1); diff --git a/src/plugins/interactive_setup/server/routes/enroll.ts b/src/plugins/interactive_setup/server/routes/enroll.ts index 91b391bf8b109..6a2da28787a4d 100644 --- a/src/plugins/interactive_setup/server/routes/enroll.ts +++ b/src/plugins/interactive_setup/server/routes/enroll.ts @@ -73,12 +73,20 @@ export function defineEnrollRoutes({ }); } + // Convert a plain hex string returned in the enrollment token to a format that ES client + // expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF. + const colonFormattedCaFingerprint = + request.body.caFingerprint + .toUpperCase() + .match(/.{1,2}/g) + ?.join(':') ?? ''; + let enrollResult: EnrollResult; try { enrollResult = await elasticsearch.enroll({ apiKey: request.body.apiKey, hosts: request.body.hosts, - caFingerprint: request.body.caFingerprint, + caFingerprint: colonFormattedCaFingerprint, }); } catch { // For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment diff --git a/src/plugins/kibana_legacy/public/dashboard_config.ts b/src/plugins/kibana_legacy/public/dashboard_config.ts deleted file mode 100644 index 045b4d0599cca..0000000000000 --- a/src/plugins/kibana_legacy/public/dashboard_config.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. - */ - -export interface DashboardConfig { - turnHideWriteControlsOn(): void; - getHideWriteControls(): boolean; -} - -export function getDashboardConfig(hideWriteControls: boolean): DashboardConfig { - let _hideWriteControls = hideWriteControls; - - return { - /** - * Part of the exposed plugin API - do not remove without careful consideration. - * @type {boolean} - */ - turnHideWriteControlsOn() { - _hideWriteControls = true; - }, - getHideWriteControls() { - return _hideWriteControls; - }, - }; -} diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index 6116c0682cb3b..9f79daf0f3505 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -17,10 +17,6 @@ const createStartContract = (): Start => ({ config: { defaultAppId: 'home', }, - dashboardConfig: { - turnHideWriteControlsOn: jest.fn(), - getHideWriteControls: jest.fn(), - }, loadFontAwesome: jest.fn(), loadAngularBootstrap: jest.fn(), }); diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index f60130d367b58..af22ceadaa9e1 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -8,7 +8,6 @@ import { PluginInitializerContext, CoreStart, CoreSetup } from 'kibana/public'; import { ConfigSchema } from '../config'; -import { getDashboardConfig } from './dashboard_config'; import { injectHeaderStyle } from './utils/inject_header_style'; export class KibanaLegacyPlugin { @@ -21,11 +20,6 @@ export class KibanaLegacyPlugin { public start({ application, http: { basePath }, uiSettings }: CoreStart) { injectHeaderStyle(uiSettings); return { - /** - * Used to power dashboard mode. Should be removed when dashboard mode is removed eventually. - * @deprecated - */ - dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls), /** * Loads the font-awesome icon font. Should be removed once the last consumer has migrated to EUI * @deprecated diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx index b6d486a656860..68a469c753ce9 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx @@ -93,9 +93,9 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => useEffect(() => { const fetchIsNewKibanaInstance = async () => { - const resp = await indexPatternService.getTitles(); + const hasUserIndexPattern = await indexPatternService.hasUserIndexPattern().catch(() => true); - setNewKibanaInstance(resp.length === 0); + setNewKibanaInstance(!hasUserIndexPattern); }; fetchIsNewKibanaInstance(); diff --git a/src/plugins/kibana_react/public/table_list_view/__snapshots__/table_list_view.test.tsx.snap b/src/plugins/kibana_react/public/table_list_view/__snapshots__/table_list_view.test.tsx.snap index f56dc56073542..a0c34cfdfee07 100644 --- a/src/plugins/kibana_react/public/table_list_view/__snapshots__/table_list_view.test.tsx.snap +++ b/src/plugins/kibana_react/public/table_list_view/__snapshots__/table_list_view.test.tsx.snap @@ -42,6 +42,52 @@ exports[`TableListView render default empty prompt 1`] = ` `; +exports[`TableListView render default empty prompt with create action when createItem supplied 1`] = ` + + + + + } + title={ +

+ +

+ } + /> +
+`; + exports[`TableListView render list view 1`] = ` { expect(component).toMatchSnapshot(); }); + // avoid trapping users in empty prompt that can not create new items + test('render default empty prompt with create action when createItem supplied', async () => { + const component = shallowWithIntl( {}} />); + + // Using setState to check the final render while sidestepping the debounced promise management + component.setState({ + hasInitialFetchReturned: true, + isFetchingItems: false, + }); + + expect(component).toMatchSnapshot(); + }); + test('render custom empty prompt', () => { const component = shallowWithIntl( } /> diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index d3cbda7c2ab2e..30d09f4bf8657 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -362,6 +362,7 @@ class TableListView extends React.Component } + actions={this.renderCreateButton()} /> ); } diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 54a3fe9e4399c..d29d6d6a86e85 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -134,7 +134,6 @@ export const applicationUsageSchema = { // X-Pack apm: commonSchema, canvas: commonSchema, - dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it enterpriseSearch: commonSchema, appSearch: commonSchema, workplaceSearch: commonSchema, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 8c0d4c3994200..00bb24f5293fa 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -144,10 +144,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'long', _meta: { description: 'Non-default value of setting.' }, }, - 'courier:batchSearches': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'courier:setRequestPreference': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 7e2ab85ea9df3..2375de4a35467 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -60,7 +60,6 @@ export interface UsageStats { 'securitySolution:enableNewsFeed': boolean; 'search:includeFrozen': boolean; 'courier:maxConcurrentShardRequests': number; - 'courier:batchSearches': boolean; 'courier:setRequestPreference': string; 'courier:customRequestPreference': string; 'courier:ignoreFilterIfFieldNotInIndex': boolean; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts index b64c23a2af289..fec314fc6b87e 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_metric/schema.ts @@ -35,7 +35,6 @@ const uiMetricFromDataPluginSchema: MakeSchemaFrom = { apm: commonSchema, csm: commonSchema, canvas: commonSchema, - dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it enterpriseSearch: commonSchema, appSearch: commonSchema, workplaceSearch: commonSchema, diff --git a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_component.tsx b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_component.tsx index 2feb527ff9160..4aff1ff4eee96 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_component.tsx +++ b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_component.tsx @@ -6,133 +6,63 @@ * Side Public License, v 1. */ -import React, { useMemo, useEffect, useState, useCallback, useRef } from 'react'; -import { debounceTime, tap } from 'rxjs/operators'; -import useMount from 'react-use/lib/useMount'; +import React, { useState } from 'react'; import classNames from 'classnames'; -import { Subject } from 'rxjs'; -import { EuiFilterButton, EuiFilterGroup, EuiPopover, EuiSelectableOption } from '@elastic/eui'; -import { - OptionsListDataFetcher, - OptionsListEmbeddable, - OptionsListEmbeddableInput, -} from './options_list_embeddable'; +import { EuiFilterButton, EuiFilterGroup, EuiPopover, EuiSelectableOption } from '@elastic/eui'; +import { Subject } from 'rxjs'; import { OptionsListStrings } from './options_list_strings'; -import { InputControlOutput } from '../../embeddable/types'; import { OptionsListPopover } from './options_list_popover_component'; -import { withEmbeddableSubscription } from '../../../../../../embeddable/public'; import './options_list.scss'; - -const toggleAvailableOptions = ( - indices: number[], - availableOptions: EuiSelectableOption[], - enabled: boolean -) => { - const newAvailableOptions = [...availableOptions]; - indices.forEach((index) => (newAvailableOptions[index].checked = enabled ? 'on' : undefined)); - return newAvailableOptions; -}; - -interface OptionsListProps { - input: OptionsListEmbeddableInput; - fetchData: OptionsListDataFetcher; +import { useStateObservable } from '../../use_state_observable'; + +export interface OptionsListComponentState { + availableOptions?: EuiSelectableOption[]; + selectedOptionsString?: string; + selectedOptionsCount?: number; + twoLineLayout?: boolean; + searchString?: string; + loading: boolean; } -export const OptionsListInner = ({ input, fetchData }: OptionsListProps) => { - const [availableOptions, setAvailableOptions] = useState([]); - const selectedOptions = useRef>(new Set()); - - // raw search string is stored here so it is remembered when popover is closed. - const [searchString, setSearchString] = useState(''); - const [debouncedSearchString, setDebouncedSearchString] = useState(); - +export const OptionsListComponent = ({ + componentStateSubject, + typeaheadSubject, + updateOption, +}: { + componentStateSubject: Subject; + typeaheadSubject: Subject; + updateOption: (index: number) => void; +}) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [loading, setIsLoading] = useState(false); - - const typeaheadSubject = useMemo(() => new Subject(), []); - - useMount(() => { - typeaheadSubject - .pipe( - tap((rawSearchText) => setSearchString(rawSearchText)), - debounceTime(100) - ) - .subscribe((search) => setDebouncedSearchString(search)); - // default selections can be applied here... + const optionsListState = useStateObservable(componentStateSubject, { + loading: true, }); - const { indexPattern, timeRange, filters, field, query } = input; - useEffect(() => { - let canceled = false; - setIsLoading(true); - fetchData({ - search: debouncedSearchString, - indexPattern, - timeRange, - filters, - field, - query, - }).then((newOptions) => { - if (canceled) return; - setIsLoading(false); - // We now have new 'availableOptions', we need to ensure the previously selected options are still selected. - const enabledIndices: number[] = []; - selectedOptions.current?.forEach((selectedOption) => { - const optionIndex = newOptions.findIndex( - (availableOption) => availableOption.label === selectedOption - ); - if (optionIndex >= 0) enabledIndices.push(optionIndex); - }); - newOptions = toggleAvailableOptions(enabledIndices, newOptions, true); - setAvailableOptions(newOptions); - }); - return () => { - canceled = true; - }; - }, [indexPattern, timeRange, filters, field, query, debouncedSearchString, fetchData]); - - const updateItem = useCallback( - (index: number) => { - const item = availableOptions?.[index]; - if (!item) return; - - const toggleOff = availableOptions[index].checked === 'on'; - - const newAvailableOptions = toggleAvailableOptions([index], availableOptions, !toggleOff); - setAvailableOptions(newAvailableOptions); - - if (toggleOff) { - selectedOptions.current.delete(item.label); - } else { - selectedOptions.current.add(item.label); - } - }, - [availableOptions] - ); - - const selectedOptionsString = Array.from(selectedOptions.current).join( - OptionsListStrings.summary.getSeparator() - ); - const selectedOptionsLength = Array.from(selectedOptions.current).length; - - const { twoLineLayout } = input; + const { + selectedOptionsString, + selectedOptionsCount, + availableOptions, + twoLineLayout, + searchString, + loading, + } = optionsListState; const button = ( setIsPopoverOpen((openState) => !openState)} isSelected={isPopoverOpen} - numFilters={availableOptions.length} - hasActiveFilters={selectedOptionsLength > 0} - numActiveFilters={selectedOptionsLength} + numFilters={availableOptions?.length ?? 0} + hasActiveFilters={(selectedOptionsCount ?? 0) > 0} + numActiveFilters={selectedOptionsCount} > - {!selectedOptionsLength ? OptionsListStrings.summary.getPlaceholder() : selectedOptionsString} + {!selectedOptionsCount ? OptionsListStrings.summary.getPlaceholder() : selectedOptionsString} ); @@ -155,7 +85,7 @@ export const OptionsListInner = ({ input, fetchData }: OptionsListProps) => { > { ); }; - -export const OptionsListComponent = withEmbeddableSubscription< - OptionsListEmbeddableInput, - InputControlOutput, - OptionsListEmbeddable, - { fetchData: OptionsListDataFetcher } ->(OptionsListInner); diff --git a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_embeddable.tsx b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_embeddable.tsx index 4dcc4a75dc1f0..bdd3660606b7e 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_embeddable.tsx +++ b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_embeddable.tsx @@ -8,12 +8,39 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { merge, Subject } from 'rxjs'; +import deepEqual from 'fast-deep-equal'; import { EuiSelectableOption } from '@elastic/eui'; +import { tap, debounceTime, map, distinctUntilChanged } from 'rxjs/operators'; -import { OptionsListComponent } from './options_list_component'; +import { esFilters } from '../../../../../../data/public'; +import { OptionsListStrings } from './options_list_strings'; +import { OptionsListComponent, OptionsListComponentState } from './options_list_component'; import { Embeddable } from '../../../../../../embeddable/public'; import { InputControlInput, InputControlOutput } from '../../embeddable/types'; +const toggleAvailableOptions = ( + indices: number[], + availableOptions: EuiSelectableOption[], + enabled?: boolean +) => { + const newAvailableOptions = [...availableOptions]; + indices.forEach((index) => (newAvailableOptions[index].checked = enabled ? 'on' : undefined)); + return newAvailableOptions; +}; + +const diffDataFetchProps = ( + current?: OptionsListDataFetchProps, + last?: OptionsListDataFetchProps +) => { + if (!current || !last) return false; + const { filters: currentFilters, ...currentWithoutFilters } = current; + const { filters: lastFilters, ...lastWithoutFilters } = last; + if (!deepEqual(currentWithoutFilters, lastWithoutFilters)) return false; + if (!esFilters.compareFilters(lastFilters ?? [], currentFilters ?? [])) return false; + return true; +}; + interface OptionsListDataFetchProps { field: string; search?: string; @@ -32,6 +59,7 @@ export interface OptionsListEmbeddableInput extends InputControlInput { field: string; indexPattern: string; multiSelect: boolean; + defaultSelections?: string[]; } export class OptionsListEmbeddable extends Embeddable< OptionsListEmbeddableInput, @@ -42,6 +70,21 @@ export class OptionsListEmbeddable extends Embeddable< private node?: HTMLElement; private fetchData: OptionsListDataFetcher; + // internal state for this input control. + private selectedOptions: Set; + private typeaheadSubject: Subject = new Subject(); + private searchString: string = ''; + + private componentState: OptionsListComponentState; + private componentStateSubject$ = new Subject(); + private updateComponentState(changes: Partial) { + this.componentState = { + ...this.componentState, + ...changes, + }; + this.componentStateSubject$.next(this.componentState); + } + constructor( input: OptionsListEmbeddableInput, output: InputControlOutput, @@ -49,15 +92,118 @@ export class OptionsListEmbeddable extends Embeddable< ) { super(input, output); this.fetchData = fetchData; + + // populate default selections from input + this.selectedOptions = new Set(input.defaultSelections ?? []); + const { selectedOptionsCount, selectedOptionsString } = this.buildSelectedOptionsString(); + + // fetch available options when input changes or when search string has changed + const typeaheadPipe = this.typeaheadSubject.pipe( + tap((newSearchString) => (this.searchString = newSearchString)), + debounceTime(100) + ); + const inputPipe = this.getInput$().pipe( + map( + (newInput) => ({ + field: newInput.field, + indexPattern: newInput.indexPattern, + query: newInput.query, + filters: newInput.filters, + timeRange: newInput.timeRange, + }), + distinctUntilChanged(diffDataFetchProps) + ) + ); + merge(typeaheadPipe, inputPipe).subscribe(this.fetchAvailableOptions); + + // push changes from input into component state + this.getInput$().subscribe((newInput) => { + if (newInput.twoLineLayout !== this.componentState.twoLineLayout) + this.updateComponentState({ twoLineLayout: newInput.twoLineLayout }); + }); + + this.componentState = { + loading: true, + selectedOptionsCount, + selectedOptionsString, + twoLineLayout: input.twoLineLayout, + }; + this.updateComponentState(this.componentState); + } + + private fetchAvailableOptions = async () => { + this.updateComponentState({ loading: true }); + + const { indexPattern, timeRange, filters, field, query } = this.getInput(); + let newOptions = await this.fetchData({ + search: this.searchString, + indexPattern, + timeRange, + filters, + field, + query, + }); + + // We now have new 'availableOptions', we need to ensure the selected options are still selected in the new list. + const enabledIndices: number[] = []; + this.selectedOptions?.forEach((selectedOption) => { + const optionIndex = newOptions.findIndex( + (availableOption) => availableOption.label === selectedOption + ); + if (optionIndex >= 0) enabledIndices.push(optionIndex); + }); + newOptions = toggleAvailableOptions(enabledIndices, newOptions, true); + this.updateComponentState({ loading: false, availableOptions: newOptions }); + }; + + private updateOption = (index: number) => { + const item = this.componentState.availableOptions?.[index]; + if (!item) return; + const toggleOff = item.checked === 'on'; + + // update availableOptions to show selection check marks + const newAvailableOptions = toggleAvailableOptions( + [index], + this.componentState.availableOptions ?? [], + !toggleOff + ); + this.componentState.availableOptions = newAvailableOptions; + + // update selectedOptions string + if (toggleOff) this.selectedOptions.delete(item.label); + else this.selectedOptions.add(item.label); + const { selectedOptionsString, selectedOptionsCount } = this.buildSelectedOptionsString(); + this.updateComponentState({ selectedOptionsString, selectedOptionsCount }); + }; + + private buildSelectedOptionsString(): { + selectedOptionsString: string; + selectedOptionsCount: number; + } { + const selectedOptionsArray = Array.from(this.selectedOptions ?? []); + const selectedOptionsString = selectedOptionsArray.join( + OptionsListStrings.summary.getSeparator() + ); + const selectedOptionsCount = selectedOptionsArray.length; + return { selectedOptionsString, selectedOptionsCount }; } - reload = () => {}; + reload = () => { + this.fetchAvailableOptions(); + }; public render = (node: HTMLElement) => { if (this.node) { ReactDOM.unmountComponentAtNode(this.node); } this.node = node; - ReactDOM.render(, node); + ReactDOM.render( + , + node + ); }; } diff --git a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_popover_component.tsx b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_popover_component.tsx index cd558b99f9aa1..4bfce9eb377e9 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_popover_component.tsx +++ b/src/plugins/presentation_util/public/components/input_controls/control_types/options_list/options_list_popover_component.tsx @@ -23,14 +23,14 @@ import { OptionsListStrings } from './options_list_strings'; interface OptionsListPopoverProps { loading: boolean; typeaheadSubject: Subject; - searchString: string; - updateItem: (index: number) => void; - availableOptions: EuiSelectableOption[]; + searchString?: string; + updateOption: (index: number) => void; + availableOptions?: EuiSelectableOption[]; } export const OptionsListPopover = ({ loading, - updateItem, + updateOption, searchString, typeaheadSubject, availableOptions, @@ -53,7 +53,7 @@ export const OptionsListPopover = ({ updateItem(index)} + onClick={() => updateOption(index)} > {item.label} diff --git a/src/plugins/presentation_util/public/components/input_controls/use_state_observable.ts b/src/plugins/presentation_util/public/components/input_controls/use_state_observable.ts new file mode 100644 index 0000000000000..c317f11979f54 --- /dev/null +++ b/src/plugins/presentation_util/public/components/input_controls/use_state_observable.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 { useEffect, useState } from 'react'; +import { Observable } from 'rxjs'; + +export const useStateObservable = ( + stateObservable: Observable, + initialState: T +) => { + useEffect(() => { + const subscription = stateObservable.subscribe((newState) => setInnerState(newState)); + return () => subscription.unsubscribe(); + }, [stateObservable]); + const [innerState, setInnerState] = useState(initialState); + + return innerState; +}; diff --git a/src/plugins/saved_objects_management/kibana.json b/src/plugins/saved_objects_management/kibana.json index 48e61eb9e4da5..b8207e0627b81 100644 --- a/src/plugins/saved_objects_management/kibana.json +++ b/src/plugins/saved_objects_management/kibana.json @@ -8,7 +8,7 @@ "server": true, "ui": true, "requiredPlugins": ["management", "data"], - "optionalPlugins": ["dashboard", "visualizations", "discover", "home", "savedObjectsTaggingOss", "spacesOss"], + "optionalPlugins": ["dashboard", "visualizations", "discover", "home", "savedObjectsTaggingOss", "spaces"], "extraPublicDirs": ["public/lib"], "requiredBundles": ["kibanaReact", "home"] } diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index a21ad6b7a440a..e5aaec6fa4bbc 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -39,7 +39,7 @@ export const mountManagementSection = async ({ }: MountParams) => { const [ coreStart, - { data, savedObjectsTaggingOss, spacesOss }, + { data, savedObjectsTaggingOss, spaces: spacesApi }, pluginStart, ] = await core.getStartServices(); const { element, history, setBreadcrumbs } = mountParams; @@ -61,8 +61,6 @@ export const mountManagementSection = async ({ return children! as React.ReactElement; }; - const spacesApi = spacesOss?.isSpacesAvailable ? spacesOss : undefined; - ReactDOM.render( diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx index fd938abd2704b..f22f0333ec229 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -13,10 +13,7 @@ import { Query } from '@elastic/eui'; import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { CoreStart, ChromeBreadcrumb } from 'src/core/public'; -import type { - SpacesAvailableStartContract, - SpacesContextProps, -} from 'src/plugins/spaces_oss/public'; +import type { SpacesApi, SpacesContextProps } from '../../../../../x-pack/plugins/spaces/public'; import { DataPublicPluginStart } from '../../../data/public'; import { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; import { @@ -42,7 +39,7 @@ const SavedObjectsTablePage = ({ coreStart: CoreStart; dataStart: DataPublicPluginStart; taggingApi?: SavedObjectsTaggingApi; - spacesApi?: SpacesAvailableStartContract; + spacesApi?: SpacesApi; allowedTypes: string[]; serviceRegistry: ISavedObjectsManagementServiceRegistry; actionRegistry: SavedObjectsManagementActionServiceStart; diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index f4578c4c4b8e1..cc6bd83005463 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; import { ManagementSetup } from '../../management/public'; import { DataPublicPluginStart } from '../../data/public'; import { DashboardStart } from '../../dashboard/public'; @@ -15,7 +16,6 @@ import { DiscoverStart } from '../../discover/public'; import { HomePublicPluginSetup, FeatureCatalogueCategory } from '../../home/public'; import { VisualizationsStart } from '../../visualizations/public'; import { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public'; -import type { SpacesOssPluginStart } from '../../spaces_oss/public'; import { SavedObjectsManagementActionService, SavedObjectsManagementActionServiceSetup, @@ -50,7 +50,7 @@ export interface StartDependencies { visualizations?: VisualizationsStart; discover?: DiscoverStart; savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; - spacesOss?: SpacesOssPluginStart; + spaces?: SpacesPluginStart; } export class SavedObjectsManagementPlugin @@ -116,9 +116,9 @@ export class SavedObjectsManagementPlugin }; } - public start(core: CoreStart, { data }: StartDependencies) { - const actionStart = this.actionService.start(); - const columnStart = this.columnService.start(); + public start(_core: CoreStart, { spaces: spacesApi }: StartDependencies) { + const actionStart = this.actionService.start(spacesApi); + const columnStart = this.columnService.start(spacesApi); return { actions: actionStart, diff --git a/src/plugins/saved_objects_management/public/services/action_service.test.ts b/src/plugins/saved_objects_management/public/services/action_service.test.ts index 609cd5e5d3a04..7a2536611f58a 100644 --- a/src/plugins/saved_objects_management/public/services/action_service.test.ts +++ b/src/plugins/saved_objects_management/public/services/action_service.test.ts @@ -6,6 +6,11 @@ * Side Public License, v 1. */ +import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks'; +import { + CopyToSpaceSavedObjectsManagementAction, + ShareToSpaceSavedObjectsManagementAction, +} from './actions'; import { SavedObjectsManagementActionService, SavedObjectsManagementActionServiceSetup, @@ -44,8 +49,12 @@ describe('SavedObjectsManagementActionRegistry', () => { it('allows actions to be registered and retrieved', () => { const action = createAction('foo'); setup.register(action); - const start = service.start(); - expect(start.getAll()).toContain(action); + const start = service.start(spacesPluginMock.createStartContract()); + expect(start.getAll()).toEqual([ + action, + expect.any(ShareToSpaceSavedObjectsManagementAction), + expect.any(CopyToSpaceSavedObjectsManagementAction), + ]); }); it('does not allow actions with duplicate ids to be registered', () => { diff --git a/src/plugins/saved_objects_management/public/services/action_service.ts b/src/plugins/saved_objects_management/public/services/action_service.ts index 015a4953fe238..b72ca3d2535de 100644 --- a/src/plugins/saved_objects_management/public/services/action_service.ts +++ b/src/plugins/saved_objects_management/public/services/action_service.ts @@ -6,6 +6,11 @@ * Side Public License, v 1. */ +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; +import { + CopyToSpaceSavedObjectsManagementAction, + ShareToSpaceSavedObjectsManagementAction, +} from './actions'; import { SavedObjectsManagementAction } from './types'; export interface SavedObjectsManagementActionServiceSetup { @@ -40,10 +45,21 @@ export class SavedObjectsManagementActionService { }; } - start(): SavedObjectsManagementActionServiceStart { + start(spacesApi?: SpacesApi): SavedObjectsManagementActionServiceStart { + if (spacesApi) { + registerSpacesApiActions(this, spacesApi); + } return { has: (actionId) => this.actions.has(actionId), getAll: () => [...this.actions.values()], }; } } + +function registerSpacesApiActions( + service: SavedObjectsManagementActionService, + spacesApi: SpacesApi +) { + service.setup().register(new ShareToSpaceSavedObjectsManagementAction(spacesApi.ui)); + service.setup().register(new CopyToSpaceSavedObjectsManagementAction(spacesApi.ui)); +} diff --git a/src/plugins/saved_objects_management/public/services/actions/copy_saved_objects_to_space_action.tsx b/src/plugins/saved_objects_management/public/services/actions/copy_saved_objects_to_space_action.tsx new file mode 100644 index 0000000000000..5773f64a1e628 --- /dev/null +++ b/src/plugins/saved_objects_management/public/services/actions/copy_saved_objects_to_space_action.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import type { + CopyToSpaceFlyoutProps, + SpacesApiUi, +} from '../../../../../../x-pack/plugins/spaces/public'; +import type { SavedObjectsManagementRecord } from '../types'; +import { SavedObjectsManagementAction } from '../types'; + +interface WrapperProps { + spacesApiUi: SpacesApiUi; + props: CopyToSpaceFlyoutProps; +} + +const Wrapper = ({ spacesApiUi, props }: WrapperProps) => { + const LazyComponent = useMemo(() => spacesApiUi.components.getCopyToSpaceFlyout, [spacesApiUi]); + + return ; +}; + +export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { + public id: string = 'copy_saved_objects_to_space'; + + public euiAction = { + name: i18n.translate('savedObjectsManagement.copyToSpace.actionTitle', { + defaultMessage: 'Copy to space', + }), + description: i18n.translate('savedObjectsManagement.copyToSpace.actionDescription', { + defaultMessage: 'Make a copy of this saved object in one or more spaces', + }), + icon: 'copy', + type: 'icon', + available: (object: SavedObjectsManagementRecord) => { + return object.meta.namespaceType !== 'agnostic' && !object.meta.hiddenType; + }, + onClick: (object: SavedObjectsManagementRecord) => { + this.start(object); + }, + }; + + constructor(private readonly spacesApiUi: SpacesApiUi) { + super(); + } + + public render = () => { + if (!this.record) { + throw new Error('No record available! `render()` was likely called before `start()`.'); + } + + const props: CopyToSpaceFlyoutProps = { + onClose: this.onClose, + savedObjectTarget: { + type: this.record.type, + id: this.record.id, + namespaces: this.record.namespaces ?? [], + title: this.record.meta.title, + icon: this.record.meta.icon, + }, + }; + + return ; + }; + + private onClose = () => { + this.finish(); + }; +} diff --git a/src/plugins/saved_objects_management/public/services/actions/index.ts b/src/plugins/saved_objects_management/public/services/actions/index.ts new file mode 100644 index 0000000000000..39cde652fd54f --- /dev/null +++ b/src/plugins/saved_objects_management/public/services/actions/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 { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; +export { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx b/src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.test.tsx similarity index 86% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx rename to src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.test.tsx index 9c3a56aac20ad..235f8d4508a64 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx +++ b/src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.test.tsx @@ -1,18 +1,18 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; - -import { uiApiMock } from '../ui_api/mocks'; +import { spacesPluginMock } from '../../../../../../x-pack/plugins/spaces/public/mocks'; +import type { SavedObjectsManagementRecord } from '../types'; import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; describe('ShareToSpaceSavedObjectsManagementAction', () => { const createAction = () => { - const spacesApiUi = uiApiMock.create(); + const { ui: spacesApiUi } = spacesPluginMock.createStartContract(); return new ShareToSpaceSavedObjectsManagementAction(spacesApiUi); }; describe('#euiAction.available', () => { diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx b/src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.tsx similarity index 79% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx rename to src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.tsx index 90dda8ad0b013..e36c13bd8fd8b 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx +++ b/src/plugins/saved_objects_management/public/services/actions/share_saved_objects_to_space_action.tsx @@ -1,17 +1,21 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; -import type { ShareToSpaceFlyoutProps, SpacesApiUi } from 'src/plugins/spaces_oss/public'; -import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public'; +import type { + ShareToSpaceFlyoutProps, + SpacesApiUi, +} from '../../../../../../x-pack/plugins/spaces/public'; +import type { SavedObjectsManagementRecord } from '../types'; +import { SavedObjectsManagementAction } from '../types'; interface WrapperProps { spacesApiUi: SpacesApiUi; @@ -28,10 +32,10 @@ export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManage public id: string = 'share_saved_objects_to_space'; public euiAction = { - name: i18n.translate('xpack.spaces.shareToSpace.actionTitle', { + name: i18n.translate('savedObjectsManagement.shareToSpace.actionTitle', { defaultMessage: 'Share to space', }), - description: i18n.translate('xpack.spaces.shareToSpace.actionDescription', { + description: i18n.translate('savedObjectsManagement.shareToSpace.actionDescription', { defaultMessage: 'Share this saved object to one or more spaces', }), icon: 'share', diff --git a/src/plugins/saved_objects_management/public/services/column_service.test.ts b/src/plugins/saved_objects_management/public/services/column_service.test.ts index 3e18cdaec0c47..581a55fa0066d 100644 --- a/src/plugins/saved_objects_management/public/services/column_service.test.ts +++ b/src/plugins/saved_objects_management/public/services/column_service.test.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks'; +// import { ShareToSpaceSavedObjectsManagementColumn } from './columns'; import { SavedObjectsManagementColumnService, SavedObjectsManagementColumnServiceSetup, @@ -40,8 +42,11 @@ describe('SavedObjectsManagementColumnRegistry', () => { it('allows columns to be registered and retrieved', () => { const column = createColumn('foo'); setup.register(column); - const start = service.start(); - expect(start.getAll()).toContain(column); + const start = service.start(spacesPluginMock.createStartContract()); + expect(start.getAll()).toEqual([ + column, + // expect.any(ShareToSpaceSavedObjectsManagementColumn), + ]); }); it('does not allow columns with duplicate ids to be registered', () => { diff --git a/src/plugins/saved_objects_management/public/services/column_service.ts b/src/plugins/saved_objects_management/public/services/column_service.ts index fb919af2b4028..74c06a3d33218 100644 --- a/src/plugins/saved_objects_management/public/services/column_service.ts +++ b/src/plugins/saved_objects_management/public/services/column_service.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; +// import { ShareToSpaceSavedObjectsManagementColumn } from './columns'; import { SavedObjectsManagementColumn } from './types'; export interface SavedObjectsManagementColumnServiceSetup { @@ -36,9 +38,20 @@ export class SavedObjectsManagementColumnService { }; } - start(): SavedObjectsManagementColumnServiceStart { + start(spacesApi?: SpacesApi): SavedObjectsManagementColumnServiceStart { + if (spacesApi) { + registerSpacesApiColumns(this, spacesApi); + } return { getAll: () => [...this.columns.values()], }; } } + +function registerSpacesApiColumns( + service: SavedObjectsManagementColumnService, + spacesApi: SpacesApi +) { + // Note: this column is hidden for now because no saved objects are shareable. It should be uncommented when at least one saved object type is multi-namespace. + // service.setup().register(new ShareToSpaceSavedObjectsManagementColumn(spacesApi.ui)); +} diff --git a/src/plugins/saved_objects_management/public/services/columns/index.ts b/src/plugins/saved_objects_management/public/services/columns/index.ts new file mode 100644 index 0000000000000..f93c603f9011d --- /dev/null +++ b/src/plugins/saved_objects_management/public/services/columns/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 { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx b/src/plugins/saved_objects_management/public/services/columns/share_saved_objects_to_space_column.tsx similarity index 69% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx rename to src/plugins/saved_objects_management/public/services/columns/share_saved_objects_to_space_column.tsx index 609811cd6b7ce..736b656f15d93 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx +++ b/src/plugins/saved_objects_management/public/services/columns/share_saved_objects_to_space_column.tsx @@ -1,15 +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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import type { SavedObjectsManagementColumn } from 'src/plugins/saved_objects_management/public'; -import type { SpaceListProps, SpacesApiUi } from 'src/plugins/spaces_oss/public'; + +import type { SpaceListProps, SpacesApiUi } from '../../../../../../x-pack/plugins/spaces/public'; +import type { SavedObjectsManagementColumn } from '../types'; interface WrapperProps { spacesApiUi: SpacesApiUi; @@ -28,10 +30,10 @@ export class ShareToSpaceSavedObjectsManagementColumn public euiColumn = { field: 'namespaces', - name: i18n.translate('xpack.spaces.shareToSpace.columnTitle', { + name: i18n.translate('savedObjectsManagement.shareToSpace.columnTitle', { defaultMessage: 'Shared spaces', }), - description: i18n.translate('xpack.spaces.shareToSpace.columnDescription', { + description: i18n.translate('savedObjectsManagement.shareToSpace.columnDescription', { defaultMessage: 'The other spaces that this object is currently shared to', }), render: (namespaces: string[] | undefined) => { diff --git a/src/plugins/saved_objects_management/tsconfig.json b/src/plugins/saved_objects_management/tsconfig.json index 0f26da69acd17..545d4697ca2cd 100644 --- a/src/plugins/saved_objects_management/tsconfig.json +++ b/src/plugins/saved_objects_management/tsconfig.json @@ -20,6 +20,6 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../management/tsconfig.json" }, { "path": "../visualizations/tsconfig.json" }, - { "path": "../spaces_oss/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" }, ] } diff --git a/src/plugins/spaces_oss/README.md b/src/plugins/spaces_oss/README.md deleted file mode 100644 index 73de736d6fb4e..0000000000000 --- a/src/plugins/spaces_oss/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# SpacesOss - -Bridge plugin for consumption of the Spaces feature from OSS plugins. diff --git a/src/plugins/spaces_oss/common/types.ts b/src/plugins/spaces_oss/common/types.ts deleted file mode 100644 index b5c418cf3177e..0000000000000 --- a/src/plugins/spaces_oss/common/types.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** - * A Space. - */ -export interface Space { - /** - * The unique identifier for this space. - * The id becomes part of the "URL Identifier" of the space. - * - * Example: an id of `marketing` would result in the URL identifier of `/s/marketing`. - */ - id: string; - - /** - * Display name for this space. - */ - name: string; - - /** - * Optional description for this space. - */ - description?: string; - - /** - * Optional color (hex code) for this space. - * If neither `color` nor `imageUrl` is specified, then a color will be automatically generated. - */ - color?: string; - - /** - * Optional display initials for this space's avatar. Supports a maximum of 2 characters. - * If initials are not provided, then they will be derived from the space name automatically. - * - * Initials are not displayed if an `imageUrl` has been specified. - */ - initials?: string; - - /** - * Optional base-64 encoded data image url to show as this space's avatar. - * This setting takes precedence over any configured `color` or `initials`. - */ - imageUrl?: string; - - /** - * The set of feature ids that should be hidden within this space. - */ - disabledFeatures: string[]; - - /** - * Indicates that this space is reserved (system controlled). - * Reserved spaces cannot be created or deleted by end-users. - * @private - */ - _reserved?: boolean; -} diff --git a/src/plugins/spaces_oss/kibana.json b/src/plugins/spaces_oss/kibana.json deleted file mode 100644 index 10127634618f1..0000000000000 --- a/src/plugins/spaces_oss/kibana.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "spacesOss", - "owner": { - "name": "Platform Security", - "githubTeam": "kibana-security" - }, - "description": "This plugin exposes a limited set of spaces functionality to OSS plugins.", - "version": "kibana", - "server": false, - "ui": true, - "requiredPlugins": [], - "optionalPlugins": [] -} diff --git a/src/plugins/spaces_oss/public/api.ts b/src/plugins/spaces_oss/public/api.ts deleted file mode 100644 index 7492142f0d792..0000000000000 --- a/src/plugins/spaces_oss/public/api.ts +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 { ReactElement } from 'react'; -import type { Observable } from 'rxjs'; - -import type { Space } from '../common'; - -/** - * Client-side Spaces API. - */ -export interface SpacesApi { - /** - * Observable representing the currently active space. - * The details of the space can change without a full page reload (such as display name, color, etc.) - */ - getActiveSpace$(): Observable; - - /** - * Retrieve the currently active space. - */ - getActiveSpace(): Promise; - - /** - * UI components and services to add spaces capabilities to an application. - */ - ui: SpacesApiUi; -} - -/** - * Function that returns a promise for a lazy-loadable component. - */ -export type LazyComponentFn = (props: T) => ReactElement; - -/** - * UI components and services to add spaces capabilities to an application. - */ -export interface SpacesApiUi { - /** - * Lazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature. - */ - components: SpacesApiUiComponent; - /** - * Redirect the user from a legacy URL to a new URL. This needs to be used if a call to `SavedObjectsClient.resolve()` results in an - * `"aliasMatch"` outcome, which indicates that the user has loaded the page using a legacy URL. Calling this function will trigger a - * client-side redirect to the new URL, and it will display a toast to the user. - * - * Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call - * `SavedObjectsClient.resolve()` (old ID) and the object ID in the result (new ID). For example... - * - * The old object ID is `workpad-123` and the new object ID is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`. - * - * Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1` - * - * New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1` - * - * The protocol, hostname, port, base path, and app path are automatically included. - * - * @param path The path to use for the new URL, optionally including `search` and/or `hash` URL components. - * @param objectNoun The string that is used to describe the object in the toast, e.g., _The **object** you're looking for has a new - * location_. Default value is 'object'. - */ - redirectLegacyUrl: (path: string, objectNoun?: string) => Promise; -} - -/** - * React UI components to be used to display the Spaces feature in any application. - */ -export interface SpacesApiUiComponent { - /** - * Provides a context that is required to render some Spaces components. - */ - getSpacesContextProvider: LazyComponentFn; - /** - * Displays a flyout to edit the spaces that an object is shared to. - * - * Note: must be rendered inside of a SpacesContext. - */ - getShareToSpaceFlyout: LazyComponentFn; - /** - * Displays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for - * any number of spaces that the user is not authorized to see) by default. If more than five named spaces would be displayed, the extras - * (along with the unauthorized spaces indicator, if present) are hidden behind a button. If '*' (aka "All spaces") is present, it - * supersedes all of the above and just displays a single badge without a button. - * - * Note: must be rendered inside of a SpacesContext. - */ - getSpaceList: LazyComponentFn; - /** - * Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which - * indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a - * different object (B). - * - * In this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining - * that there is a conflict, and it includes a button that will redirect the user to object B when clicked. - * - * Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call - * `SavedObjectsClient.resolve()` (A) and the `alias_target_id` value in the response (B). For example... - * - * A is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`. - * - * Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1` - * - * New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1` - */ - getLegacyUrlConflict: LazyComponentFn; - /** - * Displays an avatar for the given space. - */ - getSpaceAvatar: LazyComponentFn; -} - -/** - * Properties for the SpacesContext. - */ -export interface SpacesContextProps { - /** - * If a feature is specified, all Spaces components will treat it appropriately if the feature is disabled in a given Space. - */ - feature?: string; -} - -/** - * Properties for the ShareToSpaceFlyout. - */ -export interface ShareToSpaceFlyoutProps { - /** - * The object to render the flyout for. - */ - savedObjectTarget: ShareToSpaceSavedObjectTarget; - /** - * The EUI icon that is rendered in the flyout's title. - * - * Default is 'share'. - */ - flyoutIcon?: string; - /** - * The string that is rendered in the flyout's title. - * - * Default is 'Edit spaces for object'. - */ - flyoutTitle?: string; - /** - * When enabled, if the object is not yet shared to multiple spaces, a callout will be displayed that suggests the user might want to - * create a copy instead. - * - * Default value is false. - */ - enableCreateCopyCallout?: boolean; - /** - * When enabled, if no other spaces exist _and_ the user has the appropriate privileges, a sentence will be displayed that suggests the - * user might want to create a space. - * - * Default value is false. - */ - enableCreateNewSpaceLink?: boolean; - /** - * When set to 'within-space' (default), the flyout behaves like it is running on a page within the active space, and it will prevent the - * user from removing the object from the active space. - * - * Conversely, when set to 'outside-space', the flyout behaves like it is running on a page outside of any space, so it will allow the - * user to remove the object from the active space. - */ - behaviorContext?: 'within-space' | 'outside-space'; - /** - * Optional handler that is called when the user has saved changes and there are spaces to be added to and/or removed from the object and - * its relatives. If this is not defined, a default handler will be used that calls `/api/spaces/_update_objects_spaces` and displays a - * toast indicating what occurred. - */ - changeSpacesHandler?: ( - objects: Array<{ type: string; id: string }>, - spacesToAdd: string[], - spacesToRemove: string[] - ) => Promise; - /** - * Optional callback when the target object and its relatives are updated. - */ - onUpdate?: (updatedObjects: Array<{ type: string; id: string }>) => void; - /** - * Optional callback when the flyout is closed. - */ - onClose?: () => void; -} - -/** - * Describes the target saved object during a share operation. - */ -export interface ShareToSpaceSavedObjectTarget { - /** - * The object's type. - */ - type: string; - /** - * The object's ID. - */ - id: string; - /** - * The namespaces that the object currently exists in. - */ - namespaces: string[]; - /** - * The EUI icon that is rendered in the flyout's subtitle. - * - * Default is 'empty'. - */ - icon?: string; - /** - * The string that is rendered in the flyout's subtitle. - * - * Default is `${type} [id=${id}]`. - */ - title?: string; - /** - * The string that is used to describe the object in several places, e.g., _Make **object** available in selected spaces only_. - * - * Default value is 'object'. - */ - noun?: string; -} - -/** - * Properties for the SpaceList component. - */ -export interface SpaceListProps { - /** - * The namespaces of a saved object to render into a corresponding list of spaces. - */ - namespaces: string[]; - /** - * Optional limit to the number of spaces that can be displayed in the list. If the number of spaces exceeds this limit, they will be - * hidden behind a "show more" button. Set to 0 to disable. - * - * Default value is 5. - */ - displayLimit?: number; - /** - * When set to 'within-space' (default), the space list behaves like it is running on a page within the active space, and it will omit the - * active space (e.g., it displays a list of all the _other_ spaces that an object is shared to). - * - * Conversely, when set to 'outside-space', the space list behaves like it is running on a page outside of any space, so it will not omit - * the active space. - */ - behaviorContext?: 'within-space' | 'outside-space'; -} - -/** - * Properties for the LegacyUrlConflict component. - */ -export interface LegacyUrlConflictProps { - /** - * The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different - * **object**_. - * - * Default value is 'object'. - */ - objectNoun?: string; - /** - * The ID of the object that is currently shown on the page. - */ - currentObjectId: string; - /** - * The ID of the other object that the legacy URL alias points to. - */ - otherObjectId: string; - /** - * The path to use for the new URL, optionally including `search` and/or `hash` URL components. - */ - otherObjectPath: string; -} - -/** - * Properties for the SpaceAvatar component. - */ -export interface SpaceAvatarProps { - /** The space to represent with an avatar. */ - space: Partial; - - /** The size of the avatar. */ - size?: 's' | 'm' | 'l' | 'xl'; - - /** Optional CSS class(es) to apply. */ - className?: string; - - /** - * When enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers. - * - * Default value is true. - */ - announceSpaceName?: boolean; - - /** - * Whether or not to render the avatar in a disabled state. - * - * Default value is false. - */ - isDisabled?: boolean; -} diff --git a/src/plugins/spaces_oss/public/index.ts b/src/plugins/spaces_oss/public/index.ts deleted file mode 100644 index 9c4d5fd17700c..0000000000000 --- a/src/plugins/spaces_oss/public/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SpacesOssPlugin } from './plugin'; - -export type { - SpacesOssPluginSetup, - SpacesOssPluginStart, - SpacesAvailableStartContract, - SpacesUnavailableStartContract, -} from './types'; - -export type { - LazyComponentFn, - SpacesApi, - SpacesApiUi, - SpacesApiUiComponent, - SpacesContextProps, - ShareToSpaceFlyoutProps, - ShareToSpaceSavedObjectTarget, - SpaceListProps, - LegacyUrlConflictProps, - SpaceAvatarProps, -} from './api'; - -export const plugin = () => new SpacesOssPlugin(); diff --git a/src/plugins/spaces_oss/public/mocks/index.ts b/src/plugins/spaces_oss/public/mocks/index.ts deleted file mode 100644 index dc7b9e34fe822..0000000000000 --- a/src/plugins/spaces_oss/public/mocks/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 { SpacesOssPluginSetup, SpacesOssPluginStart } from '../'; -import { spacesApiMock } from '../api.mock'; - -const createSetupContract = (): jest.Mocked => ({ - registerSpacesApi: jest.fn(), -}); - -const createStartContract = (): jest.Mocked => ({ - isSpacesAvailable: true, - ...spacesApiMock.create(), -}); - -export const spacesOssPluginMock = { - createSetupContract, - createStartContract, -}; diff --git a/src/plugins/spaces_oss/public/plugin.test.ts b/src/plugins/spaces_oss/public/plugin.test.ts deleted file mode 100644 index fcbe1c7d86ce5..0000000000000 --- a/src/plugins/spaces_oss/public/plugin.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { spacesApiMock } from './api.mock'; -import { SpacesOssPlugin } from './plugin'; - -describe('SpacesOssPlugin', () => { - let plugin: SpacesOssPlugin; - - beforeEach(() => { - plugin = new SpacesOssPlugin(); - }); - - describe('#setup', () => { - it('only allows the API to be registered once', async () => { - const spacesApi = spacesApiMock.create(); - const { registerSpacesApi } = plugin.setup(); - - expect(() => registerSpacesApi(spacesApi)).not.toThrow(); - - expect(() => registerSpacesApi(spacesApi)).toThrowErrorMatchingInlineSnapshot( - `"Spaces API can only be registered once"` - ); - }); - }); - - describe('#start', () => { - it('returns the spaces API if registered', async () => { - const spacesApi = spacesApiMock.create(); - const { registerSpacesApi } = plugin.setup(); - - registerSpacesApi(spacesApi); - - const { isSpacesAvailable, ...api } = plugin.start(); - - expect(isSpacesAvailable).toBe(true); - expect(api).toStrictEqual(spacesApi); - }); - - it('does not return the spaces API if not registered', async () => { - plugin.setup(); - - const { isSpacesAvailable, ...api } = plugin.start(); - - expect(isSpacesAvailable).toBe(false); - expect(Object.keys(api)).toHaveLength(0); - }); - }); -}); diff --git a/src/plugins/spaces_oss/public/plugin.ts b/src/plugins/spaces_oss/public/plugin.ts deleted file mode 100644 index 2531453257e3e..0000000000000 --- a/src/plugins/spaces_oss/public/plugin.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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 } from 'src/core/public'; - -import type { SpacesApi } from './api'; -import type { SpacesOssPluginSetup, SpacesOssPluginStart } from './types'; - -export class SpacesOssPlugin implements Plugin { - private api?: SpacesApi; - - constructor() {} - - public setup() { - return { - registerSpacesApi: (provider: SpacesApi) => { - if (this.api) { - throw new Error('Spaces API can only be registered once'); - } - this.api = provider; - }, - }; - } - - public start() { - if (this.api) { - return { - isSpacesAvailable: true as true, - ...this.api!, - }; - } else { - return { - isSpacesAvailable: false as false, - }; - } - } -} diff --git a/src/plugins/spaces_oss/public/types.ts b/src/plugins/spaces_oss/public/types.ts deleted file mode 100644 index df20e9be6eaa1..0000000000000 --- a/src/plugins/spaces_oss/public/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { SpacesApi } from './api'; - -/** - * OSS Spaces plugin start contract when the Spaces feature is enabled. - */ -export interface SpacesAvailableStartContract extends SpacesApi { - /** Indicates if the Spaces feature is enabled. */ - isSpacesAvailable: true; -} - -/** - * OSS Spaces plugin start contract when the Spaces feature is disabled. - * @deprecated The Spaces plugin will always be enabled starting in 8.0. - * @removeBy 8.0 - */ -export interface SpacesUnavailableStartContract { - /** Indicates if the Spaces feature is enabled. */ - isSpacesAvailable: false; -} - -/** - * OSS Spaces plugin setup contract. - */ -export interface SpacesOssPluginSetup { - /** - * Register a provider for the Spaces API. - * - * Only one provider can be registered, subsequent calls to this method will fail. - * - * @param provider the API provider. - * - * @private designed to only be consumed by the `spaces` plugin. - */ - registerSpacesApi(provider: SpacesApi): void; -} - -/** - * OSS Spaces plugin start contract. - */ -export type SpacesOssPluginStart = SpacesAvailableStartContract | SpacesUnavailableStartContract; diff --git a/src/plugins/spaces_oss/tsconfig.json b/src/plugins/spaces_oss/tsconfig.json deleted file mode 100644 index 35942863c1f1b..0000000000000 --- a/src/plugins/spaces_oss/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - ] -} diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index ad19362dbf7db..11b56f045e22c 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -2136,137 +2136,6 @@ } } }, - "dashboard_mode": { - "properties": { - "appId": { - "type": "keyword", - "_meta": { - "description": "The application being tracked" - } - }, - "viewId": { - "type": "keyword", - "_meta": { - "description": "Always `main`" - } - }, - "clicks_total": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application since we started counting them" - } - }, - "clicks_7_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 7 days" - } - }, - "clicks_30_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 30 days" - } - }, - "clicks_90_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application over the last 90 days" - } - }, - "minutes_on_screen_total": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen since we started counting them." - } - }, - "minutes_on_screen_7_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 7 days" - } - }, - "minutes_on_screen_30_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 30 days" - } - }, - "minutes_on_screen_90_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen over the last 90 days" - } - }, - "views": { - "type": "array", - "items": { - "properties": { - "appId": { - "type": "keyword", - "_meta": { - "description": "The application being tracked" - } - }, - "viewId": { - "type": "keyword", - "_meta": { - "description": "The application view being tracked" - } - }, - "clicks_total": { - "type": "long", - "_meta": { - "description": "General number of clicks in the application sub view since we started counting them" - } - }, - "clicks_7_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 7 days" - } - }, - "clicks_30_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 30 days" - } - }, - "clicks_90_days": { - "type": "long", - "_meta": { - "description": "General number of clicks in the active application sub view over the last 90 days" - } - }, - "minutes_on_screen_total": { - "type": "float", - "_meta": { - "description": "Minutes the application sub view is active and on-screen since we started counting them." - } - }, - "minutes_on_screen_7_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" - } - }, - "minutes_on_screen_30_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" - } - }, - "minutes_on_screen_90_days": { - "type": "float", - "_meta": { - "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" - } - } - } - } - } - } - }, "enterpriseSearch": { "properties": { "appId": { @@ -7354,12 +7223,6 @@ "description": "Non-default value of setting." } }, - "courier:batchSearches": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "courier:setRequestPreference": { "type": "keyword", "_meta": { @@ -8487,25 +8350,6 @@ } } }, - "dashboard_mode": { - "type": "array", - "items": { - "properties": { - "key": { - "type": "keyword", - "_meta": { - "description": "The event that is tracked" - } - }, - "value": { - "type": "long", - "_meta": { - "description": "The value of the event" - } - } - } - } - }, "enterpriseSearch": { "type": "array", "items": { diff --git a/src/plugins/vis_type_pie/tsconfig.json b/src/plugins/vis_type_pie/tsconfig.json deleted file mode 100644 index 9640447b35d98..0000000000000 --- a/src/plugins/vis_type_pie/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../charts/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../expressions/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" }, - { "path": "../vis_default_editor/tsconfig.json" }, - { "path": "../field_formats/tsconfig.json" } - ] -} diff --git a/src/plugins/vis_type_tagcloud/kibana.json b/src/plugins/vis_type_tagcloud/kibana.json index 1c427600b5de6..b51d5d49cb7b2 100644 --- a/src/plugins/vis_type_tagcloud/kibana.json +++ b/src/plugins/vis_type_tagcloud/kibana.json @@ -4,7 +4,7 @@ "ui": true, "server": true, "requiredPlugins": ["data", "expressions", "visualizations", "charts"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "visDefaultEditor"], + "requiredBundles": ["kibanaReact", "visDefaultEditor"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_type_tagcloud/public/plugin.ts b/src/plugins/vis_type_tagcloud/public/plugin.ts index b2414762f6e47..06e1c516d9e61 100644 --- a/src/plugins/vis_type_tagcloud/public/plugin.ts +++ b/src/plugins/vis_type_tagcloud/public/plugin.ts @@ -7,20 +7,14 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; import { VisualizationsSetup } from '../../visualizations/public'; import { ChartsPluginSetup } from '../../charts/public'; -import { createTagCloudFn } from './tag_cloud_fn'; import { getTagCloudVisTypeDefinition } from './tag_cloud_type'; -import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService } from './services'; import { ConfigSchema } from '../config'; -import { getTagCloudVisRenderer } from './tag_cloud_vis_renderer'; /** @internal */ export interface TagCloudPluginSetupDependencies { - expressions: ReturnType; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; } @@ -30,11 +24,6 @@ export interface TagCloudVisDependencies { palettes: ChartsPluginSetup['palettes']; } -/** @internal */ -export interface TagCloudVisPluginStartDependencies { - data: DataPublicPluginStart; -} - /** @internal */ export class TagCloudPlugin implements Plugin { initializerContext: PluginInitializerContext; @@ -43,23 +32,13 @@ export class TagCloudPlugin implements Plugin { this.initializerContext = initializerContext; } - public setup( - core: CoreSetup, - { expressions, visualizations, charts }: TagCloudPluginSetupDependencies - ) { + public setup(core: CoreSetup, { visualizations, charts }: TagCloudPluginSetupDependencies) { const visualizationDependencies: TagCloudVisDependencies = { palettes: charts.palettes, }; - expressions.registerFunction(createTagCloudFn); - expressions.registerRenderer(getTagCloudVisRenderer(visualizationDependencies)); - visualizations.createBaseVisualization( - getTagCloudVisTypeDefinition({ - palettes: charts.palettes, - }) - ); - } - public start(core: CoreStart, { data }: TagCloudVisPluginStartDependencies) { - setFormatService(data.fieldFormats); + visualizations.createBaseVisualization(getTagCloudVisTypeDefinition(visualizationDependencies)); } + + public start(core: CoreStart) {} } diff --git a/src/plugins/vis_type_tagcloud/public/services.ts b/src/plugins/vis_type_tagcloud/public/services.ts deleted file mode 100644 index abec36c4aae7b..0000000000000 --- a/src/plugins/vis_type_tagcloud/public/services.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { createGetterSetter } from '../../kibana_utils/public'; -import { DataPublicPluginStart } from '../../data/public'; - -export const [getFormatService, setFormatService] = createGetterSetter< - DataPublicPluginStart['fieldFormats'] ->('data.fieldFormats'); diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts deleted file mode 100644 index bfaf557c6baff..0000000000000 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; - -import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; -import { prepareLogTable, Dimension } from '../../visualizations/public'; -import { TagCloudVisParams, TagCloudVisConfig } from './types'; - -const name = 'tagcloud'; - -interface Arguments extends TagCloudVisConfig { - palette: string; -} - -export interface TagCloudVisRenderValue { - visType: typeof name; - visData: Datatable; - visParams: TagCloudVisParams; - syncColors: boolean; -} - -export type TagcloudExpressionFunctionDefinition = ExpressionFunctionDefinition< - typeof name, - Datatable, - Arguments, - Render ->; - -export const createTagCloudFn = (): TagcloudExpressionFunctionDefinition => ({ - name, - type: 'render', - inputTypes: ['datatable'], - help: i18n.translate('visTypeTagCloud.function.help', { - defaultMessage: 'Tagcloud visualization', - }), - args: { - scale: { - types: ['string'], - default: 'linear', - options: ['linear', 'log', 'square root'], - help: i18n.translate('visTypeTagCloud.function.scale.help', { - defaultMessage: 'Scale to determine font size of a word', - }), - }, - orientation: { - types: ['string'], - default: 'single', - options: ['single', 'right angled', 'multiple'], - help: i18n.translate('visTypeTagCloud.function.orientation.help', { - defaultMessage: 'Orientation of words inside tagcloud', - }), - }, - minFontSize: { - types: ['number'], - default: 18, - help: '', - }, - maxFontSize: { - types: ['number'], - default: 72, - help: '', - }, - showLabel: { - types: ['boolean'], - default: true, - help: '', - }, - palette: { - types: ['string'], - help: i18n.translate('visTypeTagCloud.function.paletteHelpText', { - defaultMessage: 'Defines the chart palette name', - }), - default: 'default', - }, - metric: { - types: ['vis_dimension'], - help: i18n.translate('visTypeTagCloud.function.metric.help', { - defaultMessage: 'metric dimension configuration', - }), - required: true, - }, - bucket: { - types: ['vis_dimension'], - help: i18n.translate('visTypeTagCloud.function.bucket.help', { - defaultMessage: 'bucket dimension configuration', - }), - }, - }, - fn(input, args, handlers) { - const visParams = { - scale: args.scale, - orientation: args.orientation, - minFontSize: args.minFontSize, - maxFontSize: args.maxFontSize, - showLabel: args.showLabel, - metric: args.metric, - ...(args.bucket && { - bucket: args.bucket, - }), - palette: { - type: 'palette', - name: args.palette, - }, - } as TagCloudVisParams; - - if (handlers?.inspectorAdapters?.tables) { - const argsTable: Dimension[] = [ - [ - [args.metric], - i18n.translate('visTypeTagCloud.function.dimension.tagSize', { - defaultMessage: 'Tag size', - }), - ], - ]; - if (args.bucket) { - argsTable.push([ - [args.bucket], - i18n.translate('visTypeTagCloud.function.adimension.tags', { - defaultMessage: 'Tags', - }), - ]); - } - const logTable = prepareLogTable(input, argsTable); - handlers.inspectorAdapters.tables.logDatatable('default', logTable); - } - return { - type: 'render', - as: 'tagloud_vis', - value: { - visData: input, - visType: name, - visParams, - syncColors: handlers?.isSyncColorsEnabled?.() ?? false, - }, - }; - }, -}); diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx b/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx deleted file mode 100644 index 279bfdfffee67..0000000000000 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not 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, { lazy } from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { I18nProvider } from '@kbn/i18n/react'; - -import { VisualizationContainer } from '../../visualizations/public'; -import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers'; -import { TagCloudVisDependencies } from './plugin'; -import { TagCloudVisRenderValue } from './tag_cloud_fn'; - -const TagCloudChart = lazy(() => import('./components/tag_cloud_chart')); - -export const getTagCloudVisRenderer: ( - deps: TagCloudVisDependencies -) => ExpressionRenderDefinition = ({ palettes }) => ({ - name: 'tagloud_vis', - displayName: 'Tag Cloud visualization', - reuseDomNode: true, - render: async (domNode, config, handlers) => { - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); - const palettesRegistry = await palettes.getPalettes(); - - render( - - - - - , - domNode - ); - }, -}); diff --git a/src/plugins/vis_type_tagcloud/public/to_ast.ts b/src/plugins/vis_type_tagcloud/public/to_ast.ts index 8a2fb4e843973..c8810aa0397ee 100644 --- a/src/plugins/vis_type_tagcloud/public/to_ast.ts +++ b/src/plugins/vis_type_tagcloud/public/to_ast.ts @@ -12,7 +12,6 @@ import { } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../visualizations/public'; -import { TagcloudExpressionFunctionDefinition } from './tag_cloud_fn'; import { TagCloudVisParams } from './types'; const prepareDimension = (params: SchemaConfig) => { @@ -41,7 +40,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, para const schemas = getVisSchemas(vis, params); const { scale, orientation, minFontSize, maxFontSize, showLabel, palette } = vis.params; - const tagcloud = buildExpressionFunction('tagcloud', { + const tagcloud = buildExpressionFunction('tagcloud', { scale, orientation, minFontSize, diff --git a/src/plugins/vis_type_tagcloud/public/types.ts b/src/plugins/vis_type_tagcloud/public/types.ts index 7105476670693..d855ae5ab65c6 100644 --- a/src/plugins/vis_type_tagcloud/public/types.ts +++ b/src/plugins/vis_type_tagcloud/public/types.ts @@ -7,7 +7,6 @@ */ import type { ChartsPluginSetup, PaletteOutput } from '../../charts/public'; import type { SerializedFieldFormat } from '../../expressions/public'; -import { ExpressionValueVisDimension } from '../../visualizations/public'; interface Dimension { accessor: number; @@ -25,11 +24,6 @@ interface TagCloudCommonParams { showLabel: boolean; } -export interface TagCloudVisConfig extends TagCloudCommonParams { - metric: ExpressionValueVisDimension; - bucket?: ExpressionValueVisDimension; -} - export interface TagCloudVisParams extends TagCloudCommonParams { palette: PaletteOutput; metric: Dimension; diff --git a/src/plugins/vis_type_tagcloud/tsconfig.json b/src/plugins/vis_type_tagcloud/tsconfig.json index 021237dd7ad5b..043eed06c6bcb 100644 --- a/src/plugins/vis_type_tagcloud/tsconfig.json +++ b/src/plugins/vis_type_tagcloud/tsconfig.json @@ -17,7 +17,6 @@ { "path": "../expressions/tsconfig.json" }, { "path": "../visualizations/tsconfig.json" }, { "path": "../charts/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, { "path": "../vis_default_editor/tsconfig.json" }, ] diff --git a/src/plugins/vis_type_vislib/tsconfig.json b/src/plugins/vis_type_vislib/tsconfig.json deleted file mode 100644 index 0d2a4094f04be..0000000000000 --- a/src/plugins/vis_type_vislib/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../charts/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../expressions/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../kibana_legacy/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../vis_default_editor/tsconfig.json" }, - { "path": "../vis_type_xy/tsconfig.json" }, - { "path": "../vis_type_pie/tsconfig.json" }, - ] -} diff --git a/src/plugins/vis_type_xy/tsconfig.json b/src/plugins/vis_type_xy/tsconfig.json deleted file mode 100644 index 0e4e41c286bd2..0000000000000 --- a/src/plugins/vis_type_xy/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../charts/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../expressions/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../vis_default_editor/tsconfig.json" }, - ] -} diff --git a/src/plugins/vis_types/README.md b/src/plugins/vis_types/README.md new file mode 100644 index 0000000000000..db3d426d4bb48 --- /dev/null +++ b/src/plugins/vis_types/README.md @@ -0,0 +1,19 @@ +# Vis types + +This folder contains all the legacy visualizations plugins. The legacy visualizations are: +- TSVB +- Vega +- All the aggregation-based visualizations + +The structure is: +``` + └ vis_types (just a folder) + + └ pie (previous vis_type_pie) + + └ tagcloud (previous vis_type_tagcloud) + + └ ... +``` + + If their renderer/expression is not shared with any other plugin, it can be contained within the vis_types/* plugin in this folder. If it's sharing a renderer/expression with Lens or Canvas, the renderer must be extracted into the chart_expression folder. diff --git a/src/plugins/vis_type_pie/common/index.ts b/src/plugins/vis_types/pie/common/index.ts similarity index 100% rename from src/plugins/vis_type_pie/common/index.ts rename to src/plugins/vis_types/pie/common/index.ts diff --git a/src/plugins/spaces_oss/jest.config.js b/src/plugins/vis_types/pie/jest.config.js similarity index 84% rename from src/plugins/spaces_oss/jest.config.js rename to src/plugins/vis_types/pie/jest.config.js index 8be5bf6e0fb54..505ea97f489f7 100644 --- a/src/plugins/spaces_oss/jest.config.js +++ b/src/plugins/vis_types/pie/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/spaces_oss'], + rootDir: '../../../..', + roots: ['/src/plugins/vis_types/pie'], }; diff --git a/src/plugins/vis_type_pie/kibana.json b/src/plugins/vis_types/pie/kibana.json similarity index 100% rename from src/plugins/vis_type_pie/kibana.json rename to src/plugins/vis_types/pie/kibana.json diff --git a/src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap similarity index 100% rename from src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap rename to src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap diff --git a/src/plugins/vis_type_pie/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap similarity index 100% rename from src/plugins/vis_type_pie/public/__snapshots__/to_ast.test.ts.snap rename to src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap diff --git a/src/plugins/vis_type_pie/public/chart.scss b/src/plugins/vis_types/pie/public/chart.scss similarity index 100% rename from src/plugins/vis_type_pie/public/chart.scss rename to src/plugins/vis_types/pie/public/chart.scss diff --git a/src/plugins/vis_type_pie/public/components/chart_split.tsx b/src/plugins/vis_types/pie/public/components/chart_split.tsx similarity index 96% rename from src/plugins/vis_type_pie/public/components/chart_split.tsx rename to src/plugins/vis_types/pie/public/components/chart_split.tsx index 46f841113c03d..563d9e9234b66 100644 --- a/src/plugins/vis_type_pie/public/components/chart_split.tsx +++ b/src/plugins/vis_types/pie/public/components/chart_split.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Accessor, AccessorFn, GroupBy, GroupBySort, SmallMultiples } from '@elastic/charts'; -import { DatatableColumn } from '../../../expressions/public'; +import { DatatableColumn } from '../../../../expressions/public'; import { SplitDimensionParams } from '../types'; interface ChartSplitProps { diff --git a/src/plugins/vis_type_pie/public/editor/collections.ts b/src/plugins/vis_types/pie/public/editor/collections.ts similarity index 100% rename from src/plugins/vis_type_pie/public/editor/collections.ts rename to src/plugins/vis_types/pie/public/editor/collections.ts diff --git a/src/plugins/vis_type_pie/public/editor/components/index.tsx b/src/plugins/vis_types/pie/public/editor/components/index.tsx similarity index 91% rename from src/plugins/vis_type_pie/public/editor/components/index.tsx rename to src/plugins/vis_types/pie/public/editor/components/index.tsx index 6bc31208fbdb0..5f7c744db0716 100644 --- a/src/plugins/vis_type_pie/public/editor/components/index.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/index.tsx @@ -7,7 +7,7 @@ */ import React, { lazy } from 'react'; -import { VisEditorOptionsProps } from '../../../../visualizations/public'; +import { VisEditorOptionsProps } from '../../../../../visualizations/public'; import { PieVisParams, PieTypeProps } from '../../types'; const PieOptionsLazy = lazy(() => import('./pie')); diff --git a/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx similarity index 98% rename from src/plugins/vis_type_pie/public/editor/components/pie.test.tsx rename to src/plugins/vis_types/pie/public/editor/components/pie.test.tsx index d37f4c10ea9ea..ee8d0bf19ecac 100644 --- a/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import PieOptions, { PieOptionsProps } from './pie'; -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { findTestSubject } from '@elastic/eui/lib/test'; import { act } from 'react-dom/test-utils'; diff --git a/src/plugins/vis_type_pie/public/editor/components/pie.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.tsx similarity index 98% rename from src/plugins/vis_type_pie/public/editor/components/pie.tsx rename to src/plugins/vis_types/pie/public/editor/components/pie.tsx index 3bf28ba58d4eb..78ae9527da3f9 100644 --- a/src/plugins/vis_type_pie/public/editor/components/pie.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/pie.tsx @@ -27,10 +27,10 @@ import { SelectOption, PalettePicker, LongLegendOptions, -} from '../../../../vis_default_editor/public'; -import { VisEditorOptionsProps } from '../../../../visualizations/public'; +} from '../../../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from '../../../../../visualizations/public'; import { TruncateLabelsOption } from './truncate_labels'; -import { PaletteRegistry } from '../../../../charts/public'; +import { PaletteRegistry } from '../../../../../charts/public'; import { DEFAULT_PERCENT_DECIMALS } from '../../../common'; import { PieVisParams, LabelPositions, ValueFormats, PieTypeProps } from '../../types'; import { getLabelPositions, getValuesFormats } from '../collections'; diff --git a/src/plugins/vis_type_pie/public/editor/components/truncate_labels.test.tsx b/src/plugins/vis_types/pie/public/editor/components/truncate_labels.test.tsx similarity index 100% rename from src/plugins/vis_type_pie/public/editor/components/truncate_labels.test.tsx rename to src/plugins/vis_types/pie/public/editor/components/truncate_labels.test.tsx diff --git a/src/plugins/vis_type_pie/public/editor/components/truncate_labels.tsx b/src/plugins/vis_types/pie/public/editor/components/truncate_labels.tsx similarity index 100% rename from src/plugins/vis_type_pie/public/editor/components/truncate_labels.tsx rename to src/plugins/vis_types/pie/public/editor/components/truncate_labels.tsx diff --git a/src/plugins/vis_type_pie/public/editor/positions.ts b/src/plugins/vis_types/pie/public/editor/positions.ts similarity index 100% rename from src/plugins/vis_type_pie/public/editor/positions.ts rename to src/plugins/vis_types/pie/public/editor/positions.ts diff --git a/src/plugins/vis_type_pie/public/expression_functions/pie_labels.ts b/src/plugins/vis_types/pie/public/expression_functions/pie_labels.ts similarity index 98% rename from src/plugins/vis_type_pie/public/expression_functions/pie_labels.ts rename to src/plugins/vis_types/pie/public/expression_functions/pie_labels.ts index 269d5d5f779d6..eeda49bce4c4c 100644 --- a/src/plugins/vis_type_pie/public/expression_functions/pie_labels.ts +++ b/src/plugins/vis_types/pie/public/expression_functions/pie_labels.ts @@ -11,7 +11,7 @@ import { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; interface Arguments { show: boolean; diff --git a/src/plugins/vis_type_pie/public/index.ts b/src/plugins/vis_types/pie/public/index.ts similarity index 100% rename from src/plugins/vis_type_pie/public/index.ts rename to src/plugins/vis_types/pie/public/index.ts diff --git a/src/plugins/vis_type_pie/public/mocks.ts b/src/plugins/vis_types/pie/public/mocks.ts similarity index 99% rename from src/plugins/vis_type_pie/public/mocks.ts rename to src/plugins/vis_types/pie/public/mocks.ts index 6cf291b8bf370..f7ba4056c77e4 100644 --- a/src/plugins/vis_type_pie/public/mocks.ts +++ b/src/plugins/vis_types/pie/public/mocks.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Datatable } from '../../expressions/public'; +import { Datatable } from '../../../expressions/public'; import { BucketColumns, PieVisParams, LabelPositions, ValueFormats } from './types'; export const createMockBucketColumns = (): BucketColumns[] => { diff --git a/src/plugins/vis_type_pie/public/pie_component.test.tsx b/src/plugins/vis_types/pie/public/pie_component.test.tsx similarity index 97% rename from src/plugins/vis_type_pie/public/pie_component.test.tsx rename to src/plugins/vis_types/pie/public/pie_component.test.tsx index 177396f25adb6..c70cad285f2ae 100644 --- a/src/plugins/vis_type_pie/public/pie_component.test.tsx +++ b/src/plugins/vis_types/pie/public/pie_component.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { Settings, TooltipType, SeriesIdentifier } from '@elastic/charts'; -import { chartPluginMock } from '../../charts/public/mocks'; -import { dataPluginMock } from '../../data/public/mocks'; +import { chartPluginMock } from '../../../charts/public/mocks'; +import { dataPluginMock } from '../../../data/public/mocks'; import { shallow, mount } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; import { act } from 'react-dom/test-utils'; diff --git a/src/plugins/vis_type_pie/public/pie_component.tsx b/src/plugins/vis_types/pie/public/pie_component.tsx similarity index 96% rename from src/plugins/vis_type_pie/public/pie_component.tsx rename to src/plugins/vis_types/pie/public/pie_component.tsx index 9119f2f2ecd6c..a5475a76e27cd 100644 --- a/src/plugins/vis_type_pie/public/pie_component.tsx +++ b/src/plugins/vis_types/pie/public/pie_component.tsx @@ -25,11 +25,15 @@ import { ClickTriggerEvent, ChartsPluginSetup, PaletteRegistry, -} from '../../charts/public'; -import { DataPublicPluginStart } from '../../data/public'; -import type { FieldFormat } from '../../field_formats/common'; -import type { PersistedState } from '../../visualizations/public'; -import { Datatable, DatatableColumn, IInterpreterRenderHandlers } from '../../expressions/public'; +} from '../../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import type { PersistedState } from '../../../visualizations/public'; +import { + Datatable, + DatatableColumn, + IInterpreterRenderHandlers, +} from '../../../expressions/public'; +import type { FieldFormat } from '../../../field_formats/common'; import { DEFAULT_PERCENT_DECIMALS } from '../common'; import { PieVisParams, BucketColumns, ValueFormats, PieContainerDimensions } from './types'; import { diff --git a/src/plugins/vis_type_pie/public/pie_fn.test.ts b/src/plugins/vis_types/pie/public/pie_fn.test.ts similarity index 90% rename from src/plugins/vis_type_pie/public/pie_fn.test.ts rename to src/plugins/vis_types/pie/public/pie_fn.test.ts index 33b5f38cbe630..d0e0af9807f34 100644 --- a/src/plugins/vis_type_pie/public/pie_fn.test.ts +++ b/src/plugins/vis_types/pie/public/pie_fn.test.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; +import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils'; import { createPieVisFn } from './pie_fn'; -import { Datatable } from '../../expressions/common/expression_types/specs'; +import { Datatable } from '../../../expressions/common/expression_types/specs'; describe('interpreter/functions#pie', () => { const fn = functionWrapper(createPieVisFn()); diff --git a/src/plugins/vis_type_pie/public/pie_fn.ts b/src/plugins/vis_types/pie/public/pie_fn.ts similarity index 98% rename from src/plugins/vis_type_pie/public/pie_fn.ts rename to src/plugins/vis_types/pie/public/pie_fn.ts index c5987001d4494..74e8127712399 100644 --- a/src/plugins/vis_type_pie/public/pie_fn.ts +++ b/src/plugins/vis_types/pie/public/pie_fn.ts @@ -7,9 +7,9 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/common'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../../expressions/common'; import { PieVisParams, PieVisConfig } from './types'; -import { prepareLogTable } from '../../visualizations/public'; +import { prepareLogTable } from '../../../visualizations/public'; export const vislibPieName = 'pie_vis'; diff --git a/src/plugins/vis_type_pie/public/pie_renderer.tsx b/src/plugins/vis_types/pie/public/pie_renderer.tsx similarity index 90% rename from src/plugins/vis_type_pie/public/pie_renderer.tsx rename to src/plugins/vis_types/pie/public/pie_renderer.tsx index bcd4cad4efa66..e8fb6311904a6 100644 --- a/src/plugins/vis_type_pie/public/pie_renderer.tsx +++ b/src/plugins/vis_types/pie/public/pie_renderer.tsx @@ -9,9 +9,9 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { ExpressionRenderDefinition } from '../../expressions/public'; -import { VisualizationContainer } from '../../visualizations/public'; -import type { PersistedState } from '../../visualizations/public'; +import { ExpressionRenderDefinition } from '../../../expressions/public'; +import { VisualizationContainer } from '../../../visualizations/public'; +import type { PersistedState } from '../../../visualizations/public'; import { VisTypePieDependencies } from './plugin'; import { RenderValue, vislibPieName } from './pie_fn'; diff --git a/src/plugins/vis_type_pie/public/plugin.ts b/src/plugins/vis_types/pie/public/plugin.ts similarity index 87% rename from src/plugins/vis_type_pie/public/plugin.ts rename to src/plugins/vis_types/pie/public/plugin.ts index 787f49c19aca3..12be6dd5de10f 100644 --- a/src/plugins/vis_type_pie/public/plugin.ts +++ b/src/plugins/vis_types/pie/public/plugin.ts @@ -7,11 +7,11 @@ */ import { CoreSetup, DocLinksStart } from 'src/core/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; -import { ChartsPluginSetup } from '../../charts/public'; -import { UsageCollectionSetup } from '../../usage_collection/public'; -import { DataPublicPluginStart } from '../../data/public'; +import { VisualizationsSetup } from '../../../visualizations/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { ChartsPluginSetup } from '../../../charts/public'; +import { UsageCollectionSetup } from '../../../usage_collection/public'; +import { DataPublicPluginStart } from '../../../data/public'; import { LEGACY_PIE_CHARTS_LIBRARY } from '../common'; import { pieLabels as pieLabelsExpressionFunction } from './expression_functions/pie_labels'; import { createPieVisFn } from './pie_fn'; diff --git a/src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts similarity index 100% rename from src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts rename to src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts diff --git a/src/plugins/vis_type_pie/public/to_ast.test.ts b/src/plugins/vis_types/pie/public/to_ast.test.ts similarity index 94% rename from src/plugins/vis_type_pie/public/to_ast.test.ts rename to src/plugins/vis_types/pie/public/to_ast.test.ts index 019c6e2176710..9d1dba32f2623 100644 --- a/src/plugins/vis_type_pie/public/to_ast.test.ts +++ b/src/plugins/vis_types/pie/public/to_ast.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; +import { Vis } from '../../../visualizations/public'; import { PieVisParams } from './types'; import { samplePieVis } from './sample_vis.test.mocks'; diff --git a/src/plugins/vis_type_pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts similarity index 97% rename from src/plugins/vis_type_pie/public/to_ast.ts rename to src/plugins/vis_types/pie/public/to_ast.ts index b360e375bf40d..fbfffbb77d5fb 100644 --- a/src/plugins/vis_type_pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { getVisSchemas, VisToExpressionAst, SchemaConfig } from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { getVisSchemas, VisToExpressionAst, SchemaConfig } from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; import { PieVisParams, LabelsParams } from './types'; import { vislibPieName, VisTypePieExpressionFunctionDefinition } from './pie_fn'; import { getEsaggsFn } from './to_ast_esaggs'; diff --git a/src/plugins/vis_type_pie/public/to_ast_esaggs.ts b/src/plugins/vis_types/pie/public/to_ast_esaggs.ts similarity index 90% rename from src/plugins/vis_type_pie/public/to_ast_esaggs.ts rename to src/plugins/vis_types/pie/public/to_ast_esaggs.ts index 9b760bd4bebcc..48a7dc50de171 100644 --- a/src/plugins/vis_type_pie/public/to_ast_esaggs.ts +++ b/src/plugins/vis_types/pie/public/to_ast_esaggs.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, -} from '../../data/public'; +} from '../../../data/public'; import { PieVisParams } from './types'; diff --git a/src/plugins/data/common/search/search_source/legacy/index.ts b/src/plugins/vis_types/pie/public/types/index.ts similarity index 100% rename from src/plugins/data/common/search/search_source/legacy/index.ts rename to src/plugins/vis_types/pie/public/types/index.ts diff --git a/src/plugins/vis_type_pie/public/types/types.ts b/src/plugins/vis_types/pie/public/types/types.ts similarity index 92% rename from src/plugins/vis_type_pie/public/types/types.ts rename to src/plugins/vis_types/pie/public/types/types.ts index 94eaeb55f7242..a1f41e80fae28 100644 --- a/src/plugins/vis_type_pie/public/types/types.ts +++ b/src/plugins/vis_types/pie/public/types/types.ts @@ -8,10 +8,10 @@ import { Position } from '@elastic/charts'; import { UiCounterMetricType } from '@kbn/analytics'; -import { DatatableColumn, SerializedFieldFormat } from '../../../expressions/public'; -import { ExpressionValueVisDimension } from '../../../visualizations/public'; +import { DatatableColumn, SerializedFieldFormat } from '../../../../expressions/public'; +import { ExpressionValueVisDimension } from '../../../../visualizations/public'; import { ExpressionValuePieLabels } from '../expression_functions/pie_labels'; -import { PaletteOutput, ChartsPluginSetup } from '../../../charts/public'; +import { PaletteOutput, ChartsPluginSetup } from '../../../../charts/public'; export interface Dimension { accessor: number; diff --git a/src/plugins/vis_type_pie/public/utils/filter_helpers.test.ts b/src/plugins/vis_types/pie/public/utils/filter_helpers.test.ts similarity index 97% rename from src/plugins/vis_type_pie/public/utils/filter_helpers.test.ts rename to src/plugins/vis_types/pie/public/utils/filter_helpers.test.ts index 3f532cf4c384f..f6e20104779fa 100644 --- a/src/plugins/vis_type_pie/public/utils/filter_helpers.test.ts +++ b/src/plugins/vis_types/pie/public/utils/filter_helpers.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 { DatatableColumn } from '../../../expressions/public'; +import { DatatableColumn } from '../../../../expressions/public'; import { getFilterClickData, getFilterEventData } from './filter_helpers'; import { createMockBucketColumns, createMockVisData } from '../mocks'; diff --git a/src/plugins/vis_type_pie/public/utils/filter_helpers.ts b/src/plugins/vis_types/pie/public/utils/filter_helpers.ts similarity index 88% rename from src/plugins/vis_type_pie/public/utils/filter_helpers.ts rename to src/plugins/vis_types/pie/public/utils/filter_helpers.ts index f1a4791821c12..31fff7612faf3 100644 --- a/src/plugins/vis_type_pie/public/utils/filter_helpers.ts +++ b/src/plugins/vis_types/pie/public/utils/filter_helpers.ts @@ -7,11 +7,11 @@ */ import { LayerValue, SeriesIdentifier } from '@elastic/charts'; -import { Datatable, DatatableColumn } from '../../../expressions/public'; -import { DataPublicPluginStart } from '../../../data/public'; -import type { FieldFormat } from '../../../field_formats/common'; -import { ClickTriggerEvent } from '../../../charts/public'; -import { ValueClickContext } from '../../../embeddable/public'; +import { Datatable, DatatableColumn } from '../../../../expressions/public'; +import { DataPublicPluginStart } from '../../../../data/public'; +import { ClickTriggerEvent } from '../../../../charts/public'; +import { ValueClickContext } from '../../../../embeddable/public'; +import type { FieldFormat } from '../../../../field_formats/common'; import { BucketColumns } from '../types'; export const canFilter = async ( diff --git a/src/plugins/vis_type_pie/public/utils/get_color_picker.test.tsx b/src/plugins/vis_types/pie/public/utils/get_color_picker.test.tsx similarity index 96% rename from src/plugins/vis_type_pie/public/utils/get_color_picker.test.tsx rename to src/plugins/vis_types/pie/public/utils/get_color_picker.test.tsx index 5e9087947b95e..bb4cbd8c08ae2 100644 --- a/src/plugins/vis_type_pie/public/utils/get_color_picker.test.tsx +++ b/src/plugins/vis_types/pie/public/utils/get_color_picker.test.tsx @@ -12,8 +12,8 @@ import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { ComponentType, ReactWrapper } from 'enzyme'; import { getColorPicker } from './get_color_picker'; -import { ColorPicker } from '../../../charts/public'; -import type { PersistedState } from '../../../visualizations/public'; +import { ColorPicker } from '../../../../charts/public'; +import type { PersistedState } from '../../../../visualizations/public'; import { createMockBucketColumns, createMockVisData } from '../mocks'; const bucketColumns = createMockBucketColumns(); diff --git a/src/plugins/vis_type_pie/public/utils/get_color_picker.tsx b/src/plugins/vis_types/pie/public/utils/get_color_picker.tsx similarity index 94% rename from src/plugins/vis_type_pie/public/utils/get_color_picker.tsx rename to src/plugins/vis_types/pie/public/utils/get_color_picker.tsx index 628c2d74dc438..68daa7bb82df7 100644 --- a/src/plugins/vis_type_pie/public/utils/get_color_picker.tsx +++ b/src/plugins/vis_types/pie/public/utils/get_color_picker.tsx @@ -10,9 +10,9 @@ import React, { useCallback } from 'react'; import Color from 'color'; import { LegendColorPicker, Position } from '@elastic/charts'; import { PopoverAnchorPosition, EuiPopover, EuiOutsideClickDetector } from '@elastic/eui'; -import type { DatatableRow } from '../../../expressions/public'; -import type { PersistedState } from '../../../visualizations/public'; -import { ColorPicker } from '../../../charts/public'; +import type { DatatableRow } from '../../../../expressions/public'; +import type { PersistedState } from '../../../../visualizations/public'; +import { ColorPicker } from '../../../../charts/public'; import { BucketColumns } from '../types'; const KEY_CODE_ENTER = 13; diff --git a/src/plugins/vis_type_pie/public/utils/get_columns.test.ts b/src/plugins/vis_types/pie/public/utils/get_columns.test.ts similarity index 100% rename from src/plugins/vis_type_pie/public/utils/get_columns.test.ts rename to src/plugins/vis_types/pie/public/utils/get_columns.test.ts diff --git a/src/plugins/vis_type_pie/public/utils/get_columns.ts b/src/plugins/vis_types/pie/public/utils/get_columns.ts similarity index 94% rename from src/plugins/vis_type_pie/public/utils/get_columns.ts rename to src/plugins/vis_types/pie/public/utils/get_columns.ts index 4a32466d808da..c8b8399f0f786 100644 --- a/src/plugins/vis_type_pie/public/utils/get_columns.ts +++ b/src/plugins/vis_types/pie/public/utils/get_columns.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { DatatableColumn, Datatable } from '../../../expressions/public'; +import { DatatableColumn, Datatable } from '../../../../expressions/public'; import { BucketColumns, PieVisParams } from '../types'; export const getColumns = ( diff --git a/src/plugins/vis_type_pie/public/utils/get_config.ts b/src/plugins/vis_types/pie/public/utils/get_config.ts similarity index 100% rename from src/plugins/vis_type_pie/public/utils/get_config.ts rename to src/plugins/vis_types/pie/public/utils/get_config.ts diff --git a/src/plugins/vis_type_pie/public/utils/get_distinct_series.test.ts b/src/plugins/vis_types/pie/public/utils/get_distinct_series.test.ts similarity index 100% rename from src/plugins/vis_type_pie/public/utils/get_distinct_series.test.ts rename to src/plugins/vis_types/pie/public/utils/get_distinct_series.test.ts diff --git a/src/plugins/vis_type_pie/public/utils/get_distinct_series.ts b/src/plugins/vis_types/pie/public/utils/get_distinct_series.ts similarity index 94% rename from src/plugins/vis_type_pie/public/utils/get_distinct_series.ts rename to src/plugins/vis_types/pie/public/utils/get_distinct_series.ts index ba5042dfc210c..8e0111391ec0f 100644 --- a/src/plugins/vis_type_pie/public/utils/get_distinct_series.ts +++ b/src/plugins/vis_types/pie/public/utils/get_distinct_series.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 { DatatableRow } from '../../../expressions/public'; +import { DatatableRow } from '../../../../expressions/public'; import { BucketColumns } from '../types'; export const getDistinctSeries = (rows: DatatableRow[], buckets: Array>) => { diff --git a/src/plugins/vis_type_pie/public/utils/get_layers.test.ts b/src/plugins/vis_types/pie/public/utils/get_layers.test.ts similarity index 95% rename from src/plugins/vis_type_pie/public/utils/get_layers.test.ts rename to src/plugins/vis_types/pie/public/utils/get_layers.test.ts index d6f80b3eb231d..859d0daf07a02 100644 --- a/src/plugins/vis_type_pie/public/utils/get_layers.test.ts +++ b/src/plugins/vis_types/pie/public/utils/get_layers.test.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ import { ShapeTreeNode } from '@elastic/charts'; -import { PaletteDefinition, SeriesLayer } from '../../../charts/public'; -import { dataPluginMock } from '../../../data/public/mocks'; -import type { DataPublicPluginStart } from '../../../data/public'; +import { PaletteDefinition, SeriesLayer } from '../../../../charts/public'; +import { dataPluginMock } from '../../../../data/public/mocks'; +import type { DataPublicPluginStart } from '../../../../data/public'; import { computeColor } from './get_layers'; import { createMockVisData, createMockBucketColumns, createMockPieParams } from '../mocks'; diff --git a/src/plugins/vis_type_pie/public/utils/get_layers.ts b/src/plugins/vis_types/pie/public/utils/get_layers.ts similarity index 97% rename from src/plugins/vis_type_pie/public/utils/get_layers.ts rename to src/plugins/vis_types/pie/public/utils/get_layers.ts index 42c4650419c6b..6ecef858619b5 100644 --- a/src/plugins/vis_type_pie/public/utils/get_layers.ts +++ b/src/plugins/vis_types/pie/public/utils/get_layers.ts @@ -14,9 +14,9 @@ import { ArrayEntry, } from '@elastic/charts'; import { isEqual } from 'lodash'; -import { SeriesLayer, PaletteRegistry, lightenColor } from '../../../charts/public'; -import type { DataPublicPluginStart } from '../../../data/public'; -import type { DatatableRow } from '../../../expressions/public'; +import { SeriesLayer, PaletteRegistry, lightenColor } from '../../../../charts/public'; +import type { DataPublicPluginStart } from '../../../../data/public'; +import type { DatatableRow } from '../../../../expressions/public'; import type { BucketColumns, PieVisParams, SplitDimensionParams } from '../types'; import { getDistinctSeries } from './get_distinct_series'; diff --git a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx b/src/plugins/vis_types/pie/public/utils/get_legend_actions.tsx similarity index 96% rename from src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx rename to src/plugins/vis_types/pie/public/utils/get_legend_actions.tsx index 4ffc458bfd401..cd1d1d71aaa76 100644 --- a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx +++ b/src/plugins/vis_types/pie/public/utils/get_legend_actions.tsx @@ -11,9 +11,9 @@ import React, { useState, useEffect, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover, EuiContextMenu } from '@elastic/eui'; import { LegendAction, SeriesIdentifier } from '@elastic/charts'; -import { DataPublicPluginStart } from '../../../data/public'; +import { DataPublicPluginStart } from '../../../../data/public'; import { PieVisParams } from '../types'; -import { ClickTriggerEvent } from '../../../charts/public'; +import { ClickTriggerEvent } from '../../../../charts/public'; export const getLegendActions = ( canFilter: ( diff --git a/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts b/src/plugins/vis_types/pie/public/utils/get_split_dimension_accessor.ts similarity index 87% rename from src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts rename to src/plugins/vis_types/pie/public/utils/get_split_dimension_accessor.ts index 5addae51dd011..4f30d4f8b3cc4 100644 --- a/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts +++ b/src/plugins/vis_types/pie/public/utils/get_split_dimension_accessor.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ import { AccessorFn } from '@elastic/charts'; -import type { FieldFormatsStart } from '../../../field_formats/public'; -import { DatatableColumn } from '../../../expressions/public'; +import { DatatableColumn } from '../../../../expressions/public'; +import type { FieldFormatsStart } from '../../../../field_formats/public'; import { Dimension } from '../types'; export const getSplitDimensionAccessor = ( diff --git a/src/plugins/vis_type_pie/public/utils/index.ts b/src/plugins/vis_types/pie/public/utils/index.ts similarity index 100% rename from src/plugins/vis_type_pie/public/utils/index.ts rename to src/plugins/vis_types/pie/public/utils/index.ts diff --git a/src/plugins/vis_type_pie/public/vis_type/index.ts b/src/plugins/vis_types/pie/public/vis_type/index.ts similarity index 100% rename from src/plugins/vis_type_pie/public/vis_type/index.ts rename to src/plugins/vis_types/pie/public/vis_type/index.ts diff --git a/src/plugins/vis_type_pie/public/vis_type/pie.ts b/src/plugins/vis_types/pie/public/vis_type/pie.ts similarity index 97% rename from src/plugins/vis_type_pie/public/vis_type/pie.ts rename to src/plugins/vis_types/pie/public/vis_type/pie.ts index 95a9d0d41481b..cfe38442a1548 100644 --- a/src/plugins/vis_type_pie/public/vis_type/pie.ts +++ b/src/plugins/vis_types/pie/public/vis_type/pie.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; -import { AggGroupNames } from '../../../data/public'; -import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../../visualizations/public'; +import { AggGroupNames } from '../../../../data/public'; +import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../../../visualizations/public'; import { DEFAULT_PERCENT_DECIMALS } from '../../common'; import { PieVisParams, LabelPositions, ValueFormats, PieTypeProps } from '../types'; import { toExpressionAst } from '../to_ast'; diff --git a/src/plugins/vis_type_pie/server/index.ts b/src/plugins/vis_types/pie/server/index.ts similarity index 100% rename from src/plugins/vis_type_pie/server/index.ts rename to src/plugins/vis_types/pie/server/index.ts diff --git a/src/plugins/vis_type_pie/server/plugin.ts b/src/plugins/vis_types/pie/server/plugin.ts similarity index 100% rename from src/plugins/vis_type_pie/server/plugin.ts rename to src/plugins/vis_types/pie/server/plugin.ts diff --git a/src/plugins/vis_types/pie/tsconfig.json b/src/plugins/vis_types/pie/tsconfig.json new file mode 100644 index 0000000000000..9a0a3418d72db --- /dev/null +++ b/src/plugins/vis_types/pie/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*" + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../usage_collection/tsconfig.json" }, + { "path": "../../vis_default_editor/tsconfig.json" }, + { "path": "../../field_formats/tsconfig.json" } + ] + } \ No newline at end of file diff --git a/src/plugins/vis_type_vislib/common/index.ts b/src/plugins/vis_types/vislib/common/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/common/index.ts rename to src/plugins/vis_types/vislib/common/index.ts diff --git a/src/plugins/vis_type_pie/jest.config.js b/src/plugins/vis_types/vislib/jest.config.js similarity index 83% rename from src/plugins/vis_type_pie/jest.config.js rename to src/plugins/vis_types/vislib/jest.config.js index e4900ef4a35c8..6b6d7c3361ecf 100644 --- a/src/plugins/vis_type_pie/jest.config.js +++ b/src/plugins/vis_types/vislib/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/vis_type_pie'], + rootDir: '../../../..', + roots: ['/src/plugins/vis_types/vislib'], }; diff --git a/src/plugins/vis_type_vislib/kibana.json b/src/plugins/vis_types/vislib/kibana.json similarity index 100% rename from src/plugins/vis_type_vislib/kibana.json rename to src/plugins/vis_types/vislib/kibana.json diff --git a/src/plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/pie_fn.test.ts.snap similarity index 100% rename from src/plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap rename to src/plugins/vis_types/vislib/public/__snapshots__/pie_fn.test.ts.snap diff --git a/src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap similarity index 100% rename from src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap rename to src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap diff --git a/src/plugins/vis_type_vislib/public/__snapshots__/to_ast_pie.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap similarity index 100% rename from src/plugins/vis_type_vislib/public/__snapshots__/to_ast_pie.test.ts.snap rename to src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap diff --git a/src/plugins/vis_type_vislib/public/area.ts b/src/plugins/vis_types/vislib/public/area.ts similarity index 82% rename from src/plugins/vis_type_vislib/public/area.ts rename to src/plugins/vis_types/vislib/public/area.ts index 3b132ae9be12c..f4ac79e12bbe2 100644 --- a/src/plugins/vis_type_vislib/public/area.ts +++ b/src/plugins/vis_types/vislib/public/area.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { xyVisTypes } from '../../vis_type_xy/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { xyVisTypes } from '../../xy/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; diff --git a/src/plugins/vis_type_vislib/public/editor/collections.ts b/src/plugins/vis_types/vislib/public/editor/collections.ts similarity index 92% rename from src/plugins/vis_type_vislib/public/editor/collections.ts rename to src/plugins/vis_types/vislib/public/editor/collections.ts index cee6901b611ae..e7905ccaf1c29 100644 --- a/src/plugins/vis_type_vislib/public/editor/collections.ts +++ b/src/plugins/vis_types/vislib/public/editor/collections.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; -import { colorSchemas } from '../../../charts/public'; -import { getPositions, getScaleTypes } from '../../../vis_type_xy/public'; +import { colorSchemas } from '../../../../charts/public'; +import { getPositions, getScaleTypes } from '../../../xy/public'; import { Alignment, GaugeType } from '../types'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx b/src/plugins/vis_types/vislib/public/editor/components/gauge/index.tsx similarity index 100% rename from src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx rename to src/plugins/vis_types/vislib/public/editor/components/gauge/index.tsx diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx b/src/plugins/vis_types/vislib/public/editor/components/gauge/labels_panel.tsx similarity index 96% rename from src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx rename to src/plugins/vis_types/vislib/public/editor/components/gauge/labels_panel.tsx index a5fb435da4550..ae200892cec57 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx +++ b/src/plugins/vis_types/vislib/public/editor/components/gauge/labels_panel.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SwitchOption, TextInputOption } from '../../../../../vis_default_editor/public'; +import { SwitchOption, TextInputOption } from '../../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInternalProps) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx b/src/plugins/vis_types/vislib/public/editor/components/gauge/ranges_panel.tsx similarity index 97% rename from src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx rename to src/plugins/vis_types/vislib/public/editor/components/gauge/ranges_panel.tsx index 5091c29c28752..0cb6d7d5940fd 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx +++ b/src/plugins/vis_types/vislib/public/editor/components/gauge/ranges_panel.tsx @@ -16,8 +16,8 @@ import { SwitchOption, ColorSchemaOptions, PercentageModeOption, -} from '../../../../../vis_default_editor/public'; -import { ColorSchemaParams, ColorSchemas, colorSchemas } from '../../../../../charts/public'; +} from '../../../../../../vis_default_editor/public'; +import { ColorSchemaParams, ColorSchemas, colorSchemas } from '../../../../../../charts/public'; import { GaugeOptionsInternalProps } from '../gauge'; import { Gauge } from '../../../gauge'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx b/src/plugins/vis_types/vislib/public/editor/components/gauge/style_panel.tsx similarity index 93% rename from src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx rename to src/plugins/vis_types/vislib/public/editor/components/gauge/style_panel.tsx index 79e4ed96cadec..30bdab93cdfa8 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx +++ b/src/plugins/vis_types/vislib/public/editor/components/gauge/style_panel.tsx @@ -11,9 +11,9 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption } from '../../../../../vis_default_editor/public'; +import { SelectOption } from '../../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; -import { AggGroupNames } from '../../../../../data/public'; +import { AggGroupNames } from '../../../../../../data/public'; import { getGaugeCollections } from './../../collections'; const gaugeCollections = getGaugeCollections(); diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_types/vislib/public/editor/components/heatmap/index.tsx similarity index 98% rename from src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx rename to src/plugins/vis_types/vislib/public/editor/components/heatmap/index.tsx index bdabded67a74a..c0d89f2f66958 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx +++ b/src/plugins/vis_types/vislib/public/editor/components/heatmap/index.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; -import { ValueAxis } from '../../../../../vis_type_xy/public'; +import { ValueAxis } from '../../../../../xy/public'; import { BasicOptions, SelectOption, @@ -24,7 +24,7 @@ import { ColorSchemaOptions, NumberInputOption, PercentageModeOption, -} from '../../../../../vis_default_editor/public'; +} from '../../../../../../vis_default_editor/public'; import { HeatmapVisParams } from '../../../heatmap'; import { LabelsPanel } from './labels_panel'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx b/src/plugins/vis_types/vislib/public/editor/components/heatmap/labels_panel.tsx similarity index 96% rename from src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx rename to src/plugins/vis_types/vislib/public/editor/components/heatmap/labels_panel.tsx index 206900959a35b..05b8949901e79 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx +++ b/src/plugins/vis_types/vislib/public/editor/components/heatmap/labels_panel.tsx @@ -13,8 +13,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; -import { SwitchOption } from '../../../../../vis_default_editor/public'; -import { ValueAxis } from '../../../../../vis_type_xy/public'; +import { SwitchOption } from '../../../../../../vis_default_editor/public'; +import { ValueAxis } from '../../../../../xy/public'; import { HeatmapVisParams } from '../../../heatmap'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/index.tsx b/src/plugins/vis_types/vislib/public/editor/components/index.tsx similarity index 100% rename from src/plugins/vis_type_vislib/public/editor/components/index.tsx rename to src/plugins/vis_types/vislib/public/editor/components/index.tsx diff --git a/src/plugins/vis_type_vislib/public/editor/index.ts b/src/plugins/vis_types/vislib/public/editor/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/editor/index.ts rename to src/plugins/vis_types/vislib/public/editor/index.ts diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_normal.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_config_normal.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_normal.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_config_normal.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_percentage.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_config_percentage.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_percentage.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_config_percentage.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_d3.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_d3.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_d3.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_d3.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_data_point.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_data_point.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_data_point.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_bar_chart_data_point.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_config.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_config.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_config.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_config.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_d3.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_d3.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_d3.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_d3.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_data_point.json b/src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_data_point.json similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_data_point.json rename to src/plugins/vis_types/vislib/public/fixtures/dispatch_heatmap_data_point.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_neg.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_neg.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/filters/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_geo_json.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_geo_json.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_geo_json.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_geo_json.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/geohash/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_slices.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_slices.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_slices.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/histogram/_slices.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/not_enough_data/_one_point.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/not_enough_data/_one_point.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/not_enough_data/_one_point.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/not_enough_data/_one_point.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/range/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/significant_terms/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/stacked/_stacked.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/stacked/_stacked.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/stacked/_stacked.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/stacked/_stacked.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_columns.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_columns.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_rows.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_rows.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_rows.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_rows.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_series.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple.js b/src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_series_multiple.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple.js rename to src/plugins/vis_types/vislib/public/fixtures/mock_data/terms/_series_multiple.js diff --git a/src/plugins/vis_type_vislib/public/fixtures/mocks.js b/src/plugins/vis_types/vislib/public/fixtures/mocks.js similarity index 100% rename from src/plugins/vis_type_vislib/public/fixtures/mocks.js rename to src/plugins/vis_types/vislib/public/fixtures/mocks.js diff --git a/src/plugins/vis_type_vislib/public/gauge.ts b/src/plugins/vis_types/vislib/public/gauge.ts similarity index 93% rename from src/plugins/vis_type_vislib/public/gauge.ts rename to src/plugins/vis_types/vislib/public/gauge.ts index fa463bea6f27f..e03abf5d90cbe 100644 --- a/src/plugins/vis_type_vislib/public/gauge.ts +++ b/src/plugins/vis_types/vislib/public/gauge.ts @@ -8,10 +8,10 @@ import { i18n } from '@kbn/i18n'; -import { ColorMode, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public'; -import { RangeValues } from '../../vis_default_editor/public'; -import { AggGroupNames } from '../../data/public'; -import { VisTypeDefinition, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; +import { ColorMode, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../../charts/public'; +import { RangeValues } from '../../../vis_default_editor/public'; +import { AggGroupNames } from '../../../data/public'; +import { VisTypeDefinition, VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; import { Alignment, GaugeType, VislibChartType } from './types'; import { toExpressionAst } from './to_ast'; diff --git a/src/plugins/vis_type_vislib/public/goal.ts b/src/plugins/vis_types/vislib/public/goal.ts similarity index 93% rename from src/plugins/vis_type_vislib/public/goal.ts rename to src/plugins/vis_types/vislib/public/goal.ts index e594122871fe7..5e6074b12ce47 100644 --- a/src/plugins/vis_type_vislib/public/goal.ts +++ b/src/plugins/vis_types/vislib/public/goal.ts @@ -8,9 +8,9 @@ import { i18n } from '@kbn/i18n'; -import { AggGroupNames } from '../../data/public'; -import { ColorMode, ColorSchemas } from '../../charts/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { AggGroupNames } from '../../../data/public'; +import { ColorMode, ColorSchemas } from '../../../charts/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { GaugeOptions } from './editor'; import { toExpressionAst } from './to_ast'; diff --git a/src/plugins/vis_type_vislib/public/heatmap.ts b/src/plugins/vis_types/vislib/public/heatmap.ts similarity index 91% rename from src/plugins/vis_type_vislib/public/heatmap.ts rename to src/plugins/vis_types/vislib/public/heatmap.ts index f3f320b3658a0..3ea3a4b1e4a06 100644 --- a/src/plugins/vis_type_vislib/public/heatmap.ts +++ b/src/plugins/vis_types/vislib/public/heatmap.ts @@ -9,11 +9,11 @@ import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; -import { RangeValues } from '../../vis_default_editor/public'; -import { AggGroupNames } from '../../data/public'; -import { ColorSchemas, ColorSchemaParams } from '../../charts/public'; -import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../visualizations/public'; -import { ValueAxis, ScaleType, AxisType } from '../../vis_type_xy/public'; +import { RangeValues } from '../../../vis_default_editor/public'; +import { AggGroupNames } from '../../../data/public'; +import { ColorSchemas, ColorSchemaParams } from '../../../charts/public'; +import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../../visualizations/public'; +import { ValueAxis, ScaleType, AxisType } from '../../xy/public'; import { HeatmapOptions } from './editor'; import { TimeMarker } from './vislib/visualizations/time_marker'; diff --git a/src/plugins/vis_type_vislib/public/histogram.ts b/src/plugins/vis_types/vislib/public/histogram.ts similarity index 82% rename from src/plugins/vis_type_vislib/public/histogram.ts rename to src/plugins/vis_types/vislib/public/histogram.ts index e7200a9ff30aa..bb4f570c6a2d8 100644 --- a/src/plugins/vis_type_vislib/public/histogram.ts +++ b/src/plugins/vis_types/vislib/public/histogram.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { xyVisTypes } from '../../vis_type_xy/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { xyVisTypes } from '../../xy/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; diff --git a/src/plugins/vis_type_vislib/public/horizontal_bar.ts b/src/plugins/vis_types/vislib/public/horizontal_bar.ts similarity index 83% rename from src/plugins/vis_type_vislib/public/horizontal_bar.ts rename to src/plugins/vis_types/vislib/public/horizontal_bar.ts index 70f0372025e3e..37aa79a0b1aee 100644 --- a/src/plugins/vis_type_vislib/public/horizontal_bar.ts +++ b/src/plugins/vis_types/vislib/public/horizontal_bar.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { xyVisTypes } from '../../vis_type_xy/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { xyVisTypes } from '../../xy/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; diff --git a/src/plugins/vis_type_vislib/public/index.scss b/src/plugins/vis_types/vislib/public/index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/index.scss rename to src/plugins/vis_types/vislib/public/index.scss diff --git a/src/plugins/vis_type_vislib/public/index.ts b/src/plugins/vis_types/vislib/public/index.ts similarity index 89% rename from src/plugins/vis_type_vislib/public/index.ts rename to src/plugins/vis_types/vislib/public/index.ts index 2a063e4d8a7f4..232e0494a9ebf 100644 --- a/src/plugins/vis_type_vislib/public/index.ts +++ b/src/plugins/vis_types/vislib/public/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PluginInitializerContext } from '../../../core/public'; +import { PluginInitializerContext } from '../../../../core/public'; import { VisTypeVislibPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/vis_type_vislib/public/line.ts b/src/plugins/vis_types/vislib/public/line.ts similarity index 82% rename from src/plugins/vis_type_vislib/public/line.ts rename to src/plugins/vis_types/vislib/public/line.ts index d91bb5d0384b6..0f33c393e0643 100644 --- a/src/plugins/vis_type_vislib/public/line.ts +++ b/src/plugins/vis_types/vislib/public/line.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { xyVisTypes } from '../../vis_type_xy/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { xyVisTypes } from '../../xy/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; diff --git a/src/plugins/vis_type_vislib/public/pie.ts b/src/plugins/vis_types/vislib/public/pie.ts similarity index 86% rename from src/plugins/vis_type_vislib/public/pie.ts rename to src/plugins/vis_types/vislib/public/pie.ts index 4f6eb7e536509..45794776bc998 100644 --- a/src/plugins/vis_type_vislib/public/pie.ts +++ b/src/plugins/vis_types/vislib/public/pie.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { pieVisType } from '../../vis_type_pie/public'; -import { VisTypeDefinition } from '../../visualizations/public'; +import { pieVisType } from '../../pie/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { CommonVislibParams } from './types'; import { toExpressionAst } from './to_ast_pie'; diff --git a/src/plugins/vis_type_vislib/public/pie_fn.test.ts b/src/plugins/vis_types/vislib/public/pie_fn.test.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/pie_fn.test.ts rename to src/plugins/vis_types/vislib/public/pie_fn.test.ts index 4291b5c05fc39..0df7bf1365bea 100644 --- a/src/plugins/vis_type_vislib/public/pie_fn.test.ts +++ b/src/plugins/vis_types/vislib/public/pie_fn.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; +import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils'; import { createPieVisFn } from './pie_fn'; // @ts-ignore import { vislibSlicesResponseHandler } from './vislib/response_handler'; diff --git a/src/plugins/vis_type_vislib/public/pie_fn.ts b/src/plugins/vis_types/vislib/public/pie_fn.ts similarity index 98% rename from src/plugins/vis_type_vislib/public/pie_fn.ts rename to src/plugins/vis_types/vislib/public/pie_fn.ts index 8776a6bc2d18a..dd5d2689af74d 100644 --- a/src/plugins/vis_type_vislib/public/pie_fn.ts +++ b/src/plugins/vis_types/vislib/public/pie_fn.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../../expressions/public'; // @ts-ignore import { vislibSlicesResponseHandler } from './vislib/response_handler'; diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_types/vislib/public/plugin.ts similarity index 83% rename from src/plugins/vis_type_vislib/public/plugin.ts rename to src/plugins/vis_types/vislib/public/plugin.ts index cdc02aacafa3b..24ba7741cab91 100644 --- a/src/plugins/vis_type_vislib/public/plugin.ts +++ b/src/plugins/vis_types/vislib/public/plugin.ts @@ -8,13 +8,13 @@ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { ChartsPluginSetup } from '../../charts/public'; -import { DataPublicPluginStart } from '../../data/public'; -import { KibanaLegacyStart } from '../../kibana_legacy/public'; -import { LEGACY_CHARTS_LIBRARY } from '../../vis_type_xy/common/index'; -import { LEGACY_PIE_CHARTS_LIBRARY } from '../../vis_type_pie/common/index'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { VisualizationsSetup } from '../../../visualizations/public'; +import { ChartsPluginSetup } from '../../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { KibanaLegacyStart } from '../../../kibana_legacy/public'; +import { LEGACY_CHARTS_LIBRARY } from '../../xy/common/index'; +import { LEGACY_PIE_CHARTS_LIBRARY } from '../../pie/common/index'; import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn'; import { createPieVisFn } from './pie_fn'; diff --git a/src/plugins/vis_type_vislib/public/services.ts b/src/plugins/vis_types/vislib/public/services.ts similarity index 82% rename from src/plugins/vis_type_vislib/public/services.ts rename to src/plugins/vis_types/vislib/public/services.ts index 00e3ed0791e5d..d111007598b8b 100644 --- a/src/plugins/vis_type_vislib/public/services.ts +++ b/src/plugins/vis_types/vislib/public/services.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { createGetterSetter } from '../../kibana_utils/public'; -import { DataPublicPluginStart } from '../../data/public'; +import { createGetterSetter } from '../../../kibana_utils/public'; +import { DataPublicPluginStart } from '../../../data/public'; export const [getDataActions, setDataActions] = createGetterSetter< DataPublicPluginStart['actions'] diff --git a/src/plugins/vis_type_vislib/public/to_ast.test.ts b/src/plugins/vis_types/vislib/public/to_ast.test.ts similarity index 78% rename from src/plugins/vis_type_vislib/public/to_ast.test.ts rename to src/plugins/vis_types/vislib/public/to_ast.test.ts index d4e4d4fcdd1dd..70a1f938a8266 100644 --- a/src/plugins/vis_type_vislib/public/to_ast.test.ts +++ b/src/plugins/vis_types/vislib/public/to_ast.test.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression } from '../../../expressions/public'; import { BasicVislibParams } from './types'; import { toExpressionAst } from './to_ast'; -import { sampleAreaVis } from '../../vis_type_xy/public/sample_vis.test.mocks'; +import { sampleAreaVis } from '../../xy/public/sample_vis.test.mocks'; -jest.mock('../../expressions/public', () => ({ - ...(jest.requireActual('../../expressions/public') as any), +jest.mock('../../../expressions/public', () => ({ + ...(jest.requireActual('../../../expressions/public') as any), buildExpression: jest.fn().mockImplementation(() => ({ toAst: () => ({ type: 'expression', diff --git a/src/plugins/vis_type_vislib/public/to_ast.ts b/src/plugins/vis_types/vislib/public/to_ast.ts similarity index 94% rename from src/plugins/vis_type_vislib/public/to_ast.ts rename to src/plugins/vis_types/vislib/public/to_ast.ts index 1e33c589ff1fc..c1de94ba0f5f9 100644 --- a/src/plugins/vis_type_vislib/public/to_ast.ts +++ b/src/plugins/vis_types/vislib/public/to_ast.ts @@ -13,12 +13,12 @@ import { VisToExpressionAstParams, getVisSchemas, VisParams, -} from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import type { Dimensions } from '../../vis_type_xy/public'; -import type { DateHistogramParams, HistogramParams } from '../../visualizations/public'; +} from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; +import type { Dimensions } from '../../xy/public'; +import type { DateHistogramParams, HistogramParams } from '../../../visualizations/public'; -import { BUCKET_TYPES } from '../../data/public'; +import { BUCKET_TYPES } from '../../../data/public'; import { vislibVisName, VisTypeVislibExpressionFunctionDefinition } from './vis_type_vislib_vis_fn'; import { BasicVislibParams, VislibChartType } from './types'; diff --git a/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts b/src/plugins/vis_types/vislib/public/to_ast_esaggs.ts similarity index 91% rename from src/plugins/vis_type_vislib/public/to_ast_esaggs.ts rename to src/plugins/vis_types/vislib/public/to_ast_esaggs.ts index c7e351ccc0429..d34989917e707 100644 --- a/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts +++ b/src/plugins/vis_types/vislib/public/to_ast_esaggs.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, -} from '../../data/public'; +} from '../../../data/public'; /** * Get esaggs expressions function diff --git a/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts b/src/plugins/vis_types/vislib/public/to_ast_pie.test.ts similarity index 78% rename from src/plugins/vis_type_vislib/public/to_ast_pie.test.ts rename to src/plugins/vis_types/vislib/public/to_ast_pie.test.ts index 3178c23ee8fa0..6202df3f65094 100644 --- a/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts +++ b/src/plugins/vis_types/vislib/public/to_ast_pie.test.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression } from '../../../expressions/public'; import { PieVisParams } from './pie'; -import { samplePieVis } from '../../vis_type_pie/public/sample_vis.test.mocks'; +import { samplePieVis } from '../../pie/public/sample_vis.test.mocks'; import { toExpressionAst } from './to_ast_pie'; -jest.mock('../../expressions/public', () => ({ - ...(jest.requireActual('../../expressions/public') as any), +jest.mock('../../../expressions/public', () => ({ + ...(jest.requireActual('../../../expressions/public') as any), buildExpression: jest.fn().mockImplementation(() => ({ toAst: () => ({ type: 'expression', diff --git a/src/plugins/vis_type_vislib/public/to_ast_pie.ts b/src/plugins/vis_types/vislib/public/to_ast_pie.ts similarity index 91% rename from src/plugins/vis_type_vislib/public/to_ast_pie.ts rename to src/plugins/vis_types/vislib/public/to_ast_pie.ts index 05a887b5513a3..90c181f8ac74e 100644 --- a/src/plugins/vis_type_vislib/public/to_ast_pie.ts +++ b/src/plugins/vis_types/vislib/public/to_ast_pie.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { getVisSchemas, VisToExpressionAst } from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; import { PieVisParams } from './pie'; import { vislibPieName, VisTypeVislibPieExpressionFunctionDefinition } from './pie_fn'; diff --git a/src/plugins/vis_type_vislib/public/types.ts b/src/plugins/vis_types/vislib/public/types.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/types.ts rename to src/plugins/vis_types/vislib/public/types.ts index d5432a12624fb..5196f0e33f404 100644 --- a/src/plugins/vis_type_vislib/public/types.ts +++ b/src/plugins/vis_types/vislib/public/types.ts @@ -9,7 +9,7 @@ import { $Values } from '@kbn/utility-types'; import { Position } from '@elastic/charts'; -import { Labels } from '../../charts/public'; +import { Labels } from '../../../charts/public'; import { CategoryAxis, Dimensions, @@ -17,7 +17,7 @@ import { SeriesParam, ThresholdLine, ValueAxis, -} from '../../vis_type_xy/public'; +} from '../../../vis_types/xy/public'; import { TimeMarker } from './vislib/visualizations/time_marker'; /** diff --git a/src/plugins/vis_type_vislib/public/vis_controller.tsx b/src/plugins/vis_types/vislib/public/vis_controller.tsx similarity index 94% rename from src/plugins/vis_type_vislib/public/vis_controller.tsx rename to src/plugins/vis_types/vislib/public/vis_controller.tsx index 73d110e4d8d75..7bae32d031b46 100644 --- a/src/plugins/vis_type_vislib/public/vis_controller.tsx +++ b/src/plugins/vis_types/vislib/public/vis_controller.tsx @@ -9,10 +9,10 @@ import $ from 'jquery'; import React, { RefObject } from 'react'; -import { mountReactNode } from '../../../core/public/utils'; -import { ChartsPluginSetup } from '../../charts/public'; -import type { PersistedState } from '../../visualizations/public'; -import { IInterpreterRenderHandlers } from '../../expressions/public'; +import { mountReactNode } from '../../../../core/public/utils'; +import { ChartsPluginSetup } from '../../../charts/public'; +import type { PersistedState } from '../../../visualizations/public'; +import { IInterpreterRenderHandlers } from '../../../expressions/public'; import { VisTypeVislibCoreSetup } from './plugin'; import { VisLegend, CUSTOM_LEGEND_VIS_TYPES } from './vislib/components/legend'; diff --git a/src/plugins/vis_type_vislib/public/vis_renderer.tsx b/src/plugins/vis_types/vislib/public/vis_renderer.tsx similarity index 89% rename from src/plugins/vis_type_vislib/public/vis_renderer.tsx rename to src/plugins/vis_types/vislib/public/vis_renderer.tsx index 2e954b9a5b710..04c4c3cedc9d2 100644 --- a/src/plugins/vis_type_vislib/public/vis_renderer.tsx +++ b/src/plugins/vis_types/vislib/public/vis_renderer.tsx @@ -9,9 +9,9 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { ExpressionRenderDefinition } from '../../expressions/public'; -import { VisualizationContainer } from '../../visualizations/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import { ExpressionRenderDefinition } from '../../../expressions/public'; +import { VisualizationContainer } from '../../../visualizations/public'; +import { ChartsPluginSetup } from '../../../charts/public'; import { VisTypeVislibCoreSetup } from './plugin'; import { VislibRenderValue, vislibVisName } from './vis_type_vislib_vis_fn'; diff --git a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts b/src/plugins/vis_types/vislib/public/vis_type_vislib_vis_fn.ts similarity index 98% rename from src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts rename to src/plugins/vis_types/vislib/public/vis_type_vislib_vis_fn.ts index 2a987b3691f5e..0658ed1b7c4b1 100644 --- a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts +++ b/src/plugins/vis_types/vislib/public/vis_type_vislib_vis_fn.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../../expressions/public'; // @ts-ignore import { vislibSeriesResponseHandler } from './vislib/response_handler'; diff --git a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts b/src/plugins/vis_types/vislib/public/vis_type_vislib_vis_types.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts rename to src/plugins/vis_types/vislib/public/vis_type_vislib_vis_types.ts diff --git a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx b/src/plugins/vis_types/vislib/public/vis_wrapper.tsx similarity index 92% rename from src/plugins/vis_type_vislib/public/vis_wrapper.tsx rename to src/plugins/vis_types/vislib/public/vis_wrapper.tsx index c9b978b3a2cee..e3948807005e6 100644 --- a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx +++ b/src/plugins/vis_types/vislib/public/vis_wrapper.tsx @@ -10,9 +10,9 @@ import React, { useEffect, useMemo, useRef } from 'react'; import { EuiResizeObserver } from '@elastic/eui'; import { debounce } from 'lodash'; -import { IInterpreterRenderHandlers } from '../../expressions/public'; -import type { PersistedState } from '../../visualizations/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import { IInterpreterRenderHandlers } from '../../../expressions/public'; +import type { PersistedState } from '../../../visualizations/public'; +import { ChartsPluginSetup } from '../../../charts/public'; import { VislibRenderValue } from './vis_type_vislib_vis_fn'; import { createVislibVisController, VislibVisController } from './vis_controller'; diff --git a/src/plugins/vis_type_vislib/public/vislib/VISLIB.md b/src/plugins/vis_types/vislib/public/vislib/VISLIB.md similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/VISLIB.md rename to src/plugins/vis_types/vislib/public/vislib/VISLIB.md diff --git a/src/plugins/vis_type_vislib/public/vislib/_index.scss b/src/plugins/vis_types/vislib/public/vislib/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/_variables.scss b/src/plugins/vis_types/vislib/public/vislib/_variables.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/_variables.scss rename to src/plugins/vis_types/vislib/public/vislib/_variables.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss b/src/plugins/vis_types/vislib/public/vislib/_vislib_vis_type.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss rename to src/plugins/vis_types/vislib/public/vislib/_vislib_vis_type.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/data_array.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/data_array.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/data_array.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/data_array.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/flatten_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/flatten_series.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/index.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/index.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/index.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/index.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/labels.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/labels.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/labels.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/labels.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/labels.test.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/labels.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/labels.test.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/labels.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/truncate_labels.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/truncate_labels.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js b/src/plugins/vis_types/vislib/public/vislib/components/labels/uniq_labels.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js rename to src/plugins/vis_types/vislib/public/vislib/components/labels/uniq_labels.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap rename to src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/_index.scss b/src/plugins/vis_types/vislib/public/vislib/components/legend/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/components/legend/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss b/src/plugins/vis_types/vislib/public/vislib/components/legend/_legend.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss rename to src/plugins/vis_types/vislib/public/vislib/components/legend/_legend.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/index.ts b/src/plugins/vis_types/vislib/public/vislib/components/legend/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/index.ts rename to src/plugins/vis_types/vislib/public/vislib/components/legend/index.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx rename to src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx rename to src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx index 9ce5a5339c04f..56f9025a6bd0b 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx @@ -13,8 +13,8 @@ import { compact, uniqBy, map, every, isUndefined } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiPopoverProps, EuiIcon, keys, htmlIdGenerator } from '@elastic/eui'; -import { PersistedState } from '../../../../../visualizations/public'; -import { IInterpreterRenderHandlers } from '../../../../../expressions/public'; +import { PersistedState } from '../../../../../../visualizations/public'; +import { IInterpreterRenderHandlers } from '../../../../../../expressions/public'; import { getDataActions } from '../../../services'; import { CUSTOM_LEGEND_VIS_TYPES, LegendItem } from './models'; diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx rename to src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx index f4ca3eb5c40ae..5752a0ac09e8f 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend_item.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { LegendItem } from './models'; -import { ColorPicker } from '../../../../../charts/public'; +import { ColorPicker } from '../../../../../../charts/public'; interface Props { item: LegendItem; diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/models.ts b/src/plugins/vis_types/vislib/public/vislib/components/legend/models.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/models.ts rename to src/plugins/vis_types/vislib/public/vislib/components/legend/models.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts b/src/plugins/vis_types/vislib/public/vislib/components/legend/pie_utils.ts similarity index 97% rename from src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts rename to src/plugins/vis_types/vislib/public/vislib/components/legend/pie_utils.ts index 61051eadf6b93..0912adaf9f548 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/pie_utils.ts @@ -14,7 +14,7 @@ import _ from 'lodash'; * * > Duplicated utilty method from vislib Data class to decouple `vislib_vis_legend` from `vislib` * - * @see src/plugins/vis_type_vislib/public/vislib/lib/data.js + * @see src/plugins/vis_types/vislib/public/vislib/lib/data.js * * @returns {Array} Array of unique names (strings) */ diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_collect_branch.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_collect_branch.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_collect_branch.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_collect_branch.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js similarity index 97% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js index 04ab8db1cda8f..ecd741bc4d5d0 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js +++ b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js @@ -9,7 +9,7 @@ import { last } from 'lodash'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; -import { FORMATS_UI_SETTINGS } from '../../../../../../plugins/field_formats/common'; +import { FORMATS_UI_SETTINGS } from '../../../../../../../plugins/field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; function getMax(handler, config, isGauge) { diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/_tooltip.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/_tooltip.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/index.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/index.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/index.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/index.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/position_tooltip.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/position_tooltip.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.test.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/position_tooltip.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.test.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/position_tooltip.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js b/src/plugins/vis_types/vislib/public/vislib/components/tooltip/tooltip.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js rename to src/plugins/vis_types/vislib/public/vislib/components/tooltip/tooltip.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/flatten_data.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/flatten_data.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/inject_zeros.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/inject_zeros.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/ordered_x_keys.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/ordered_x_keys.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/uniq_keys.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/uniq_keys.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_fill_data_array.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_fill_data_array.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_filled_array.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_filled_array.js diff --git a/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js b/src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_injection.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js rename to src/plugins/vis_types/vislib/public/vislib/components/zero_injection/zero_injection.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/errors.ts b/src/plugins/vis_types/vislib/public/vislib/errors.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/vislib/errors.ts rename to src/plugins/vis_types/vislib/public/vislib/errors.ts index cde0f4b43d1cb..67a9d79363e73 100644 --- a/src/plugins/vis_type_vislib/public/vislib/errors.ts +++ b/src/plugins/vis_types/vislib/public/vislib/errors.ts @@ -9,7 +9,7 @@ /* eslint-disable max-classes-per-file */ import { i18n } from '@kbn/i18n'; -import { KbnError } from '../../../kibana_utils/public'; +import { KbnError } from '../../../../kibana_utils/public'; export class VislibError extends KbnError { constructor(message: string) { diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts similarity index 99% rename from src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts index de91053b6dc4d..43fdb3c198474 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.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 { Dimensions, Dimension } from '../../../../../vis_type_pie/public'; +import type { Dimensions, Dimension } from '../../../../../pie/public'; import { buildHierarchicalData } from './build_hierarchical_data'; import { Table, TableParent } from '../../types'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts similarity index 97% rename from src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts index da10edf9591fb..e5b11fcc0339c 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts @@ -9,7 +9,7 @@ import { toArray } from 'lodash'; import { getFormatService } from '../../../services'; import { Table } from '../../types'; -import type { Dimensions } from '../../../../../vis_type_pie/public'; +import type { Dimensions } from '../../../../../pie/public'; interface Slice { name: string; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/index.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/index.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/index.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts similarity index 97% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts index a5897519901a1..9256276b1e9c7 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Dimension } from '../../../../../vis_type_xy/public'; +import type { Dimension } from '../../../../../xy/public'; import { addToSiri, Serie } from './_add_to_siri'; import { Point } from './_get_point'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts similarity index 89% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts index 187b569f28867..c334a83f3dd6a 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { getAggId } from '../../../../../vis_type_xy/public'; -import type { Dimension } from '../../../../../vis_type_xy/public'; +import { getAggId } from '../../../../../xy/public'; +import type { Dimension } from '../../../../../xy/public'; import { Point } from './_get_point'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.test.ts similarity index 96% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.test.ts index 710857e08ccde..e4ebb1fa47929 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Dimension, Dimensions } from '../../../../../vis_type_xy/public'; +import type { Dimension, Dimensions } from '../../../../../xy/public'; import { getAspects } from './_get_aspects'; import { Aspect } from './point_series'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.ts index 1f27d2af1942d..1fecf09f77380 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_aspects.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Dimensions } from '../../../../../vis_type_xy/public'; +import type { Dimensions } from '../../../../../xy/public'; import { makeFakeXAspect } from './_fake_x_aspect'; import { Aspects } from './point_series'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_point.test.ts similarity index 97% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_point.test.ts index 815d0e10aafb2..bf7ff40904130 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_point.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IFieldFormatsRegistry } from '../../../../../field_formats/common'; +import { IFieldFormatsRegistry } from '../../../../../../field_formats/common'; import { getPoint } from './_get_point'; import { setFormatService } from '../../../services'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_point.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_point.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_series.test.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_series.test.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_series.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_get_series.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts similarity index 99% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts index cb0ebe563f54b..251888aa19498 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts @@ -8,7 +8,7 @@ import moment from 'moment'; -import type { DateHistogramParams, HistogramParams } from '../../../../../visualizations/public'; +import type { DateHistogramParams, HistogramParams } from '../../../../../../visualizations/public'; import { initXAxis } from './_init_x_axis'; import { makeFakeXAspect } from './_fake_x_aspect'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_x_axis.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_x_axis.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_y_axis.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_init_y_axis.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts index 4dfa5035275fb..4d39147587169 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts @@ -9,7 +9,7 @@ import moment from 'moment'; import _ from 'lodash'; -import type { DateHistogramParams } from '../../../../../visualizations/public'; +import type { DateHistogramParams } from '../../../../../../visualizations/public'; import { orderedDateAxis } from './_ordered_date_axis'; import { OrderedChart } from './point_series'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/index.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.test.ts similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.test.ts index 2ff4040e3a8aa..d6e6531866d4b 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.test.ts @@ -8,7 +8,7 @@ import _ from 'lodash'; -import type { Dimensions } from '../../../../../vis_type_xy/public'; +import type { Dimensions } from '../../../../../xy/public'; import { buildPointSeriesData } from './point_series'; import { Table, Column } from '../../types'; diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.ts similarity index 95% rename from src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.ts index 62be39ffb8a73..ec9f0169a48fc 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/point_series.ts @@ -8,8 +8,8 @@ import { Duration } from 'moment'; -import type { Dimension, Dimensions } from '../../../../../vis_type_xy/public'; -import type { DateHistogramParams, HistogramParams } from '../../../../../visualizations/public'; +import type { Dimension, Dimensions } from '../../../../../xy/public'; +import type { DateHistogramParams, HistogramParams } from '../../../../../../visualizations/public'; import { getSeries } from './_get_series'; import { getAspects } from './_get_aspects'; diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap b/src/plugins/vis_types/vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap rename to src/plugins/vis_types/vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_alerts.scss b/src/plugins/vis_types/vislib/public/vislib/lib/_alerts.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/_alerts.scss rename to src/plugins/vis_types/vislib/public/vislib/lib/_alerts.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_data_label.js b/src/plugins/vis_types/vislib/public/vislib/lib/_data_label.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/_data_label.js rename to src/plugins/vis_types/vislib/public/vislib/lib/_data_label.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.js b/src/plugins/vis_types/vislib/public/vislib/lib/_error_handler.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.js rename to src/plugins/vis_types/vislib/public/vislib/lib/_error_handler.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/_error_handler.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/_error_handler.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss b/src/plugins/vis_types/vislib/public/vislib/lib/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/lib/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/alerts.js b/src/plugins/vis_types/vislib/public/vislib/lib/alerts.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/alerts.js rename to src/plugins/vis_types/vislib/public/vislib/lib/alerts.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_config.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_config.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_labels.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_labels.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_scale.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_scale.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_title.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_title.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_title.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_title.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/index.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/index.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/index.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/index.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/scale_modes.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/scale_modes.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/time_ticks.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/time_ticks.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/time_ticks.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/time_ticks.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/x_axis.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/x_axis.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/x_axis.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/x_axis.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/y_axis.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/axis/y_axis.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/axis/y_axis.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/axis/y_axis.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/binder.ts b/src/plugins/vis_types/vislib/public/vislib/lib/binder.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/binder.ts rename to src/plugins/vis_types/vislib/public/vislib/lib/binder.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/chart_grid.js b/src/plugins/vis_types/vislib/public/vislib/lib/chart_grid.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/chart_grid.js rename to src/plugins/vis_types/vislib/public/vislib/lib/chart_grid.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/chart_title.js b/src/plugins/vis_types/vislib/public/vislib/lib/chart_title.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/chart_title.js rename to src/plugins/vis_types/vislib/public/vislib/lib/chart_title.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/chart_title.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/chart_title.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/chart_title.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/chart_title.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/data.js b/src/plugins/vis_types/vislib/public/vislib/lib/data.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/data.js rename to src/plugins/vis_types/vislib/public/vislib/lib/data.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/data.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/data.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/data.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/data.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js b/src/plugins/vis_types/vislib/public/vislib/lib/dispatch.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js rename to src/plugins/vis_types/vislib/public/vislib/lib/dispatch.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/dispatch.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/dispatch.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/dispatch.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_heatmap.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/dispatch_heatmap.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/dispatch_heatmap.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/dispatch_heatmap.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/handler.js b/src/plugins/vis_types/vislib/public/vislib/lib/handler.js similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/lib/handler.js rename to src/plugins/vis_types/vislib/public/vislib/lib/handler.js index 1be6271382b10..a2b747f4d5d9c 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/handler.js +++ b/src/plugins/vis_types/vislib/public/vislib/lib/handler.js @@ -11,7 +11,7 @@ import _ from 'lodash'; import MarkdownIt from 'markdown-it'; import moment from 'moment'; -import { dispatchRenderComplete } from '../../../../kibana_utils/public'; +import { dispatchRenderComplete } from '../../../../../kibana_utils/public'; import { visTypes as chartTypes } from '../visualizations/vis_types'; import { NoResults } from '../errors'; diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/handler.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/handler.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/handler.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/handler.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss b/src/plugins/vis_types/vislib/public/vislib/lib/layout/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss b/src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/index.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/index.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/index.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/index.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/layout.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/layout.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/layout.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/layout.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/layout_types.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/layout_types.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/layout_types.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/layout_types.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/types/column_layout.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/types/column_layout.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/types/column_layout.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/types/column_layout.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/types/gauge_layout.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/types/gauge_layout.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js b/src/plugins/vis_types/vislib/public/vislib/lib/layout/types/pie_layout.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js rename to src/plugins/vis_types/vislib/public/vislib/lib/layout/types/pie_layout.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/gauge.js b/src/plugins/vis_types/vislib/public/vislib/lib/types/gauge.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/gauge.js rename to src/plugins/vis_types/vislib/public/vislib/lib/types/gauge.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/index.js b/src/plugins/vis_types/vislib/public/vislib/lib/types/index.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/index.js rename to src/plugins/vis_types/vislib/public/vislib/lib/types/index.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/pie.js b/src/plugins/vis_types/vislib/public/vislib/lib/types/pie.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/pie.js rename to src/plugins/vis_types/vislib/public/vislib/lib/types/pie.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.js b/src/plugins/vis_types/vislib/public/vislib/lib/types/point_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.js rename to src/plugins/vis_types/vislib/public/vislib/lib/types/point_series.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/types/point_series.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/types/point_series.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile.json b/src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile.json similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile.json rename to src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile.json diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value.json b/src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value.json similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value.json rename to src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value.json diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value_result.json b/src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value_result.json similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value_result.json rename to src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_float_value_result.json diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json b/src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json rename to src/plugins/vis_types/vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/vis_config.js b/src/plugins/vis_types/vislib/public/vislib/lib/vis_config.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/vis_config.js rename to src/plugins/vis_types/vislib/public/vislib/lib/vis_config.js diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/vis_config.test.js b/src/plugins/vis_types/vislib/public/vislib/lib/vis_config.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/lib/vis_config.test.js rename to src/plugins/vis_types/vislib/public/vislib/lib/vis_config.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/partials/touchdown_template.tsx b/src/plugins/vis_types/vislib/public/vislib/partials/touchdown_template.tsx similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/partials/touchdown_template.tsx rename to src/plugins/vis_types/vislib/public/vislib/partials/touchdown_template.tsx diff --git a/src/plugins/vis_type_vislib/public/vislib/percentage_mode_transform.ts b/src/plugins/vis_types/vislib/public/vislib/percentage_mode_transform.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/percentage_mode_transform.ts rename to src/plugins/vis_types/vislib/public/vislib/percentage_mode_transform.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/response_handler.js b/src/plugins/vis_types/vislib/public/vislib/response_handler.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/response_handler.js rename to src/plugins/vis_types/vislib/public/vislib/response_handler.js diff --git a/src/plugins/vis_type_vislib/public/vislib/response_handler.test.ts b/src/plugins/vis_types/vislib/public/vislib/response_handler.test.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/response_handler.test.ts rename to src/plugins/vis_types/vislib/public/vislib/response_handler.test.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/types.ts b/src/plugins/vis_types/vislib/public/vislib/types.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/types.ts rename to src/plugins/vis_types/vislib/public/vislib/types.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/vis.js b/src/plugins/vis_types/vislib/public/vislib/vis.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/vis.js rename to src/plugins/vis_types/vislib/public/vislib/vis.js diff --git a/src/plugins/vis_type_vislib/public/vislib/vis.test.js b/src/plugins/vis_types/vislib/public/vislib/vis.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/vis.test.js rename to src/plugins/vis_types/vislib/public/vislib/vis.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/_vis_fixture.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/_vis_fixture.js similarity index 91% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/_vis_fixture.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/_vis_fixture.js index aa05eb57f354a..f4e2e4b977b8f 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/_vis_fixture.js +++ b/src/plugins/vis_types/vislib/public/vislib/visualizations/_vis_fixture.js @@ -8,8 +8,8 @@ import _ from 'lodash'; import $ from 'jquery'; -import { coreMock } from '../../../../../core/public/mocks'; -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { coreMock } from '../../../../../../core/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { Vis } from '../vis'; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauge_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauge_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauge_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauge_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/_meter.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/_meter.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/gauge_types.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/gauge_types.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/meter.js similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/meter.js index 65f7df6459bfe..ad278847b0780 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js +++ b/src/plugins/vis_types/vislib/public/vislib/visualizations/gauges/meter.js @@ -9,8 +9,8 @@ import d3 from 'd3'; import _ from 'lodash'; -import { getHeatmapColors } from '../../../../../charts/public'; -import { FORMATS_UI_SETTINGS } from '../../../../../field_formats/common'; +import { getHeatmapColors } from '../../../../../../charts/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; const arcAngles = { diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart_mock_data.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart_mock_data.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart_mock_data.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/pie_chart_mock_data.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_index.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_index.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_labels.scss similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_labels.scss diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_point_series.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_point_series.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_point_series.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/_point_series.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/area_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/area_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/area_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/area_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/column_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/column_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/column_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/column_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/heatmap_chart.js similarity index 98% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/heatmap_chart.js index a25d408769273..bef6c939f864a 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js +++ b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/heatmap_chart.js @@ -12,8 +12,8 @@ import moment from 'moment'; import { isColorDark } from '@elastic/eui'; import { PointSeries } from './_point_series'; -import { getHeatmapColors } from '../../../../../../plugins/charts/public'; -import { FORMATS_UI_SETTINGS } from '../../../../../../plugins/field_formats/common'; +import { getHeatmapColors } from '../../../../../../../plugins/charts/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../../plugins/field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; const defaults = { diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/line_chart.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/line_chart.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/line_chart.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/line_chart.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/series_types.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/point_series/series_types.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts b/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.d.ts similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts rename to src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.d.ts diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.test.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/vis_types.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/vis_types.js diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.test.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/vis_types.test.js similarity index 100% rename from src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.test.js rename to src/plugins/vis_types/vislib/public/vislib/visualizations/vis_types.test.js diff --git a/src/plugins/vis_type_vislib/server/index.ts b/src/plugins/vis_types/vislib/server/index.ts similarity index 100% rename from src/plugins/vis_type_vislib/server/index.ts rename to src/plugins/vis_types/vislib/server/index.ts diff --git a/src/plugins/vis_type_vislib/server/plugin.ts b/src/plugins/vis_types/vislib/server/plugin.ts similarity index 100% rename from src/plugins/vis_type_vislib/server/plugin.ts rename to src/plugins/vis_types/vislib/server/plugin.ts diff --git a/src/plugins/vis_type_vislib/server/ui_settings.ts b/src/plugins/vis_types/vislib/server/ui_settings.ts similarity index 100% rename from src/plugins/vis_type_vislib/server/ui_settings.ts rename to src/plugins/vis_types/vislib/server/ui_settings.ts diff --git a/src/plugins/vis_types/vislib/tsconfig.json b/src/plugins/vis_types/vislib/tsconfig.json new file mode 100644 index 0000000000000..8246b3f30646b --- /dev/null +++ b/src/plugins/vis_types/vislib/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*" + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../kibana_legacy/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../vis_default_editor/tsconfig.json" }, + { "path": "../../vis_types/xy/tsconfig.json" }, + { "path": "../../vis_types/pie/tsconfig.json" }, + ] +} diff --git a/src/plugins/vis_type_xy/common/index.ts b/src/plugins/vis_types/xy/common/index.ts similarity index 100% rename from src/plugins/vis_type_xy/common/index.ts rename to src/plugins/vis_types/xy/common/index.ts diff --git a/src/plugins/vis_type_vislib/jest.config.js b/src/plugins/vis_types/xy/jest.config.js similarity index 84% rename from src/plugins/vis_type_vislib/jest.config.js rename to src/plugins/vis_types/xy/jest.config.js index 5e144dabd25e2..57b041b575e3f 100644 --- a/src/plugins/vis_type_vislib/jest.config.js +++ b/src/plugins/vis_types/xy/jest.config.js @@ -8,6 +8,6 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/vis_type_vislib'], + rootDir: '../../../..', + roots: ['/src/plugins/vis_types/xy'], }; diff --git a/src/plugins/vis_type_xy/kibana.json b/src/plugins/vis_types/xy/kibana.json similarity index 100% rename from src/plugins/vis_type_xy/kibana.json rename to src/plugins/vis_types/xy/kibana.json diff --git a/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap similarity index 100% rename from src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap rename to src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap diff --git a/src/plugins/vis_type_xy/public/_chart.scss b/src/plugins/vis_types/xy/public/_chart.scss similarity index 100% rename from src/plugins/vis_type_xy/public/_chart.scss rename to src/plugins/vis_types/xy/public/_chart.scss diff --git a/src/plugins/vis_type_xy/public/chart_splitter.tsx b/src/plugins/vis_types/xy/public/chart_splitter.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/chart_splitter.tsx rename to src/plugins/vis_types/xy/public/chart_splitter.tsx diff --git a/src/plugins/vis_type_xy/public/components/_detailed_tooltip.scss b/src/plugins/vis_types/xy/public/components/_detailed_tooltip.scss similarity index 100% rename from src/plugins/vis_type_xy/public/components/_detailed_tooltip.scss rename to src/plugins/vis_types/xy/public/components/_detailed_tooltip.scss diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.mock.ts b/src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts similarity index 100% rename from src/plugins/vis_type_xy/public/components/detailed_tooltip.mock.ts rename to src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.test.tsx b/src/plugins/vis_types/xy/public/components/detailed_tooltip.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/components/detailed_tooltip.test.tsx rename to src/plugins/vis_types/xy/public/components/detailed_tooltip.test.tsx diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx rename to src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx diff --git a/src/plugins/vis_type_xy/public/components/index.ts b/src/plugins/vis_types/xy/public/components/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/components/index.ts rename to src/plugins/vis_types/xy/public/components/index.ts diff --git a/src/plugins/vis_type_xy/public/components/xy_axis.tsx b/src/plugins/vis_types/xy/public/components/xy_axis.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/components/xy_axis.tsx rename to src/plugins/vis_types/xy/public/components/xy_axis.tsx diff --git a/src/plugins/vis_type_xy/public/components/xy_current_time.tsx b/src/plugins/vis_types/xy/public/components/xy_current_time.tsx similarity index 93% rename from src/plugins/vis_type_xy/public/components/xy_current_time.tsx rename to src/plugins/vis_types/xy/public/components/xy_current_time.tsx index 1294302d0becd..1ecf661355868 100644 --- a/src/plugins/vis_type_xy/public/components/xy_current_time.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_current_time.tsx @@ -8,7 +8,7 @@ import React, { FC } from 'react'; import { DomainRange } from '@elastic/charts'; -import { CurrentTime } from '../../../charts/public'; +import { CurrentTime } from '../../../../charts/public'; interface XYCurrentTime { enabled: boolean; diff --git a/src/plugins/vis_type_xy/public/components/xy_endzones.tsx b/src/plugins/vis_types/xy/public/components/xy_endzones.tsx similarity index 96% rename from src/plugins/vis_type_xy/public/components/xy_endzones.tsx rename to src/plugins/vis_types/xy/public/components/xy_endzones.tsx index 1510ec1bcb89a..fbf398b886ffa 100644 --- a/src/plugins/vis_type_xy/public/components/xy_endzones.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_endzones.tsx @@ -10,7 +10,7 @@ import React, { FC } from 'react'; import { DomainRange } from '@elastic/charts'; -import { Endzones } from '../../../charts/public'; +import { Endzones } from '../../../../charts/public'; interface XYEndzones { enabled: boolean; diff --git a/src/plugins/vis_type_xy/public/components/xy_settings.tsx b/src/plugins/vis_types/xy/public/components/xy_settings.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/components/xy_settings.tsx rename to src/plugins/vis_types/xy/public/components/xy_settings.tsx index 2dd7d7e0a91f9..92b47edccfd92 100644 --- a/src/plugins/vis_type_xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_settings.tsx @@ -26,7 +26,7 @@ import { HorizontalAlignment, } from '@elastic/charts'; -import { renderEndzoneTooltip } from '../../../charts/public'; +import { renderEndzoneTooltip } from '../../../../charts/public'; import { getThemeService, getUISettings } from '../services'; import { VisConfig } from '../types'; diff --git a/src/plugins/vis_type_xy/public/components/xy_threshold_line.tsx b/src/plugins/vis_types/xy/public/components/xy_threshold_line.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/components/xy_threshold_line.tsx rename to src/plugins/vis_types/xy/public/components/xy_threshold_line.tsx diff --git a/src/plugins/vis_type_xy/public/config/get_agg_id.ts b/src/plugins/vis_types/xy/public/config/get_agg_id.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/get_agg_id.ts rename to src/plugins/vis_types/xy/public/config/get_agg_id.ts diff --git a/src/plugins/vis_type_xy/public/config/get_aspects.ts b/src/plugins/vis_types/xy/public/config/get_aspects.ts similarity index 97% rename from src/plugins/vis_type_xy/public/config/get_aspects.ts rename to src/plugins/vis_types/xy/public/config/get_aspects.ts index 1485131da83bc..666a913e48402 100644 --- a/src/plugins/vis_type_xy/public/config/get_aspects.ts +++ b/src/plugins/vis_types/xy/public/config/get_aspects.ts @@ -10,7 +10,7 @@ import { compact } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { DatatableColumn } from '../../../expressions/public'; +import { DatatableColumn } from '../../../../expressions/public'; import { Aspect, Dimension, Aspects, Dimensions } from '../types'; import { getFormatService } from '../services'; diff --git a/src/plugins/vis_type_xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts similarity index 97% rename from src/plugins/vis_type_xy/public/config/get_axis.ts rename to src/plugins/vis_types/xy/public/config/get_axis.ts index 71d33cc20d057..4750724ca3d42 100644 --- a/src/plugins/vis_type_xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -10,8 +10,8 @@ import { identity, isNil } from 'lodash'; import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; -import { LabelRotation } from '../../../charts/public'; -import { BUCKET_TYPES } from '../../../data/public'; +import { LabelRotation } from '../../../../charts/public'; +import { BUCKET_TYPES } from '../../../../data/public'; import { Aspect, diff --git a/src/plugins/vis_type_xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts similarity index 94% rename from src/plugins/vis_type_xy/public/config/get_config.ts rename to src/plugins/vis_types/xy/public/config/get_config.ts index 0c687aa918056..13c9a6c275f8e 100644 --- a/src/plugins/vis_type_xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -8,9 +8,9 @@ import { ScaleContinuousType } from '@elastic/charts'; -import { Datatable } from '../../../expressions/public'; -import { BUCKET_TYPES } from '../../../data/public'; -import { DateHistogramParams } from '../../../visualizations/public'; +import { Datatable } from '../../../../expressions/public'; +import { BUCKET_TYPES } from '../../../../data/public'; +import { DateHistogramParams } from '../../../../visualizations/public'; import { Aspect, diff --git a/src/plugins/vis_type_xy/public/config/get_legend.ts b/src/plugins/vis_types/xy/public/config/get_legend.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/get_legend.ts rename to src/plugins/vis_types/xy/public/config/get_legend.ts diff --git a/src/plugins/vis_type_xy/public/config/get_rotation.ts b/src/plugins/vis_types/xy/public/config/get_rotation.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/get_rotation.ts rename to src/plugins/vis_types/xy/public/config/get_rotation.ts diff --git a/src/plugins/vis_type_xy/public/config/get_threshold_line.ts b/src/plugins/vis_types/xy/public/config/get_threshold_line.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/get_threshold_line.ts rename to src/plugins/vis_types/xy/public/config/get_threshold_line.ts diff --git a/src/plugins/vis_type_xy/public/config/get_tooltip.ts b/src/plugins/vis_types/xy/public/config/get_tooltip.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/get_tooltip.ts rename to src/plugins/vis_types/xy/public/config/get_tooltip.ts diff --git a/src/plugins/vis_type_xy/public/config/index.ts b/src/plugins/vis_types/xy/public/config/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/config/index.ts rename to src/plugins/vis_types/xy/public/config/index.ts diff --git a/src/plugins/vis_type_xy/public/editor/collections.ts b/src/plugins/vis_types/xy/public/editor/collections.ts similarity index 98% rename from src/plugins/vis_type_xy/public/editor/collections.ts rename to src/plugins/vis_types/xy/public/editor/collections.ts index 7053f7de0d328..71f7a639379e0 100644 --- a/src/plugins/vis_type_xy/public/editor/collections.ts +++ b/src/plugins/vis_types/xy/public/editor/collections.ts @@ -11,7 +11,7 @@ import { Fit } from '@elastic/charts'; import { AxisMode, ChartMode, InterpolationMode, ThresholdLineStyle } from '../types'; import { ChartType } from '../../common'; -import { LabelRotation } from '../../../charts/public'; +import { LabelRotation } from '../../../../charts/public'; import { getScaleTypes } from './scale_types'; import { getPositions } from './positions'; diff --git a/src/plugins/vis_type_xy/public/editor/common_config.tsx b/src/plugins/vis_types/xy/public/editor/common_config.tsx similarity index 94% rename from src/plugins/vis_type_xy/public/editor/common_config.tsx rename to src/plugins/vis_types/xy/public/editor/common_config.tsx index 5cafbdd0a569c..bd9882a15c124 100644 --- a/src/plugins/vis_type_xy/public/editor/common_config.tsx +++ b/src/plugins/vis_types/xy/public/editor/common_config.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import type { VisEditorOptionsProps } from '../../../visualizations/public'; +import type { VisEditorOptionsProps } from '../../../../visualizations/public'; import type { VisParams } from '../types'; import { MetricsAxisOptions, PointSeriesOptions } from './components/options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/common/index.ts b/src/plugins/vis_types/xy/public/editor/components/common/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/common/index.ts rename to src/plugins/vis_types/xy/public/editor/components/common/index.ts diff --git a/src/plugins/vis_type_xy/public/editor/components/common/truncate_labels.test.tsx b/src/plugins/vis_types/xy/public/editor/components/common/truncate_labels.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/common/truncate_labels.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/common/truncate_labels.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/common/truncate_labels.tsx b/src/plugins/vis_types/xy/public/editor/components/common/truncate_labels.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/common/truncate_labels.tsx rename to src/plugins/vis_types/xy/public/editor/components/common/truncate_labels.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/common/validation_wrapper.tsx b/src/plugins/vis_types/xy/public/editor/components/common/validation_wrapper.tsx similarity index 94% rename from src/plugins/vis_type_xy/public/editor/components/common/validation_wrapper.tsx rename to src/plugins/vis_types/xy/public/editor/components/common/validation_wrapper.tsx index 63df3f7eead43..2088878f963ae 100644 --- a/src/plugins/vis_type_xy/public/editor/components/common/validation_wrapper.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/common/validation_wrapper.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { VisEditorOptionsProps } from '../../../../../visualizations/public'; +import { VisEditorOptionsProps } from '../../../../../../visualizations/public'; export interface ValidationVisOptionsProps extends VisEditorOptionsProps { setMultipleValidity(paramName: string, isValid: boolean): void; diff --git a/src/plugins/vis_type_xy/public/editor/components/index.ts b/src/plugins/vis_types/xy/public/editor/components/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/index.ts rename to src/plugins/vis_types/xy/public/editor/components/index.ts diff --git a/src/plugins/vis_type_xy/public/editor/components/options/index.tsx b/src/plugins/vis_types/xy/public/editor/components/options/index.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/index.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/index.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/index.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/point_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/point_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/point_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/point_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx similarity index 96% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx index 5ba35717e46f3..ee5cc950ff66b 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx @@ -13,7 +13,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { Position } from '@elastic/charts'; -import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { SelectOption, SwitchOption } from '../../../../../../../vis_default_editor/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { CategoryAxis } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/chart_options.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/chart_options.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/chart_options.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/chart_options.tsx index 34ee33781f269..04013969fb4fa 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/chart_options.tsx @@ -11,7 +11,7 @@ import React, { useMemo, useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { SelectOption } from '../../../../../../vis_default_editor/public'; +import { SelectOption } from '../../../../../../../vis_default_editor/public'; import { SeriesParam, ValueAxis, ChartMode, AxisMode } from '../../../../types'; import { LineOptions } from './line_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/custom_extents_options.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/custom_extents_options.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx similarity index 99% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx index 2d3e819e96024..2152849983513 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { NumberInputOption, SwitchOption } from '../../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { YExtents } from './y_extents'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx similarity index 99% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx index 5454df3a165cd..9b4e1c61a201f 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/index.tsx @@ -11,7 +11,7 @@ import { cloneDeep, get } from 'lodash'; import { EuiSpacer } from '@elastic/eui'; -import { IAggConfig } from '../../../../../../data/public'; +import { IAggConfig } from '../../../../../../../data/public'; import { VisParams, ValueAxis, SeriesParam, CategoryAxis } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx similarity index 94% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx index bcf4beaa78945..ef48d8b6d7880 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/label_options.tsx @@ -12,8 +12,8 @@ import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; -import { Labels } from '../../../../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../../../../../vis_default_editor/public'; +import { Labels } from '../../../../../../../charts/public'; import { TruncateLabelsOption } from '../../common'; import { getRotateOptions } from '../../../collections'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.test.tsx similarity index 95% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.test.tsx index 5497c46c1dd34..41bbfec7ee939 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { NumberInputOption } from '../../../../../../vis_default_editor/public'; +import { NumberInputOption } from '../../../../../../../vis_default_editor/public'; import { LineOptions, LineOptionsParams } from './line_options'; import { seriesParam } from './mocks'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.tsx index 75dfe8627d73e..355b04f07d00d 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/line_options.tsx @@ -15,7 +15,7 @@ import { NumberInputOption, SelectOption, SwitchOption, -} from '../../../../../../vis_default_editor/public'; +} from '../../../../../../../vis_default_editor/public'; import { SeriesParam } from '../../../../types'; import { SetChart } from './chart_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/mocks.ts b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/mocks.ts similarity index 93% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/mocks.ts rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/mocks.ts index eed224cf2a514..cbc970c7ed7d8 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/mocks.ts +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/mocks.ts @@ -8,8 +8,8 @@ import { Position } from '@elastic/charts'; -import { Vis } from '../../../../../../visualizations/public'; -import { Style } from '../../../../../../charts/public'; +import { Vis } from '../../../../../../../visualizations/public'; +import { Style } from '../../../../../../../charts/public'; import { ValueAxis, diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/point_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/point_options.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/point_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/point_options.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/point_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/point_options.tsx similarity index 95% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/point_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/point_options.tsx index d35a5a2374ca3..41d5f7c2c9794 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/point_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/point_options.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiRange, EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { SwitchOption } from '../../../../../../vis_default_editor/public'; +import { SwitchOption } from '../../../../../../../vis_default_editor/public'; import { SeriesParam } from '../../../../types'; import { SetChart } from './chart_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/series_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/series_panel.tsx similarity index 96% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/series_panel.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/series_panel.tsx index 3adfe3277c969..69fbbcf80e28b 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/series_panel.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/series_panel.tsx @@ -12,7 +12,7 @@ import { EuiPanel, EuiTitle, EuiSpacer, EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Vis } from '../../../../../../visualizations/public'; +import { Vis } from '../../../../../../../visualizations/public'; import { ValueAxis, SeriesParam } from '../../../../types'; import { ChartOptions } from './chart_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/utils.ts b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/utils.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/utils.ts rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/utils.ts diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx index f2d689126166f..ceb655fa47107 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx @@ -11,7 +11,7 @@ import { shallow } from 'enzyme'; import { Position } from '@elastic/charts'; -import { TextInputOption } from '../../../../../../vis_default_editor/public'; +import { TextInputOption } from '../../../../../../../vis_default_editor/public'; import { ValueAxis, ScaleType } from '../../../../types'; import { LabelOptions } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.tsx similarity index 99% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.tsx index d39bbf5bfa532..751c61f3b1531 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/value_axis_options.tsx @@ -14,7 +14,7 @@ import { SelectOption, SwitchOption, TextInputOption, -} from '../../../../../../vis_default_editor/public'; +} from '../../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { LabelOptions, SetAxisLabel } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.test.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.test.tsx index e5ed34d03099d..da7005210865d 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.test.tsx @@ -11,7 +11,7 @@ import { mount, shallow } from 'enzyme'; import { ScaleType } from '../../../../types'; import { YExtents, YExtentsProps } from './y_extents'; -import { NumberInputOption } from '../../../../../../vis_default_editor/public'; +import { NumberInputOption } from '../../../../../../../vis_default_editor/public'; describe('YExtents component', () => { let setMultipleValidity: jest.Mock; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.tsx index e81f0fff96f49..ce546339a9912 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/metrics_axes/y_extents.tsx @@ -10,7 +10,7 @@ import React, { useEffect, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption } from '../../../../../../vis_default_editor/public'; +import { NumberInputOption } from '../../../../../../../vis_default_editor/public'; import { Scale, ScaleType } from '../../../../types'; import { SetScale } from './value_axis_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx index 271c5445a9580..105cd66799041 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -15,8 +15,8 @@ import { SelectOption, SwitchOption, PalettePicker, -} from '../../../../../../vis_default_editor/public'; -import { PaletteRegistry } from '../../../../../../charts/public'; +} from '../../../../../../../vis_default_editor/public'; +import { PaletteRegistry } from '../../../../../../../charts/public'; import { ChartType } from '../../../../../common'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/grid_panel.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/grid_panel.tsx index 69f6a08946fce..0bf5344ac7f26 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/grid_panel.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { SelectOption, SwitchOption } from '../../../../../../../vis_default_editor/public'; import { VisParams, ValueAxis } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/index.ts b/src/plugins/vis_types/xy/public/editor/components/options/point_series/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/index.ts rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/index.ts diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.mocks.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.mocks.ts diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.test.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.test.tsx diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx similarity index 97% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx index 1fd9b043e87f5..da7bdfb0d7986 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx @@ -15,8 +15,8 @@ import { BasicOptions, SwitchOption, LongLegendOptions, -} from '../../../../../../vis_default_editor/public'; -import { BUCKET_TYPES } from '../../../../../../data/public'; +} from '../../../../../../../vis_default_editor/public'; +import { BUCKET_TYPES } from '../../../../../../../data/public'; import { VisParams } from '../../../../types'; import { GridPanel } from './grid_panel'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/threshold_panel.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx rename to src/plugins/vis_types/xy/public/editor/components/options/point_series/threshold_panel.tsx index 00429c6702eeb..347354ac9d4f2 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/threshold_panel.tsx @@ -16,7 +16,7 @@ import { SelectOption, SwitchOption, RequiredNumberInputOption, -} from '../../../../../../vis_default_editor/public'; +} from '../../../../../../../vis_default_editor/public'; import { ValidationVisOptionsProps } from '../../common'; import { VisParams } from '../../../../types'; import { getThresholdLineStyles } from '../../../collections'; diff --git a/src/plugins/vis_type_xy/public/editor/index.ts b/src/plugins/vis_types/xy/public/editor/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/index.ts rename to src/plugins/vis_types/xy/public/editor/index.ts diff --git a/src/plugins/vis_type_xy/public/editor/positions.ts b/src/plugins/vis_types/xy/public/editor/positions.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/positions.ts rename to src/plugins/vis_types/xy/public/editor/positions.ts diff --git a/src/plugins/vis_type_xy/public/editor/scale_types.ts b/src/plugins/vis_types/xy/public/editor/scale_types.ts similarity index 100% rename from src/plugins/vis_type_xy/public/editor/scale_types.ts rename to src/plugins/vis_types/xy/public/editor/scale_types.ts diff --git a/src/plugins/vis_type_xy/public/expression_functions/category_axis.ts b/src/plugins/vis_types/xy/public/expression_functions/category_axis.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/category_axis.ts rename to src/plugins/vis_types/xy/public/expression_functions/category_axis.ts index 30215d8feb8a3..08958915da4a4 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/category_axis.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/category_axis.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; import type { CategoryAxis } from '../types'; import type { ExpressionValueScale } from './vis_scale'; import type { ExpressionValueLabel } from './label'; diff --git a/src/plugins/vis_type_xy/public/expression_functions/index.ts b/src/plugins/vis_types/xy/public/expression_functions/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/expression_functions/index.ts rename to src/plugins/vis_types/xy/public/expression_functions/index.ts diff --git a/src/plugins/vis_type_xy/public/expression_functions/label.ts b/src/plugins/vis_types/xy/public/expression_functions/label.ts similarity index 96% rename from src/plugins/vis_type_xy/public/expression_functions/label.ts rename to src/plugins/vis_types/xy/public/expression_functions/label.ts index 934278d13cff0..e733aebaa627c 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/label.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/label.ts @@ -7,12 +7,12 @@ */ import { i18n } from '@kbn/i18n'; -import type { Labels } from '../../../charts/public'; +import type { Labels } from '../../../../charts/public'; import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; export type ExpressionValueLabel = ExpressionValueBoxed< 'label', diff --git a/src/plugins/vis_type_xy/public/expression_functions/series_param.ts b/src/plugins/vis_types/xy/public/expression_functions/series_param.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/series_param.ts rename to src/plugins/vis_types/xy/public/expression_functions/series_param.ts index 3fd62e33e257f..174ed4c057974 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/series_param.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/series_param.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; import type { SeriesParam } from '../types'; export interface Arguments extends Omit { diff --git a/src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts b/src/plugins/vis_types/xy/public/expression_functions/threshold_line.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts rename to src/plugins/vis_types/xy/public/expression_functions/threshold_line.ts index 8c01e37503985..a4f5cf98fb968 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/threshold_line.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; import type { ThresholdLine } from '../types'; export type ExpressionValueThresholdLine = ExpressionValueBoxed< diff --git a/src/plugins/vis_type_xy/public/expression_functions/time_marker.ts b/src/plugins/vis_types/xy/public/expression_functions/time_marker.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/time_marker.ts rename to src/plugins/vis_types/xy/public/expression_functions/time_marker.ts index 3d9f609292c00..3b673394463f0 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/time_marker.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/time_marker.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; import type { TimeMarker } from '../types'; export type ExpressionValueTimeMarker = ExpressionValueBoxed< diff --git a/src/plugins/vis_type_xy/public/expression_functions/value_axis.ts b/src/plugins/vis_types/xy/public/expression_functions/value_axis.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/value_axis.ts rename to src/plugins/vis_types/xy/public/expression_functions/value_axis.ts index 510ec9bc605d2..a92d35dc9fefd 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/value_axis.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/value_axis.ts @@ -13,7 +13,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; interface Arguments { name: string; diff --git a/src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts b/src/plugins/vis_types/xy/public/expression_functions/vis_scale.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts rename to src/plugins/vis_types/xy/public/expression_functions/vis_scale.ts index fadf3d80a6e81..ddf14ead20a25 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/vis_scale.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition, Datatable, ExpressionValueBoxed, -} from '../../../expressions/public'; +} from '../../../../expressions/public'; import type { Scale } from '../types'; export type ExpressionValueScale = ExpressionValueBoxed< diff --git a/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts similarity index 98% rename from src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts rename to src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts index 6d2b860066b07..ccad0c520f8ea 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts @@ -8,8 +8,12 @@ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition, Datatable, Render } from '../../../expressions/common'; -import { prepareLogTable, Dimension } from '../../../visualizations/public'; +import type { + ExpressionFunctionDefinition, + Datatable, + Render, +} from '../../../../expressions/common'; +import { prepareLogTable, Dimension } from '../../../../visualizations/public'; import type { ChartType } from '../../common'; import type { VisParams, XYVisConfig } from '../types'; diff --git a/src/plugins/vis_type_xy/public/index.ts b/src/plugins/vis_types/xy/public/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/index.ts rename to src/plugins/vis_types/xy/public/index.ts diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts similarity index 89% rename from src/plugins/vis_type_xy/public/plugin.ts rename to src/plugins/vis_types/xy/public/plugin.ts index 488e6fd84b4da..57736444f49fe 100644 --- a/src/plugins/vis_type_xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; -import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; -import { ChartsPluginSetup, ChartsPluginStart } from '../../charts/public'; -import { DataPublicPluginStart } from '../../data/public'; -import { UsageCollectionSetup } from '../../usage_collection/public'; +import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { VisualizationsSetup, VisualizationsStart } from '../../../visualizations/public'; +import { ChartsPluginSetup, ChartsPluginStart } from '../../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { UsageCollectionSetup } from '../../../usage_collection/public'; import { setDataActions, setFormatService, diff --git a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts similarity index 100% rename from src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts rename to src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts diff --git a/src/plugins/vis_type_xy/public/services.ts b/src/plugins/vis_types/xy/public/services.ts similarity index 83% rename from src/plugins/vis_type_xy/public/services.ts rename to src/plugins/vis_types/xy/public/services.ts index 63bc55b288ae3..7f1f7e8728151 100644 --- a/src/plugins/vis_type_xy/public/services.ts +++ b/src/plugins/vis_types/xy/public/services.ts @@ -7,10 +7,10 @@ */ import { UiCounterMetricType } from '@kbn/analytics'; -import { CoreSetup, DocLinksStart } from '../../../core/public'; -import { createGetterSetter } from '../../kibana_utils/public'; -import { DataPublicPluginStart } from '../../data/public'; -import { ChartsPluginSetup, ChartsPluginStart } from '../../charts/public'; +import { CoreSetup, DocLinksStart } from '../../../../core/public'; +import { createGetterSetter } from '../../../kibana_utils/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { ChartsPluginSetup, ChartsPluginStart } from '../../../charts/public'; export const [getUISettings, setUISettings] = createGetterSetter( 'xy core.uiSettings' diff --git a/src/plugins/vis_type_xy/public/to_ast.test.ts b/src/plugins/vis_types/xy/public/to_ast.test.ts similarity index 83% rename from src/plugins/vis_type_xy/public/to_ast.test.ts rename to src/plugins/vis_types/xy/public/to_ast.test.ts index 4437986eff5f7..cbe25bc1fef6f 100644 --- a/src/plugins/vis_type_xy/public/to_ast.test.ts +++ b/src/plugins/vis_types/xy/public/to_ast.test.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression } from '../../../expressions/public'; import { sampleAreaVis } from './sample_vis.test.mocks'; import { toExpressionAst } from './to_ast'; import { VisParams } from './types'; -jest.mock('../../expressions/public', () => ({ - ...(jest.requireActual('../../expressions/public') as any), +jest.mock('../../../expressions/public', () => ({ + ...(jest.requireActual('../../../expressions/public') as any), buildExpression: jest.fn().mockImplementation(() => ({ toAst: () => ({ type: 'expression', diff --git a/src/plugins/vis_type_xy/public/to_ast.ts b/src/plugins/vis_types/xy/public/to_ast.ts similarity index 97% rename from src/plugins/vis_type_xy/public/to_ast.ts rename to src/plugins/vis_types/xy/public/to_ast.ts index 0b1eb5262d71a..5fc130a08ed27 100644 --- a/src/plugins/vis_type_xy/public/to_ast.ts +++ b/src/plugins/vis_types/xy/public/to_ast.ts @@ -13,10 +13,10 @@ import { getVisSchemas, DateHistogramParams, HistogramParams, -} from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import { BUCKET_TYPES } from '../../data/public'; -import { Labels } from '../../charts/public'; +} from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; +import { BUCKET_TYPES } from '../../../data/public'; +import { Labels } from '../../../charts/public'; import { Dimensions, @@ -32,7 +32,7 @@ import { import { visName, VisTypeXyExpressionFunctionDefinition } from './expression_functions/xy_vis_fn'; import { XyVisType } from '../common'; import { getEsaggsFn } from './to_ast_esaggs'; -import { TimeRangeBounds } from '../../data/common'; +import { TimeRangeBounds } from '../../../data/common'; const prepareLabel = (data: Labels) => { const label = buildExpressionFunction('label', { diff --git a/src/plugins/vis_type_xy/public/to_ast_esaggs.ts b/src/plugins/vis_types/xy/public/to_ast_esaggs.ts similarity index 91% rename from src/plugins/vis_type_xy/public/to_ast_esaggs.ts rename to src/plugins/vis_types/xy/public/to_ast_esaggs.ts index 71ef78b19e076..ff9dbd9ca7664 100644 --- a/src/plugins/vis_type_xy/public/to_ast_esaggs.ts +++ b/src/plugins/vis_types/xy/public/to_ast_esaggs.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { Vis } from '../../visualizations/public'; -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { Vis } from '../../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, -} from '../../data/public'; +} from '../../../data/public'; import { VisParams } from './types'; diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts similarity index 100% rename from src/plugins/vis_type_xy/public/types/config.ts rename to src/plugins/vis_types/xy/public/types/config.ts diff --git a/src/plugins/vis_type_xy/public/types/constants.ts b/src/plugins/vis_types/xy/public/types/constants.ts similarity index 100% rename from src/plugins/vis_type_xy/public/types/constants.ts rename to src/plugins/vis_types/xy/public/types/constants.ts diff --git a/src/plugins/vis_type_xy/public/types/index.ts b/src/plugins/vis_types/xy/public/types/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/types/index.ts rename to src/plugins/vis_types/xy/public/types/index.ts diff --git a/src/plugins/vis_type_xy/public/types/param.ts b/src/plugins/vis_types/xy/public/types/param.ts similarity index 97% rename from src/plugins/vis_type_xy/public/types/param.ts rename to src/plugins/vis_types/xy/public/types/param.ts index 0687bd2af2cd1..81eeca55108ca 100644 --- a/src/plugins/vis_type_xy/public/types/param.ts +++ b/src/plugins/vis_types/xy/public/types/param.ts @@ -7,14 +7,14 @@ */ import type { Fit, Position } from '@elastic/charts'; -import type { Style, Labels, PaletteOutput } from '../../../charts/public'; +import type { Style, Labels, PaletteOutput } from '../../../../charts/public'; import type { SchemaConfig, ExpressionValueXYDimension, FakeParams, HistogramParams, DateHistogramParams, -} from '../../../visualizations/public'; +} from '../../../../visualizations/public'; import type { ChartType, XyVisType } from '../../common'; import type { ExpressionValueCategoryAxis, diff --git a/src/plugins/vis_type_xy/public/types/vis_type.ts b/src/plugins/vis_types/xy/public/types/vis_type.ts similarity index 88% rename from src/plugins/vis_type_xy/public/types/vis_type.ts rename to src/plugins/vis_types/xy/public/types/vis_type.ts index 849a646cca814..311714ed3587d 100644 --- a/src/plugins/vis_type_xy/public/types/vis_type.ts +++ b/src/plugins/vis_types/xy/public/types/vis_type.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { VisTypeDefinition } from '../../../visualizations/public'; +import { VisTypeDefinition } from '../../../../visualizations/public'; import { ChartType } from '../../common'; import { VisParams } from './param'; diff --git a/src/plugins/vis_type_xy/public/utils/accessors.test.ts b/src/plugins/vis_types/xy/public/utils/accessors.test.ts similarity index 98% rename from src/plugins/vis_type_xy/public/utils/accessors.test.ts rename to src/plugins/vis_types/xy/public/utils/accessors.test.ts index d074263e5bb25..61d175fa8ff7d 100644 --- a/src/plugins/vis_type_xy/public/utils/accessors.test.ts +++ b/src/plugins/vis_types/xy/public/utils/accessors.test.ts @@ -7,7 +7,7 @@ */ import { COMPLEX_SPLIT_ACCESSOR, getComplexAccessor } from './accessors'; -import { BUCKET_TYPES } from '../../../data/common'; +import { BUCKET_TYPES } from '../../../../data/common'; import { AccessorFn, Datum } from '@elastic/charts'; describe('XY chart datum accessors', () => { diff --git a/src/plugins/vis_type_xy/public/utils/accessors.tsx b/src/plugins/vis_types/xy/public/utils/accessors.tsx similarity index 94% rename from src/plugins/vis_type_xy/public/utils/accessors.tsx rename to src/plugins/vis_types/xy/public/utils/accessors.tsx index 4920885f51a07..0356e921a9d5c 100644 --- a/src/plugins/vis_type_xy/public/utils/accessors.tsx +++ b/src/plugins/vis_types/xy/public/utils/accessors.tsx @@ -7,8 +7,8 @@ */ import { AccessorFn, Accessor } from '@elastic/charts'; -import { BUCKET_TYPES } from '../../../data/public'; -import { FakeParams } from '../../../visualizations/public'; +import { BUCKET_TYPES } from '../../../../data/public'; +import { FakeParams } from '../../../../visualizations/public'; import { Aspect } from '../types'; export const COMPLEX_X_ACCESSOR = '__customXAccessor__'; diff --git a/src/plugins/vis_type_xy/public/utils/domain.ts b/src/plugins/vis_types/xy/public/utils/domain.ts similarity index 90% rename from src/plugins/vis_type_xy/public/utils/domain.ts rename to src/plugins/vis_types/xy/public/utils/domain.ts index 48527ec9333ed..fa8dd74e3942a 100644 --- a/src/plugins/vis_type_xy/public/utils/domain.ts +++ b/src/plugins/vis_types/xy/public/utils/domain.ts @@ -11,9 +11,9 @@ import { unitOfTime } from 'moment'; import { DomainRange } from '@elastic/charts'; -import { getAdjustedInterval } from '../../../charts/public'; -import { Datatable } from '../../../expressions/public'; -import { DateHistogramParams, HistogramParams } from '../../../visualizations/public'; +import { getAdjustedInterval } from '../../../../charts/public'; +import { Datatable } from '../../../../expressions/public'; +import { DateHistogramParams, HistogramParams } from '../../../../visualizations/public'; import { Aspect } from '../types'; diff --git a/src/plugins/vis_type_xy/public/utils/get_all_series.test.ts b/src/plugins/vis_types/xy/public/utils/get_all_series.test.ts similarity index 100% rename from src/plugins/vis_type_xy/public/utils/get_all_series.test.ts rename to src/plugins/vis_types/xy/public/utils/get_all_series.test.ts diff --git a/src/plugins/vis_type_xy/public/utils/get_all_series.ts b/src/plugins/vis_types/xy/public/utils/get_all_series.ts similarity index 95% rename from src/plugins/vis_type_xy/public/utils/get_all_series.ts rename to src/plugins/vis_types/xy/public/utils/get_all_series.ts index c9b956f7cd3b5..cdb8f816d7e4f 100644 --- a/src/plugins/vis_type_xy/public/utils/get_all_series.ts +++ b/src/plugins/vis_types/xy/public/utils/get_all_series.ts @@ -7,7 +7,7 @@ */ import { TickFormatter } from '@elastic/charts'; -import { DatatableRow } from '../../../expressions/public'; +import { DatatableRow } from '../../../../expressions/public'; import { Column, Aspect } from '../types'; interface SplitAccessors { diff --git a/src/plugins/vis_type_xy/public/utils/get_color_picker.test.tsx b/src/plugins/vis_types/xy/public/utils/get_color_picker.test.tsx similarity index 96% rename from src/plugins/vis_type_xy/public/utils/get_color_picker.test.tsx rename to src/plugins/vis_types/xy/public/utils/get_color_picker.test.tsx index c2377b42bb1c2..e015521f7436e 100644 --- a/src/plugins/vis_type_xy/public/utils/get_color_picker.test.tsx +++ b/src/plugins/vis_types/xy/public/utils/get_color_picker.test.tsx @@ -12,8 +12,8 @@ import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { ComponentType, ReactWrapper } from 'enzyme'; import { getColorPicker } from './get_color_picker'; -import { ColorPicker } from '../../../charts/public'; -import type { PersistedState } from '../../../visualizations/public'; +import { ColorPicker } from '../../../../charts/public'; +import type { PersistedState } from '../../../../visualizations/public'; jest.mock('@elastic/charts', () => { const original = jest.requireActual('@elastic/charts'); diff --git a/src/plugins/vis_type_xy/public/utils/get_color_picker.tsx b/src/plugins/vis_types/xy/public/utils/get_color_picker.tsx similarity index 95% rename from src/plugins/vis_type_xy/public/utils/get_color_picker.tsx rename to src/plugins/vis_types/xy/public/utils/get_color_picker.tsx index 4805d89068e86..1b5a16a8894aa 100644 --- a/src/plugins/vis_type_xy/public/utils/get_color_picker.tsx +++ b/src/plugins/vis_types/xy/public/utils/get_color_picker.tsx @@ -10,8 +10,8 @@ import React, { useCallback } from 'react'; import { LegendColorPicker, Position, XYChartSeriesIdentifier, SeriesName } from '@elastic/charts'; import { PopoverAnchorPosition, EuiWrappingPopover, EuiOutsideClickDetector } from '@elastic/eui'; -import type { PersistedState } from '../../../visualizations/public'; -import { ColorPicker } from '../../../charts/public'; +import type { PersistedState } from '../../../../visualizations/public'; +import { ColorPicker } from '../../../../charts/public'; function getAnchorPosition(legendPosition: Position): PopoverAnchorPosition { switch (legendPosition) { diff --git a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx b/src/plugins/vis_types/xy/public/utils/get_legend_actions.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx rename to src/plugins/vis_types/xy/public/utils/get_legend_actions.tsx index c125d8dd075ed..98ace7dd57a39 100644 --- a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx +++ b/src/plugins/vis_types/xy/public/utils/get_legend_actions.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover, EuiContextMenu } from '@elastic/eui'; import { LegendAction, XYChartSeriesIdentifier, SeriesName } from '@elastic/charts'; -import { ClickTriggerEvent } from '../../../charts/public'; +import { ClickTriggerEvent } from '../../../../charts/public'; export const getLegendActions = ( canFilter: (data: ClickTriggerEvent | null) => Promise, diff --git a/src/plugins/vis_type_xy/public/utils/get_series_name_fn.test.ts b/src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts similarity index 100% rename from src/plugins/vis_type_xy/public/utils/get_series_name_fn.test.ts rename to src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts diff --git a/src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts b/src/plugins/vis_types/xy/public/utils/get_series_name_fn.ts similarity index 100% rename from src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts rename to src/plugins/vis_types/xy/public/utils/get_series_name_fn.ts diff --git a/src/plugins/vis_type_xy/public/utils/get_time_zone.tsx b/src/plugins/vis_types/xy/public/utils/get_time_zone.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/utils/get_time_zone.tsx rename to src/plugins/vis_types/xy/public/utils/get_time_zone.tsx diff --git a/src/plugins/vis_type_xy/public/utils/index.tsx b/src/plugins/vis_types/xy/public/utils/index.tsx similarity index 100% rename from src/plugins/vis_type_xy/public/utils/index.tsx rename to src/plugins/vis_types/xy/public/utils/index.tsx diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts b/src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts similarity index 100% rename from src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts rename to src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx b/src/plugins/vis_types/xy/public/utils/render_all_series.test.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx rename to src/plugins/vis_types/xy/public/utils/render_all_series.test.tsx index 23dabef662d55..47b103003b3ed 100644 --- a/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx +++ b/src/plugins/vis_types/xy/public/utils/render_all_series.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { AreaSeries, BarSeries, CurveType } from '@elastic/charts'; -import { DatatableRow } from '../../../expressions/public'; +import { DatatableRow } from '../../../../expressions/public'; import { renderAllSeries } from './render_all_series'; import { getVisConfig, diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/utils/render_all_series.tsx rename to src/plugins/vis_types/xy/public/utils/render_all_series.tsx index d186617fef2ae..f8ca1d059ae4f 100644 --- a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx +++ b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx @@ -21,8 +21,8 @@ import { LabelOverflowConstraint, } from '@elastic/charts'; -import { DatatableRow } from '../../../expressions/public'; -import { METRIC_TYPES } from '../../../data/public'; +import { DatatableRow } from '../../../../expressions/public'; +import { METRIC_TYPES } from '../../../../data/public'; import { ChartType } from '../../common'; import { SeriesParam, VisConfig } from '../types'; diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx similarity index 98% rename from src/plugins/vis_type_xy/public/vis_component.tsx rename to src/plugins/vis_types/xy/public/vis_component.tsx index 346f6cc74a1ac..141174194f1bc 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -29,10 +29,10 @@ import { getBrushFromChartBrushEventFn, ClickTriggerEvent, PaletteRegistry, -} from '../../charts/public'; -import { Datatable, IInterpreterRenderHandlers } from '../../expressions/public'; -import type { PersistedState } from '../../visualizations/public'; -import { useActiveCursor } from '../../charts/public'; + useActiveCursor, +} from '../../../charts/public'; +import { Datatable, IInterpreterRenderHandlers } from '../../../expressions/public'; +import type { PersistedState } from '../../../visualizations/public'; import { VisParams } from './types'; import { getAdjustedDomain, diff --git a/src/plugins/vis_type_xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx similarity index 89% rename from src/plugins/vis_type_xy/public/vis_renderer.tsx rename to src/plugins/vis_types/xy/public/vis_renderer.tsx index df3bee9b3f446..093671307d538 100644 --- a/src/plugins/vis_type_xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -10,9 +10,9 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { VisualizationContainer } from '../../visualizations/public'; -import type { PersistedState } from '../../visualizations/public'; -import type { ExpressionRenderDefinition } from '../../expressions/public'; +import { VisualizationContainer } from '../../../visualizations/public'; +import type { PersistedState } from '../../../visualizations/public'; +import type { ExpressionRenderDefinition } from '../../../expressions/public'; import type { XyVisType } from '../common'; import type { VisComponentType } from './vis_component'; diff --git a/src/plugins/vis_type_xy/public/vis_types/area.ts b/src/plugins/vis_types/xy/public/vis_types/area.ts similarity index 95% rename from src/plugins/vis_type_xy/public/vis_types/area.ts rename to src/plugins/vis_types/xy/public/vis_types/area.ts index 4d886c0dcb884..b377fd54753da 100644 --- a/src/plugins/vis_type_xy/public/vis_types/area.ts +++ b/src/plugins/vis_types/xy/public/vis_types/area.ts @@ -11,9 +11,9 @@ import { i18n } from '@kbn/i18n'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { Fit, Position } from '@elastic/charts'; -import { AggGroupNames } from '../../../data/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; -import { defaultCountLabel, LabelRotation } from '../../../charts/public'; +import { AggGroupNames } from '../../../../data/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; +import { defaultCountLabel, LabelRotation } from '../../../../charts/public'; import { ChartMode, diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.ts b/src/plugins/vis_types/xy/public/vis_types/histogram.ts similarity index 96% rename from src/plugins/vis_type_xy/public/vis_types/histogram.ts rename to src/plugins/vis_types/xy/public/vis_types/histogram.ts index 9a0a3b43329fd..2d22b7566175c 100644 --- a/src/plugins/vis_type_xy/public/vis_types/histogram.ts +++ b/src/plugins/vis_types/xy/public/vis_types/histogram.ts @@ -11,8 +11,8 @@ import { i18n } from '@kbn/i18n'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { Position } from '@elastic/charts'; -import { AggGroupNames } from '../../../data/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; +import { AggGroupNames } from '../../../../data/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; import { ChartMode, @@ -26,7 +26,7 @@ import { import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getOptionTabs } from '../editor/common_config'; -import { defaultCountLabel, LabelRotation } from '../../../charts/public'; +import { defaultCountLabel, LabelRotation } from '../../../../charts/public'; export const getHistogramVisTypeDefinition = ( showElasticChartsOptions = false diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts b/src/plugins/vis_types/xy/public/vis_types/horizontal_bar.ts similarity index 96% rename from src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts rename to src/plugins/vis_types/xy/public/vis_types/horizontal_bar.ts index 5238258fcf080..8916f3f94f6ff 100644 --- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts +++ b/src/plugins/vis_types/xy/public/vis_types/horizontal_bar.ts @@ -11,8 +11,8 @@ import { i18n } from '@kbn/i18n'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { Position } from '@elastic/charts'; -import { AggGroupNames } from '../../../data/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; +import { AggGroupNames } from '../../../../data/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; import { ChartMode, @@ -26,7 +26,7 @@ import { import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getOptionTabs } from '../editor/common_config'; -import { defaultCountLabel, LabelRotation } from '../../../charts/public'; +import { defaultCountLabel, LabelRotation } from '../../../../charts/public'; export const getHorizontalBarVisTypeDefinition = ( showElasticChartsOptions = false diff --git a/src/plugins/vis_type_xy/public/vis_types/index.ts b/src/plugins/vis_types/xy/public/vis_types/index.ts similarity index 100% rename from src/plugins/vis_type_xy/public/vis_types/index.ts rename to src/plugins/vis_types/xy/public/vis_types/index.ts diff --git a/src/plugins/vis_type_xy/public/vis_types/line.ts b/src/plugins/vis_types/xy/public/vis_types/line.ts similarity index 95% rename from src/plugins/vis_type_xy/public/vis_types/line.ts rename to src/plugins/vis_types/xy/public/vis_types/line.ts index 239eae83057d4..af75c38d627df 100644 --- a/src/plugins/vis_type_xy/public/vis_types/line.ts +++ b/src/plugins/vis_types/xy/public/vis_types/line.ts @@ -11,9 +11,9 @@ import { i18n } from '@kbn/i18n'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { Position, Fit } from '@elastic/charts'; -import { AggGroupNames } from '../../../data/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; -import { defaultCountLabel, LabelRotation } from '../../../charts/public'; +import { AggGroupNames } from '../../../../data/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../visualizations/public'; +import { defaultCountLabel, LabelRotation } from '../../../../charts/public'; import { ChartMode, diff --git a/src/plugins/vis_type_xy/server/index.ts b/src/plugins/vis_types/xy/server/index.ts similarity index 100% rename from src/plugins/vis_type_xy/server/index.ts rename to src/plugins/vis_types/xy/server/index.ts diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_types/xy/server/plugin.ts similarity index 100% rename from src/plugins/vis_type_xy/server/plugin.ts rename to src/plugins/vis_types/xy/server/plugin.ts diff --git a/src/plugins/vis_types/xy/tsconfig.json b/src/plugins/vis_types/xy/tsconfig.json new file mode 100644 index 0000000000000..f1f65b6218e82 --- /dev/null +++ b/src/plugins/vis_types/xy/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*" + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../usage_collection/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../vis_default_editor/tsconfig.json" }, + ] +} diff --git a/test/api_integration/apis/index_patterns/has_user_index_pattern/has_user_index_pattern.ts b/test/api_integration/apis/index_patterns/has_user_index_pattern/has_user_index_pattern.ts new file mode 100644 index 0000000000000..8dfb892acfd90 --- /dev/null +++ b/test/api_integration/apis/index_patterns/has_user_index_pattern/has_user_index_pattern.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + describe('has user index pattern API', () => { + beforeEach(async () => { + await esArchiver.emptyKibanaIndex(); + if ((await es.indices.exists({ index: 'metrics-test' })).body) { + await es.indices.delete({ index: 'metrics-test' }); + } + + if ((await es.indices.exists({ index: 'logs-test' })).body) { + await es.indices.delete({ index: 'logs-test' }); + } + }); + + it('should return false if no index patterns', async () => { + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(false); + }); + + it('should return true if has index pattern with user data', async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'basic_index', + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + it('should return true if has user index pattern without data', async () => { + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'basic_index', + allowNoIndex: true, + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + }); + + it('should return false if only metric-* index pattern without data', async () => { + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'metrics-*', + allowNoIndex: true, + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(false); + }); + + it('should return true if metric-* index pattern with user data', async () => { + await es.index({ + index: 'metrics-test', + body: { + foo: 'bar', + }, + }); + + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'metrics-*', + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + }); + + it('should return false if only logs-* index pattern without data', async () => { + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'logs-*', + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(false); + }); + + it('should return true if logs-* index pattern with user data', async () => { + await es.index({ + index: 'logs-test', + body: { + foo: 'bar', + }, + }); + + await supertest.post('/api/index_patterns/index_pattern').send({ + override: true, + index_pattern: { + title: 'logs-*', + }, + }); + + const response = await supertest.get('/api/index_patterns/has_user_index_pattern'); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + }); + + // TODO: should setup fleet first similar to x-pack/test/fleet_functional/apps/home/welcome.ts + // but it is skipped due to flakiness https://github.com/elastic/kibana/issues/109017 + it('should return false if logs-* with .ds-logs-elastic_agent only'); + it('should return false if metrics-* with .ds-metrics-elastic_agent only'); + }); +} diff --git a/packages/kbn-securitysolution-t-grid/babel.config.js b/test/api_integration/apis/index_patterns/has_user_index_pattern/index.ts similarity index 58% rename from packages/kbn-securitysolution-t-grid/babel.config.js rename to test/api_integration/apis/index_patterns/has_user_index_pattern/index.ts index b4a118df51af5..5c05467d15c3f 100644 --- a/packages/kbn-securitysolution-t-grid/babel.config.js +++ b/test/api_integration/apis/index_patterns/has_user_index_pattern/index.ts @@ -6,14 +6,10 @@ * Side Public License, v 1. */ -module.exports = { - env: { - web: { - presets: ['@kbn/babel-preset/webpack_preset'], - }, - node: { - presets: ['@kbn/babel-preset/node_preset'], - }, - }, - ignore: ['**/*.test.ts', '**/*.test.tsx'], -}; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('has user index pattern', () => { + loadTestFile(require.resolve('./has_user_index_pattern')); + }); +} diff --git a/test/api_integration/apis/index_patterns/index.js b/test/api_integration/apis/index_patterns/index.js index 3dbe01206afa3..b34012e362cbf 100644 --- a/test/api_integration/apis/index_patterns/index.js +++ b/test/api_integration/apis/index_patterns/index.js @@ -18,5 +18,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./runtime_fields_crud')); loadTestFile(require.resolve('./integration')); loadTestFile(require.resolve('./deprecations')); + loadTestFile(require.resolve('./has_user_index_pattern')); }); } diff --git a/test/api_integration/apis/search/index.ts b/test/api_integration/apis/search/index.ts index 38c937cb04cb8..d5d6e928b5483 100644 --- a/test/api_integration/apis/search/index.ts +++ b/test/api_integration/apis/search/index.ts @@ -12,6 +12,5 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('search', () => { loadTestFile(require.resolve('./search')); loadTestFile(require.resolve('./bsearch')); - loadTestFile(require.resolve('./msearch')); }); } diff --git a/test/api_integration/apis/search/msearch.ts b/test/api_integration/apis/search/msearch.ts deleted file mode 100644 index d50e0e9a259f2..0000000000000 --- a/test/api_integration/apis/search/msearch.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('msearch', () => { - describe('post', () => { - it('should return 200 when correctly formatted searches are provided', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - searches: [ - { - header: { index: 'foo' }, - body: { - query: { - match_all: {}, - }, - }, - }, - ], - }) - .expect(200)); - - it('should return 400 if you provide malformed content', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - foo: false, - }) - .expect(400)); - - it('should require you to provide an index for each request', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - searches: [ - { header: { index: 'foo' }, body: {} }, - { header: {}, body: {} }, - ], - }) - .expect(400)); - - it('should not require optional params', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - searches: [{ header: { index: 'foo' }, body: {} }], - }) - .expect(200)); - - it('should allow passing preference as a string', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - searches: [{ header: { index: 'foo', preference: '_custom' }, body: {} }], - }) - .expect(200)); - - it('should allow passing preference as a number', async () => - await supertest - .post(`/internal/_msearch`) - .send({ - searches: [{ header: { index: 'foo', preference: 123 }, body: {} }], - }) - .expect(200)); - }); - }); -} diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index 46fe5c34f4cf3..a77bc4c77568a 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -10,7 +10,6 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from './ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const log = getService('log'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); @@ -33,12 +32,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { - await esArchiver.load('test/functional/fixtures/es_archiver/discover'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.uiSettings.replace(defaultSettings); - log.debug('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setDefaultAbsoluteRange(); }); it('allows adding custom label to existing fields', async function () { diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_1.json b/test/interpreter_functional/snapshots/baseline/partial_test_1.json index e0b62688d0662..082c7b934c17c 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_1.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_1.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json index d85444f5d3b6b..9813a3ca036a1 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json index 2c81c9447b826..bef1b10120fe5 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json index 687b669b18e61..3e594380588dc 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json index b49953f9a023b..bea6dad294e01 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json index fc7e289dfbd3a..c45b063fdb542 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"default","type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"default","type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_1.json b/test/interpreter_functional/snapshots/session/partial_test_1.json index e0b62688d0662..082c7b934c17c 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_1.json +++ b/test/interpreter_functional/snapshots/session/partial_test_1.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json index d85444f5d3b6b..9813a3ca036a1 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json index 2c81c9447b826..bef1b10120fe5 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json b/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json index 687b669b18e61..3e594380588dc 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json index b49953f9a023b..bea6dad294e01 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"default","type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_options.json b/test/interpreter_functional/snapshots/session/tagcloud_options.json index fc7e289dfbd3a..c45b063fdb542 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"default","type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"default","type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 9b98ca864f496..b51363f1b7006 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -13,7 +13,6 @@ "xpack.dashboard": "plugins/dashboard_enhanced", "xpack.discover": "plugins/discover_enhanced", "xpack.crossClusterReplication": "plugins/cross_cluster_replication", - "xpack.dashboardMode": "plugins/dashboard_mode", "xpack.data": "plugins/data_enhanced", "xpack.embeddableEnhanced": "plugins/embeddable_enhanced", "xpack.endpoint": "plugins/endpoint", diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index 8a22ded2886ff..8322e42b0743c 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MockedLogger, loggerMock } from '@kbn/logging/target/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging/mocks'; import { TaskRunnerFactory } from '../task_runner'; import { RuleTypeRegistry, ConstructorOptions } from '../rule_type_registry'; import { taskManagerMock } from '../../../task_manager/server/mocks'; diff --git a/x-pack/plugins/apm/common/search_strategies/failure_correlations/types.ts b/x-pack/plugins/apm/common/search_strategies/failure_correlations/types.ts index 08e05d46ba013..2b0d2b5642e0c 100644 --- a/x-pack/plugins/apm/common/search_strategies/failure_correlations/types.ts +++ b/x-pack/plugins/apm/common/search_strategies/failure_correlations/types.ts @@ -15,6 +15,9 @@ export interface FailedTransactionsCorrelationValue { pValue: number | null; fieldName: string; fieldValue: string; + normalizedScore: number; + failurePercentage: number; + successPercentage: number; } export type FailureCorrelationImpactThreshold = typeof FAILED_TRANSACTIONS_IMPACT_THRESHOLD[keyof typeof FAILED_TRANSACTIONS_IMPACT_THRESHOLD]; diff --git a/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts b/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts index 9efb7184f3927..54e74330c0604 100644 --- a/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts @@ -165,6 +165,34 @@ describe('date time formatters', () => { 'Dec 1, 2019, 13:00:00.000 (UTC+1)' ); }); + + it('milliseconds', () => { + moment.tz.setDefault('Europe/Copenhagen'); + expect(asAbsoluteDateTime(1559390400000, 'milliseconds')).toBe( + 'Jun 1, 2019, 14:00:00.000 (UTC+2)' + ); + }); + + it('seconds', () => { + moment.tz.setDefault('Europe/Copenhagen'); + expect(asAbsoluteDateTime(1559390400000, 'seconds')).toBe( + 'Jun 1, 2019, 14:00:00 (UTC+2)' + ); + }); + + it('minutes', () => { + moment.tz.setDefault('Europe/Copenhagen'); + expect(asAbsoluteDateTime(1559390400000, 'minutes')).toBe( + 'Jun 1, 2019, 14:00 (UTC+2)' + ); + }); + + it('hours', () => { + moment.tz.setDefault('Europe/Copenhagen'); + expect(asAbsoluteDateTime(1559390400000, 'hours')).toBe( + 'Jun 1, 2019, 14 (UTC+2)' + ); + }); }); describe('getDateDifference', () => { it('milliseconds', () => { diff --git a/x-pack/plugins/apm/common/utils/formatters/duration.test.ts b/x-pack/plugins/apm/common/utils/formatters/duration.test.ts index 42ae4d54bced3..a45582f42b5b2 100644 --- a/x-pack/plugins/apm/common/utils/formatters/duration.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/duration.test.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { asDuration, toMicroseconds, asMillisecondDuration } from './duration'; +import { + asDuration, + getDurationFormatter, + toMicroseconds, + asMillisecondDuration, +} from './duration'; describe('duration formatters', () => { describe('asDuration', () => { @@ -35,6 +40,50 @@ describe('duration formatters', () => { }); }); + describe('getDurationFormatter', () => { + // Formatting with a default threshold of 10 for more detail for single values + it('formats correctly with defaults', () => { + expect(getDurationFormatter(987654)(987654).formatted).toEqual('988 ms'); + expect(getDurationFormatter(1000000)(1000000).formatted).toEqual( + '1,000 ms' + ); + expect(getDurationFormatter(1234567)(1234567).formatted).toEqual( + '1,235 ms' + ); + expect(getDurationFormatter(9876543)(9876543).formatted).toEqual( + '9,877 ms' + ); + expect(getDurationFormatter(10000000)(10000000).formatted).toEqual( + '10,000 ms' + ); + expect(getDurationFormatter(12345678)(12345678).formatted).toEqual( + '12 s' + ); + }); + + // Formatting useful for axis ticks with a lower threshold where less detail is sufficient + it('formats correctly with a threshold of 0.9999', () => { + expect(getDurationFormatter(987654, 0.9999)(987654).formatted).toEqual( + '988 ms' + ); + expect(getDurationFormatter(1000000, 0.9999)(1000000).formatted).toEqual( + '1 s' + ); + expect(getDurationFormatter(1234567, 0.9999)(1234567).formatted).toEqual( + '1 s' + ); + expect(getDurationFormatter(9876543, 0.9999)(9876543).formatted).toEqual( + '10 s' + ); + expect( + getDurationFormatter(10000000, 0.9999)(10000000).formatted + ).toEqual('10 s'); + expect( + getDurationFormatter(12345678, 0.9999)(12345678).formatted + ).toEqual('12 s'); + }); + }); + describe('toMicroseconds', () => { it('transformes to microseconds', () => { expect(toMicroseconds(1, 'hours')).toEqual(3600000000); diff --git a/x-pack/plugins/apm/common/utils/formatters/duration.ts b/x-pack/plugins/apm/common/utils/formatters/duration.ts index 917521117af4e..bc4d58831ff35 100644 --- a/x-pack/plugins/apm/common/utils/formatters/duration.ts +++ b/x-pack/plugins/apm/common/utils/formatters/duration.ts @@ -33,12 +33,16 @@ export type TimeFormatter = ( options?: FormatterOptions ) => ConvertedDuration; -type TimeFormatterBuilder = (max: number) => TimeFormatter; +type TimeFormatterBuilder = (max: number, threshold?: number) => TimeFormatter; -export function getUnitLabelAndConvertedValue( +// threshold defines the value from which upwards there should be no decimal places. +function getUnitLabelAndConvertedValue( unitKey: DurationTimeUnit, - value: number + value: number, + threshold: number = 10 ) { + const ms = value / 1000; + switch (unitKey) { case 'hours': { return { @@ -46,7 +50,8 @@ export function getUnitLabelAndConvertedValue( defaultMessage: 'h', }), convertedValue: asDecimalOrInteger( - moment.duration(value / 1000).asHours() + moment.duration(ms).asHours(), + threshold ), }; } @@ -56,7 +61,8 @@ export function getUnitLabelAndConvertedValue( defaultMessage: 'min', }), convertedValue: asDecimalOrInteger( - moment.duration(value / 1000).asMinutes() + moment.duration(ms).asMinutes(), + threshold ), }; } @@ -66,7 +72,8 @@ export function getUnitLabelAndConvertedValue( defaultMessage: 's', }), convertedValue: asDecimalOrInteger( - moment.duration(value / 1000).asSeconds() + moment.duration(ms).asSeconds(), + threshold ), }; } @@ -76,7 +83,8 @@ export function getUnitLabelAndConvertedValue( defaultMessage: 'ms', }), convertedValue: asDecimalOrInteger( - moment.duration(value / 1000).asMilliseconds() + moment.duration(ms).asMilliseconds(), + threshold ), }; } @@ -98,10 +106,12 @@ function convertTo({ unit, microseconds, defaultValue = NOT_AVAILABLE_LABEL, + threshold = 10, }: { unit: DurationTimeUnit; microseconds: Maybe; defaultValue?: string; + threshold?: number; }): ConvertedDuration { if (!isFiniteNumber(microseconds)) { return { value: defaultValue, formatted: defaultValue }; @@ -109,7 +119,8 @@ function convertTo({ const { convertedValue, unitLabel } = getUnitLabelAndConvertedValue( unit, - microseconds + microseconds, + threshold ); return { @@ -122,10 +133,7 @@ function convertTo({ export const toMicroseconds = (value: number, timeUnit: TimeUnit) => moment.duration(value, timeUnit).asMilliseconds() * 1000; -export function getDurationUnitKey( - max: number, - threshold = 10 -): DurationTimeUnit { +function getDurationUnitKey(max: number, threshold = 10): DurationTimeUnit { if (max > toMicroseconds(threshold, 'hours')) { return 'hours'; } @@ -141,13 +149,16 @@ export function getDurationUnitKey( return 'microseconds'; } +// memoizer with a custom resolver to consider both arguments max/threshold. +// by default lodash's memoize only considers the first argument. export const getDurationFormatter: TimeFormatterBuilder = memoize( - (max: number) => { - const unit = getDurationUnitKey(max); - return (value, { defaultValue }: FormatterOptions = {}) => { - return convertTo({ unit, microseconds: value, defaultValue }); + (max: number, threshold: number = 10) => { + const unit = getDurationUnitKey(max, threshold); + return (value: Maybe, { defaultValue }: FormatterOptions = {}) => { + return convertTo({ unit, microseconds: value, defaultValue, threshold }); }; - } + }, + (max, threshold) => `${max}_${threshold}` ); export function asTransactionRate(value: Maybe) { diff --git a/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts b/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts index 230912045077d..f876b639c923d 100644 --- a/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts @@ -36,19 +36,40 @@ describe('formatters', () => { }); describe('asDecimalOrInteger', () => { - it('formats as integer when number equals to 0 ', () => { - expect(asDecimalOrInteger(0)).toEqual('0'); - }); - it('formats as integer when number is above or equals 10 ', () => { - expect(asDecimalOrInteger(10.123)).toEqual('10'); - expect(asDecimalOrInteger(15.123)).toEqual('15'); - }); - it('formats as decimal when number is below 10 ', () => { - expect(asDecimalOrInteger(0.25435632645)).toEqual('0.3'); - expect(asDecimalOrInteger(1)).toEqual('1.0'); - expect(asDecimalOrInteger(3.374329704990765)).toEqual('3.4'); - expect(asDecimalOrInteger(5)).toEqual('5.0'); - expect(asDecimalOrInteger(9)).toEqual('9.0'); + describe('with default threshold of 10', () => { + it('formats as integer when number equals to 0 ', () => { + expect(asDecimalOrInteger(0)).toEqual('0'); + }); + it('formats as integer when number is above or equals 10 ', () => { + expect(asDecimalOrInteger(10.123)).toEqual('10'); + expect(asDecimalOrInteger(15.123)).toEqual('15'); + }); + it('formats as decimal when number is below 10 ', () => { + expect(asDecimalOrInteger(0.25435632645)).toEqual('0.3'); + expect(asDecimalOrInteger(1)).toEqual('1.0'); + expect(asDecimalOrInteger(3.374329704990765)).toEqual('3.4'); + expect(asDecimalOrInteger(5)).toEqual('5.0'); + expect(asDecimalOrInteger(9)).toEqual('9.0'); + }); + }); + + describe('with custom threshold of 1', () => { + it('formats as integer when number equals to 0 ', () => { + expect(asDecimalOrInteger(0, 1)).toEqual('0'); + }); + it('formats as integer when number is above or equals 1 ', () => { + expect(asDecimalOrInteger(1, 1)).toEqual('1'); + expect(asDecimalOrInteger(1.123, 1)).toEqual('1'); + expect(asDecimalOrInteger(3.374329704990765, 1)).toEqual('3'); + expect(asDecimalOrInteger(5, 1)).toEqual('5'); + expect(asDecimalOrInteger(9, 1)).toEqual('9'); + expect(asDecimalOrInteger(10, 1)).toEqual('10'); + expect(asDecimalOrInteger(10.123, 1)).toEqual('10'); + expect(asDecimalOrInteger(15.123, 1)).toEqual('15'); + }); + it('formats as decimal when number is below 1 ', () => { + expect(asDecimalOrInteger(0.25435632645, 1)).toEqual('0.3'); + }); }); }); }); diff --git a/x-pack/plugins/apm/common/utils/formatters/formatters.ts b/x-pack/plugins/apm/common/utils/formatters/formatters.ts index 4da73a6d2c29a..67a259caa2534 100644 --- a/x-pack/plugins/apm/common/utils/formatters/formatters.ts +++ b/x-pack/plugins/apm/common/utils/formatters/formatters.ts @@ -55,9 +55,9 @@ export function asPercent( return numeral(decimal).format('0.0%'); } -export function asDecimalOrInteger(value: number) { - // exact 0 or above 10 should not have decimal - if (value === 0 || value >= 10) { +export function asDecimalOrInteger(value: number, threshold = 10) { + // exact 0 or above threshold should not have decimal + if (value === 0 || value >= threshold) { return asInteger(value); } return asDecimal(value); diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx index 57d141d763909..7293fb81f3303 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx @@ -45,7 +45,7 @@ export function AnomalyDetection() { if (!hasValidLicense) { return ( - + ); diff --git a/x-pack/plugins/apm/public/components/app/correlations/correlations_log.tsx b/x-pack/plugins/apm/public/components/app/correlations/correlations_log.tsx new file mode 100644 index 0000000000000..2115918a71415 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/correlations_log.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiAccordion, EuiCode, EuiPanel } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { asAbsoluteDateTime } from '../../../../common/utils/formatters'; + +interface Props { + logMessages: string[]; +} +export function CorrelationsLog({ logMessages }: Props) { + return ( + + + {logMessages.map((logMessage, i) => { + const [timestamp, message] = logMessage.split(': '); + return ( +

+ + {asAbsoluteDateTime(timestamp)} {message} + +

+ ); + })} +
+
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx index 28f671183ed87..f7e62b76a61c0 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx @@ -9,10 +9,12 @@ import React, { useCallback, useMemo, useState } from 'react'; import { debounce } from 'lodash'; import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import type { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; +import type { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { useUiTracker } from '../../../../../observability/public'; import { useTheme } from '../../../hooks/use_theme'; -import { CorrelationsTerm } from '../../../../common/search_strategies/failure_correlations/types'; +import type { CorrelationsTerm } from '../../../../common/search_strategies/failure_correlations/types'; const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; @@ -29,6 +31,8 @@ interface Props { selectedTerm?: { fieldName: string; fieldValue: string }; onFilter?: () => void; columns: Array>; + onTableChange: (c: Criteria) => void; + sorting?: EuiTableSortingType; } export function CorrelationsTable({ @@ -37,6 +41,8 @@ export function CorrelationsTable({ setSelectedSignificantTerm, columns, selectedTerm, + onTableChange, + sorting, }: Props) { const euiTheme = useTheme(); const trackApmEvent = useUiTracker({ app: 'apm' }); @@ -67,12 +73,17 @@ export function CorrelationsTable({ }; }, [pageIndex, pageSize, significantTerms]); - const onTableChange = useCallback(({ page }) => { - const { index, size } = page; + const onChange = useCallback( + (tableSettings) => { + const { index, size } = tableSettings.page; - setPageIndex(index); - setPageSize(size); - }, []); + setPageIndex(index); + setPageSize(size); + + onTableChange(tableSettings); + }, + [onTableChange] + ); return ( ({ }; }} pagination={pagination} - onChange={onTableChange} + onChange={onChange} + sorting={sorting} /> ); } diff --git a/x-pack/plugins/apm/public/components/app/correlations/cross_cluster_search_warning.tsx b/x-pack/plugins/apm/public/components/app/correlations/cross_cluster_search_warning.tsx new file mode 100644 index 0000000000000..9d5ca09ad3add --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/cross_cluster_search_warning.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCallOut } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +export function CrossClusterSearchCompatibilityWarning({ + version, +}: { + version: string; +}) { + return ( + +

+ {i18n.translate('xpack.apm.correlations.ccsWarningCalloutBody', { + defaultMessage: + 'Data for the correlation analysis could not be fully retrieved. This feature is supported only for {version} and later versions.', + values: { version }, + })} +

+
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/empty_state_prompt.tsx b/x-pack/plugins/apm/public/components/app/correlations/empty_state_prompt.tsx new file mode 100644 index 0000000000000..57e57a526baff --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/empty_state_prompt.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiEmptyPrompt, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export function CorrelationsEmptyStatePrompt() { + return ( + <> + + +

+ {i18n.translate('xpack.apm.correlations.noCorrelationsTitle', { + defaultMessage: 'No significant correlations', + })} +

+ + } + body={ + <> + +

+ +

+

+ +

+
+ + } + /> + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx index 4fdd908b6faf6..1cc115861a594 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx @@ -5,45 +5,46 @@ * 2.0. */ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { - EuiCallOut, - EuiCode, - EuiAccordion, - EuiPanel, EuiBasicTableColumn, - EuiButton, EuiFlexGroup, EuiFlexItem, - EuiProgress, EuiSpacer, - EuiText, - EuiBadge, EuiIcon, EuiLink, EuiTitle, EuiBetaBadge, + EuiBadge, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { useHistory } from 'react-router-dom'; +import { orderBy } from 'lodash'; +import type { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; +import type { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { CorrelationsTable } from './correlations_table'; import { enableInspectEsQueries } from '../../../../../observability/public'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { FailedTransactionsCorrelationsHelpPopover } from './failed_transactions_correlations_help_popover'; -import { FailedTransactionsCorrelationValue } from '../../../../common/search_strategies/failure_correlations/types'; import { ImpactBar } from '../../shared/ImpactBar'; import { isErrorMessage } from './utils/is_error_message'; -import { Summary } from '../../shared/Summary'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { getFailedTransactionsCorrelationImpactLabel } from './utils/get_failed_transactions_correlation_impact_label'; import { createHref, push } from '../../shared/Links/url_helpers'; import { useUiTracker } from '../../../../../observability/public'; import { useFailedTransactionsCorrelationsFetcher } from '../../../hooks/use_failed_transactions_correlations_fetcher'; -import { SearchServiceParams } from '../../../../common/search_strategies/correlations/types'; import { useApmParams } from '../../../hooks/use_apm_params'; +import { CorrelationsLog } from './correlations_log'; +import { CorrelationsEmptyStatePrompt } from './empty_state_prompt'; +import { CrossClusterSearchCompatibilityWarning } from './cross_cluster_search_warning'; +import { CorrelationsProgressControls } from './progress_controls'; +import type { SearchServiceParams } from '../../../../common/search_strategies/correlations/types'; +import type { FailedTransactionsCorrelationValue } from '../../../../common/search_strategies/failure_correlations/types'; +import { Summary } from '../../shared/Summary'; +import { asPercent } from '../../../../common/utils/formatters'; export function FailedTransactionsCorrelations({ onFilter, @@ -64,7 +65,7 @@ export function FailedTransactionsCorrelations({ const { urlParams } = useUrlParams(); const { transactionName, start, end } = urlParams; - const displayLog = uiSettings.get(enableInspectEsQueries); + const inspectEnabled = uiSettings.get(enableInspectEsQueries); const searchServicePrams: SearchServiceParams = { environment, @@ -76,7 +77,7 @@ export function FailedTransactionsCorrelations({ end, }; - const result = useFailedTransactionsCorrelationsFetcher(searchServicePrams); + const result = useFailedTransactionsCorrelationsFetcher(); const { ccsWarning, @@ -87,10 +88,20 @@ export function FailedTransactionsCorrelations({ startFetch, cancelFetch, } = result; + + const startFetchHandler = useCallback(() => { + startFetch(searchServicePrams); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [environment, serviceName, kuery, start, end]); + // start fetching on load // we want this effect to execute exactly once after the component mounts useEffect(() => { - startFetch(); + if (isRunning) { + cancelFetch(); + } + + startFetchHandler(); return () => { // cancel any running async partial request when unmounting the component @@ -98,7 +109,7 @@ export function FailedTransactionsCorrelations({ cancelFetch(); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [startFetchHandler]); const [ selectedSignificantTerm, @@ -118,10 +129,83 @@ export function FailedTransactionsCorrelations({ const failedTransactionsCorrelationsColumns: Array< EuiBasicTableColumn - > = useMemo( - () => [ + > = useMemo(() => { + const percentageColumns: Array< + EuiBasicTableColumn + > = inspectEnabled + ? [ + { + width: '100px', + field: 'failurePercentage', + name: ( + + <> + {i18n.translate( + 'xpack.apm.correlations.failedTransactions.correlationsTable.failurePercentageLabel', + { + defaultMessage: 'Failure %', + } + )} + + + + ), + render: (failurePercentage: number) => + asPercent(failurePercentage, 1), + sortable: true, + }, + { + field: 'successPercentage', + width: '100px', + name: ( + + <> + {i18n.translate( + 'xpack.apm.correlations.failedTransactions.correlationsTable.successPercentageLabel', + { + defaultMessage: 'Success %', + } + )} + + + + ), + + render: (successPercentage: number) => + asPercent(successPercentage, 1), + sortable: true, + }, + ] + : []; + return [ { - width: '116px', + width: '80px', field: 'normalizedScore', name: ( <> @@ -140,6 +224,7 @@ export function FailedTransactionsCorrelations({ ); }, + sortable: true, }, { width: '116px', @@ -154,7 +239,13 @@ export function FailedTransactionsCorrelations({ )} ), - render: getFailedTransactionsCorrelationImpactLabel, + render: (pValue: number) => { + const label = getFailedTransactionsCorrelationImpactLabel(pValue); + return label ? ( + {label.impact} + ) : null; + }, + sortable: true, }, { field: 'fieldName', @@ -162,6 +253,7 @@ export function FailedTransactionsCorrelations({ 'xpack.apm.correlations.failedTransactions.correlationsTable.fieldNameLabel', { defaultMessage: 'Field name' } ), + sortable: true, }, { field: 'key', @@ -170,7 +262,9 @@ export function FailedTransactionsCorrelations({ { defaultMessage: 'Field value' } ), render: (fieldValue: string) => String(fieldValue).slice(0, 50), + sortable: true, }, + ...percentageColumns, { width: '100px', actions: [ @@ -188,9 +282,7 @@ export function FailedTransactionsCorrelations({ onClick: (term: FailedTransactionsCorrelationValue) => { push(history, { query: { - kuery: `${term.fieldName}:"${encodeURIComponent( - term.fieldValue - )}"`, + kuery: `${term.fieldName}:"${term.fieldValue}"`, }, }); onFilter(); @@ -211,9 +303,7 @@ export function FailedTransactionsCorrelations({ onClick: (term: FailedTransactionsCorrelationValue) => { push(history, { query: { - kuery: `not ${term.fieldName}:"${encodeURIComponent( - term.fieldValue - )}"`, + kuery: `not ${term.fieldName}:"${term.fieldValue}"`, }, }); onFilter(); @@ -231,9 +321,7 @@ export function FailedTransactionsCorrelations({ @@ -243,9 +331,7 @@ export function FailedTransactionsCorrelations({ @@ -255,9 +341,8 @@ export function FailedTransactionsCorrelations({ ); }, }, - ], - [history, onFilter, trackApmEvent] - ); + ] as Array>; + }, [history, onFilter, trackApmEvent, inspectEnabled]); useEffect(() => { if (isErrorMessage(error)) { @@ -273,100 +358,124 @@ export function FailedTransactionsCorrelations({ }); } }, [error, notifications.toasts]); + + const [sortField, setSortField] = useState< + keyof FailedTransactionsCorrelationValue + >('normalizedScore'); + const [sortDirection, setSortDirection] = useState('desc'); + + const onTableChange = useCallback(({ sort }) => { + const { field: currentSortField, direction: currentSortDirection } = sort; + + setSortField(currentSortField); + setSortDirection(currentSortDirection); + }, []); + + const { sorting, correlationTerms } = useMemo(() => { + if (!Array.isArray(result.values)) { + return { correlationTerms: [], sorting: undefined }; + } + const orderedTerms = orderBy( + result.values, + // The smaller the p value the higher the impact + // So we want to sort by the normalized score here + // which goes from 0 -> 1 + sortField === 'pValue' ? 'normalizedScore' : sortField, + sortDirection + ); + return { + correlationTerms: orderedTerms, + sorting: { + sort: { + field: sortField, + direction: sortDirection, + }, + } as EuiTableSortingType, + }; + }, [result?.values, sortField, sortDirection]); + return ( - <> - - - -
- {i18n.translate( - 'xpack.apm.correlations.failedTransactions.panelTitle', +
+ + + + +
+ {i18n.translate( + 'xpack.apm.correlations.failedTransactions.panelTitle', + { + defaultMessage: 'Failed transactions', + } + )} +
+
+
+ + + - - + title={i18n.translate( + 'xpack.apm.transactionDetails.tabs.failedTransactionsCorrelationsBetaTitle', + { + defaultMessage: 'Failed transaction correlations', + } + )} + tooltipContent={i18n.translate( + 'xpack.apm.transactionDetails.tabs.failedTransactionsCorrelationsBetaDescription', + { + defaultMessage: + 'Failed transaction correlations is not GA. Please help us by reporting any bugs.', + } + )} + /> +
+ - + - + - - - {!isRunning && ( - - - - )} - {isRunning && ( - - - + + + + + {i18n.translate( + 'xpack.apm.correlations.failedTransactions.tableTitle', + { + defaultMessage: 'Correlations', + } )} - - - - - - - - - - - - - - - - - - {selectedTerm?.pValue != null ? ( + + + + + + + + {ccsWarning && ( + <> + + + + )} + + {inspectEnabled && + selectedTerm?.pValue != null && + (isRunning || correlationTerms.length > 0) ? ( <> {`p-value: ${selectedTerm.pValue.toPrecision(3)}`}, ]} /> - ) : null} - - columns={failedTransactionsCorrelationsColumns} - significantTerms={result?.values} - status={FETCH_STATUS.SUCCESS} - setSelectedSignificantTerm={setSelectedSignificantTerm} - selectedTerm={selectedTerm} - /> - {ccsWarning && ( - <> - - -

- {i18n.translate( - 'xpack.apm.correlations.failedTransactions.ccsWarningCalloutBody', - { - defaultMessage: - 'Data for the correlation analysis could not be fully retrieved. This feature is supported only for 7.15 and later versions.', - } - )} -

-
- - )} - {log.length > 0 && displayLog && ( - - - {log.map((d, i) => { - const splitItem = d.split(': '); - return ( -

- - {splitItem[0]} {splitItem[1]} - -

- ); - })} -
-
- )} - +
+ {(isRunning || correlationTerms.length > 0) && ( + + columns={failedTransactionsCorrelationsColumns} + significantTerms={correlationTerms} + status={isRunning ? FETCH_STATUS.LOADING : FETCH_STATUS.SUCCESS} + setSelectedSignificantTerm={setSelectedSignificantTerm} + selectedTerm={selectedTerm} + onTableChange={onTableChange} + sorting={sorting} + /> + )} + {correlationTerms.length < 1 && (progress === 1 || !isRunning) && ( + + )} +
+ {inspectEnabled && } +
); } diff --git a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations_help_popover.tsx b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations_help_popover.tsx index bebc889cc4ed9..e66101d619224 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations_help_popover.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations_help_popover.tsx @@ -18,6 +18,7 @@ export function FailedTransactionsCorrelationsHelpPopover() { anchorPosition="leftUp" button={ { setIsPopoverOpen((prevIsPopoverOpen) => !prevIsPopoverOpen); }} @@ -25,25 +26,28 @@ export function FailedTransactionsCorrelationsHelpPopover() { } closePopover={() => setIsPopoverOpen(false)} isOpen={isPopoverOpen} - title={i18n.translate('xpack.apm.correlations.failurePopoverTitle', { - defaultMessage: 'Failure correlations', - })} + title={i18n.translate( + 'xpack.apm.correlations.failedTransactions.helpPopover.title', + { + defaultMessage: 'Failed transaction correlations', + } + )} >

diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx index 6d6e56184e254..ed47d49948fba 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx @@ -8,24 +8,18 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { - EuiCallOut, - EuiCode, - EuiEmptyPrompt, - EuiAccordion, - EuiPanel, EuiIcon, EuiBasicTableColumn, - EuiButton, EuiFlexGroup, EuiFlexItem, - EuiProgress, EuiSpacer, - EuiText, EuiTitle, EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; +import { orderBy } from 'lodash'; +import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; @@ -42,6 +36,10 @@ import { useApmServiceContext } from '../../../context/apm_service/use_apm_servi import { LatencyCorrelationsHelpPopover } from './latency_correlations_help_popover'; import { useApmParams } from '../../../hooks/use_apm_params'; import { isErrorMessage } from './utils/is_error_message'; +import { CorrelationsLog } from './correlations_log'; +import { CorrelationsEmptyStatePrompt } from './empty_state_prompt'; +import { CrossClusterSearchCompatibilityWarning } from './cross_cluster_search_warning'; +import { CorrelationsProgressControls } from './progress_controls'; const DEFAULT_PERCENTILE_THRESHOLD = 95; @@ -133,15 +131,19 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { setSelectedSignificantTerm, ] = useState(null); - let selectedHistogram = histograms.length > 0 ? histograms[0] : undefined; + const selectedHistogram = useMemo(() => { + let selected = histograms.length > 0 ? histograms[0] : undefined; + + if (histograms.length > 0 && selectedSignificantTerm !== null) { + selected = histograms.find( + (h) => + h.field === selectedSignificantTerm.fieldName && + h.value === selectedSignificantTerm.fieldValue + ); + } + return selected; + }, [histograms, selectedSignificantTerm]); - if (histograms.length > 0 && selectedSignificantTerm !== null) { - selectedHistogram = histograms.find( - (h) => - h.field === selectedSignificantTerm.fieldName && - h.value === selectedSignificantTerm.fieldValue - ); - } const history = useHistory(); const trackApmEvent = useUiTracker({ app: 'apm' }); @@ -181,6 +183,7 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { render: (correlation: number) => { return
{asPreciseDecimal(correlation, 2)}
; }, + sortable: true, }, { field: 'fieldName', @@ -188,6 +191,7 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { 'xpack.apm.correlations.latencyCorrelations.correlationsTable.fieldNameLabel', { defaultMessage: 'Field name' } ), + sortable: true, }, { field: 'fieldValue', @@ -196,6 +200,7 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { { defaultMessage: 'Field value' } ), render: (fieldValue: string) => String(fieldValue).slice(0, 50), + sortable: true, }, { width: '100px', @@ -214,9 +219,7 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { onClick: (term: MlCorrelationsTerms) => { push(history, { query: { - kuery: `${term.fieldName}:"${encodeURIComponent( - term.fieldValue - )}"`, + kuery: `${term.fieldName}:"${term.fieldValue}"`, }, }); onFilter(); @@ -237,9 +240,7 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { onClick: (term: MlCorrelationsTerms) => { push(history, { query: { - kuery: `not ${term.fieldName}:"${encodeURIComponent( - term.fieldValue - )}"`, + kuery: `not ${term.fieldName}:"${term.fieldValue}"`, }, }); onFilter(); @@ -256,17 +257,46 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { [history, onFilter, trackApmEvent] ); - const histogramTerms: MlCorrelationsTerms[] = useMemo(() => { - return histograms.map((d) => { - return { - fieldName: d.field, - fieldValue: d.value, - ksTest: d.ksTest, - correlation: d.correlation, - duplicatedFields: d.duplicatedFields, - }; - }); - }, [histograms]); + const [sortField, setSortField] = useState( + 'correlation' + ); + const [sortDirection, setSortDirection] = useState('desc'); + + const onTableChange = useCallback(({ sort }) => { + const { field: currentSortField, direction: currentSortDirection } = sort; + + setSortField(currentSortField); + setSortDirection(currentSortDirection); + }, []); + + const { histogramTerms, sorting } = useMemo(() => { + if (!Array.isArray(histograms)) { + return { histogramTerms: [], sorting: undefined }; + } + const orderedTerms = orderBy( + histograms.map((d) => { + return { + fieldName: d.field, + fieldValue: d.value, + ksTest: d.ksTest, + correlation: d.correlation, + duplicatedFields: d.duplicatedFields, + }; + }), + sortField, + sortDirection + ); + + return { + histogramTerms: orderedTerms, + sorting: { + sort: { + field: sortField, + direction: sortDirection, + }, + } as EuiTableSortingType, + }; + }, [histograms, sortField, sortDirection]); return (
@@ -300,88 +330,34 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { - +
{i18n.translate( 'xpack.apm.correlations.latencyCorrelations.tableTitle', { defaultMessage: 'Correlations', } )} - +
- - - - - - - - - - - - - - - {!isRunning && ( - - - - )} - {isRunning && ( - - - - )} - - + + {ccsWarning && ( <> - -

- {i18n.translate( - 'xpack.apm.correlations.latencyCorrelations.ccsWarningCalloutBody', - { - defaultMessage: - 'Data for the correlation analysis could not be fully retrieved. This feature is supported only for 7.14 and later versions.', - } - )} -

-
+ )} + +
{(isRunning || histogramTerms.length > 0) && ( @@ -397,70 +373,15 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { } : undefined } + onTableChange={onTableChange} + sorting={sorting} /> )} {histogramTerms.length < 1 && (progress === 1 || !isRunning) && ( - <> - - -

- {i18n.translate( - 'xpack.apm.correlations.latencyCorrelations.noCorrelationsTitle', - { - defaultMessage: 'No significant correlations', - } - )} -

- - } - body={ - <> - - - - {/* Another EuiText element to enforce a line break */} - - - - - } - /> - + )}
- {log.length > 0 && displayLog && ( - - - {log.map((d, i) => { - const splitItem = d.split(': '); - return ( -

- - {splitItem[0]} {splitItem[1]} - -

- ); - })} -
-
- )} + {displayLog && }
); } diff --git a/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx b/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx new file mode 100644 index 0000000000000..a581313d6a5d5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiProgress, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +export function CorrelationsProgressControls({ + progress, + onRefresh, + onCancel, + isRunning, +}: { + progress: number; + onRefresh: () => void; + onCancel: () => void; + isRunning: boolean; +}) { + return ( + + + + + + + + + + + + + + + {!isRunning && ( + + + + )} + {isRunning && ( + + + + )} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.test.ts b/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.test.ts index d133ed1060ebe..edb7c8c16e267 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.test.ts +++ b/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.test.ts @@ -8,6 +8,20 @@ import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label'; import { FAILED_TRANSACTIONS_IMPACT_THRESHOLD } from '../../../../../common/search_strategies/failure_correlations/constants'; +const EXPECTED_RESULT = { + HIGH: { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.HIGH, + color: 'danger', + }, + MEDIUM: { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM, + color: 'warning', + }, + LOW: { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.LOW, + color: 'default', + }, +}; describe('getFailedTransactionsCorrelationImpactLabel', () => { it('returns null if value is invalid ', () => { expect(getFailedTransactionsCorrelationImpactLabel(-0.03)).toBe(null); @@ -21,32 +35,32 @@ describe('getFailedTransactionsCorrelationImpactLabel', () => { }); it('returns High if value is within [0, 1e-6) ', () => { - expect(getFailedTransactionsCorrelationImpactLabel(0)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.HIGH + expect(getFailedTransactionsCorrelationImpactLabel(0)).toStrictEqual( + EXPECTED_RESULT.HIGH ); - expect(getFailedTransactionsCorrelationImpactLabel(1e-7)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.HIGH + expect(getFailedTransactionsCorrelationImpactLabel(1e-7)).toStrictEqual( + EXPECTED_RESULT.HIGH ); }); it('returns Medium if value is within [1e-6, 1e-3) ', () => { - expect(getFailedTransactionsCorrelationImpactLabel(1e-6)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM + expect(getFailedTransactionsCorrelationImpactLabel(1e-6)).toStrictEqual( + EXPECTED_RESULT.MEDIUM ); - expect(getFailedTransactionsCorrelationImpactLabel(1e-5)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM + expect(getFailedTransactionsCorrelationImpactLabel(1e-5)).toStrictEqual( + EXPECTED_RESULT.MEDIUM ); - expect(getFailedTransactionsCorrelationImpactLabel(1e-4)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM + expect(getFailedTransactionsCorrelationImpactLabel(1e-4)).toStrictEqual( + EXPECTED_RESULT.MEDIUM ); }); it('returns Low if value is within [1e-3, 0.02) ', () => { - expect(getFailedTransactionsCorrelationImpactLabel(1e-3)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.LOW + expect(getFailedTransactionsCorrelationImpactLabel(1e-3)).toStrictEqual( + EXPECTED_RESULT.LOW ); - expect(getFailedTransactionsCorrelationImpactLabel(0.009)).toBe( - FAILED_TRANSACTIONS_IMPACT_THRESHOLD.LOW + expect(getFailedTransactionsCorrelationImpactLabel(0.009)).toStrictEqual( + EXPECTED_RESULT.LOW ); }); }); diff --git a/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.ts b/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.ts index af64c50617019..5a806aba5371e 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.ts +++ b/x-pack/plugins/apm/public/components/app/correlations/utils/get_failed_transactions_correlation_impact_label.ts @@ -10,14 +10,23 @@ import { FAILED_TRANSACTIONS_IMPACT_THRESHOLD } from '../../../../../common/sear export function getFailedTransactionsCorrelationImpactLabel( pValue: number -): FailureCorrelationImpactThreshold | null { +): { impact: FailureCorrelationImpactThreshold; color: string } | null { // The lower the p value, the higher the impact if (pValue >= 0 && pValue < 1e-6) - return FAILED_TRANSACTIONS_IMPACT_THRESHOLD.HIGH; + return { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.HIGH, + color: 'danger', + }; if (pValue >= 1e-6 && pValue < 0.001) - return FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM; + return { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.MEDIUM, + color: 'warning', + }; if (pValue >= 0.001 && pValue < 0.02) - return FAILED_TRANSACTIONS_IMPACT_THRESHOLD.LOW; + return { + impact: FAILED_TRANSACTIONS_IMPACT_THRESHOLD.LOW, + color: 'default', + }; return null; } diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx index 5a55fd1979c91..0432cdb2dff5e 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx @@ -21,7 +21,11 @@ import { ApmRoutes } from '../../../routing/apm_route_config'; import { StatsList } from './stats_list'; export function BackendContents({ nodeData, environment }: ContentsProps) { - const { query } = useApmParams('/*'); + const { query } = useApmParams( + '/service-map', + '/services/:serviceName/service-map' + ); + const apmRouter = useApmRouter(); const { urlParams: { start, end }, diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.ts b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.ts new file mode 100644 index 0000000000000..f541c16e655ab --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getFormattedSelection } from './index'; + +describe('transaction_details/distribution', () => { + describe('getFormattedSelection', () => { + it('displays only one unit if from and to share the same unit', () => { + expect(getFormattedSelection([10000, 100000])).toEqual('10 - 100 ms'); + }); + + it('displays two units when from and to have different units', () => { + expect(getFormattedSelection([100000, 1000000000])).toEqual( + '100 ms - 17 min' + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx index 49d28fec1a136..86bef9917daf6 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx @@ -17,6 +17,7 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useTransactionDistributionFetcher } from '../../../../hooks/use_transaction_distribution_fetcher'; @@ -27,12 +28,29 @@ import { useApmParams } from '../../../../hooks/use_apm_params'; import { isErrorMessage } from '../../correlations/utils/is_error_message'; const DEFAULT_PERCENTILE_THRESHOLD = 95; +// Enforce min height so it's consistent across all tabs on the same level +// to prevent "flickering" behavior +const MIN_TAB_TITLE_HEIGHT = 56; + +type Selection = [number, number]; + +// Format the selected latency range for the "Clear selection" badge. +// If the two values share the same unit, it will only displayed once. +// For example: 12 - 23 ms / 12 ms - 3 s +export function getFormattedSelection(selection: Selection): string { + const from = getDurationFormatter(selection[0])(selection[0]); + const to = getDurationFormatter(selection[1])(selection[1]); + + return `${from.unit === to.unit ? from.value : from.formatted} - ${ + to.formatted + }`; +} interface Props { markerCurrentTransaction?: number; onChartSelection: BrushEndListener; onClearSelection: () => void; - selection?: [number, number]; + selection?: Selection; } export function TransactionDistribution({ @@ -132,7 +150,7 @@ export function TransactionDistribution({ return (
- +
@@ -177,10 +195,9 @@ export function TransactionDistribution({ {i18n.translate( 'xpack.apm.transactionDetails.distribution.selectionText', { - defaultMessage: `Selection: {selectionFrom} - {selectionTo}ms`, + defaultMessage: `Selection: {formattedSelection}`, values: { - selectionFrom: Math.round(selection[0] / 1000), - selectionTo: Math.round(selection[1] / 1000), + formattedSelection: getFormattedSelection(selection), }, } )} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/failed_transactions_correlations_tab.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/failed_transactions_correlations_tab.tsx index 8743b8f3ea811..af66f818309a0 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/failed_transactions_correlations_tab.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/failed_transactions_correlations_tab.tsx @@ -46,7 +46,7 @@ function FailedTransactionsCorrelationsTab({ onFilter }: TabContentProps) { text={i18n.translate( 'xpack.apm.failedTransactionsCorrelations.licenseCheckText', { - defaultMessage: `To use the failed transactions correlations feature, you must be subscribed to an Elastic Platinum license.`, + defaultMessage: `To use the failed transaction correlations feature, you must be subscribed to an Elastic Platinum license. With it, you'll be able to discover which attributes are contributing to failed transactions.`, } )} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx index 3e8a8cc260a56..c511a708058d3 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx @@ -15,11 +15,12 @@ import { CurveType, LineAnnotation, LineAnnotationDatum, + LineAnnotationStyle, Position, RectAnnotation, ScaleType, Settings, - LineAnnotationStyle, + TickFormatter, } from '@elastic/charts'; import { euiPaletteColorBlind } from '@elastic/eui'; @@ -28,10 +29,7 @@ import { i18n } from '@kbn/i18n'; import { useChartTheme } from '../../../../../../observability/public'; -import { - getDurationUnitKey, - getUnitLabelAndConvertedValue, -} from '../../../../../common/utils/formatters'; +import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { HistogramItem } from '../../../../../common/search_strategies/correlations/types'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; @@ -39,7 +37,7 @@ import { useTheme } from '../../../../hooks/use_theme'; import { ChartContainer } from '../chart_container'; -interface CorrelationsChartProps { +interface TransactionDistributionChartProps { field?: string; value?: string; histogram?: HistogramItem[]; @@ -90,9 +88,15 @@ export const replaceHistogramDotsWithBars = ( } }; +// Create and call a duration formatter for every value since the durations for the +// x axis might have a wide range of values e.g. from low milliseconds to large seconds. +// This way we can get different suitable units across ticks. +const xAxisTickFormat: TickFormatter = (d) => + getDurationFormatter(d, 0.9999)(d).formatted; + export function TransactionDistributionChart({ - field, - value, + field: fieldName, + value: fieldValue, histogram: originalHistogram, markerCurrentTransaction, markerValue, @@ -100,7 +104,7 @@ export function TransactionDistributionChart({ overallHistogram, onChartSelection, selection, -}: CorrelationsChartProps) { +}: TransactionDistributionChartProps) { const chartTheme = useChartTheme(); const euiTheme = useTheme(); @@ -246,17 +250,7 @@ export function TransactionDistributionChart({ id="x-axis" title="" position={Position.Bottom} - tickFormat={(d) => { - const unit = getDurationUnitKey(d, 1); - const converted = getUnitLabelAndConvertedValue(unit, d); - const convertedValueParts = converted.convertedValue.split('.'); - const convertedValue = - convertedValueParts.length === 2 && - convertedValueParts[1] === '0' - ? convertedValueParts[0] - : converted.convertedValue; - return `${convertedValue}${converted.unitLabel}`; - }} + tickFormat={xAxisTickFormat} gridLine={{ visible: false }} /> {Array.isArray(histogram) && - field !== undefined && - value !== undefined && ( + fieldName !== undefined && + fieldValue !== undefined && ( >( path: TPath ): TypeOf; +export function useApmParams< + TPath1 extends PathsOf, + TPath2 extends PathsOf +>( + path1: TPath1, + path2: TPath2 +): TypeOf | TypeOf; + +export function useApmParams< + TPath1 extends PathsOf, + TPath2 extends PathsOf, + TPath3 extends PathsOf +>( + path1: TPath1, + path2: TPath2, + path3: TPath3 +): + | TypeOf + | TypeOf + | TypeOf; + export function useApmParams( - path: string, - optional?: true + ...args: any[] ): TypeOf> | undefined { - return useParams(path, optional); + return useParams(...args); } diff --git a/x-pack/plugins/apm/public/hooks/use_failed_transactions_correlations_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_failed_transactions_correlations_fetcher.ts index 3841419e860fc..add00968f0444 100644 --- a/x-pack/plugins/apm/public/hooks/use_failed_transactions_correlations_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_failed_transactions_correlations_fetcher.ts @@ -38,9 +38,7 @@ interface FailedTransactionsCorrelationsFetcherState { total: number; } -export const useFailedTransactionsCorrelationsFetcher = ( - params: Omit -) => { +export const useFailedTransactionsCorrelationsFetcher = () => { const { services: { data }, } = useKibana(); @@ -74,7 +72,7 @@ export const useFailedTransactionsCorrelationsFetcher = ( })); } - const startFetch = () => { + const startFetch = (params: SearchServiceParams) => { setFetchState((prevState) => ({ ...prevState, error: undefined, diff --git a/x-pack/plugins/apm/readme.md b/x-pack/plugins/apm/readme.md index 0ed0990f322a3..bf29ad1633315 100644 --- a/x-pack/plugins/apm/readme.md +++ b/x-pack/plugins/apm/readme.md @@ -141,7 +141,7 @@ node scripts/eslint.js x-pack/legacy/plugins/apm APM behaves differently depending on which the role and permissions a logged in user has. To create the users run: ```sh -node x-pack/plugins/apm/scripts/create-apm-users-and-roles.js --username elastic --password changeme --kibana-url http://localhost:5601 --role-suffix +node x-pack/plugins/apm/scripts/create-apm-users-and-roles.js --username admin --password changeme --kibana-url http://localhost:5601 --role-suffix ``` This will create: diff --git a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/async_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/async_search_service.ts index 9afe9d916b38e..89fcda926d547 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/async_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/async_search_service.ts @@ -15,6 +15,7 @@ import { fetchTransactionDurationFieldCandidates } from '../correlations/queries import type { SearchServiceFetchParams } from '../../../../common/search_strategies/correlations/types'; import { fetchFailedTransactionsCorrelationPValues } from './queries/query_failure_correlation'; import { ERROR_CORRELATION_THRESHOLD } from './constants'; +import { EVENT_OUTCOME } from '../../../../common/elasticsearch_fieldnames'; export const asyncErrorCorrelationSearchServiceProvider = ( esClient: ElasticsearchClient, @@ -35,10 +36,11 @@ export const asyncErrorCorrelationSearchServiceProvider = ( includeFrozen, }; - const { fieldCandidates } = await fetchTransactionDurationFieldCandidates( - esClient, - params - ); + const { + fieldCandidates: candidates, + } = await fetchTransactionDurationFieldCandidates(esClient, params); + + const fieldCandidates = candidates.filter((t) => !(t === EVENT_OUTCOME)); addLogMessage(`Identified ${fieldCandidates.length} fieldCandidates.`); diff --git a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/queries/query_failure_correlation.ts b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/queries/query_failure_correlation.ts index 22424d68f07ff..81fe6697d1fb1 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/queries/query_failure_correlation.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/queries/query_failure_correlation.ts @@ -75,14 +75,15 @@ export const fetchFailedTransactionsCorrelationPValues = async ( ); } - const result = (resp.body.aggregations - .failure_p_value as estypes.AggregationsMultiBucketAggregate<{ + const overallResult = resp.body.aggregations + .failure_p_value as estypes.AggregationsSignificantTermsAggregate<{ key: string; doc_count: number; bg_count: number; score: number; - }>).buckets.map((b) => { - const score = b.score; + }>; + const result = overallResult.buckets.map((bucket) => { + const score = bucket.score; // Scale the score into a value from 0 - 1 // using a concave piecewise linear function in -log(p-value) @@ -92,11 +93,17 @@ export const fetchFailedTransactionsCorrelationPValues = async ( 0.25 * Math.min(Math.max((score - 13.816) / 101.314, 0), 1); return { - ...b, + ...bucket, fieldName, - fieldValue: b.key, + fieldValue: bucket.key, pValue: Math.exp(-score), normalizedScore, + // Percentage of time the term appears in failed transactions + failurePercentage: bucket.doc_count / overallResult.doc_count, + // Percentage of time the term appears in successful transactions + successPercentage: + (bucket.bg_count - bucket.doc_count) / + (overallResult.bg_count - overallResult.doc_count), }; }); diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index 1b5df64dd8d00..ce8a8bcb8930d 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -64,8 +64,8 @@ Object { }, "body": Object { "_source": Array [ - "service.runtime.name", "agent.name", + "service.runtime.name", ], "query": Object { "bool": Object { @@ -84,17 +84,17 @@ Object { }, }, }, - Object { - "exists": Object { - "field": "service.runtime.name", - }, - }, Object { "exists": Object { "field": "agent.name", }, }, ], + "should": Object { + "exists": Object { + "field": "service.runtime.name", + }, + }, }, }, "size": 1, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent.ts index 2a6ec74bc0d1a..e99fd6b9ff278 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_agent.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_agent.ts @@ -16,14 +16,14 @@ import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; interface ServiceAgent { - service?: { - runtime: { - name: string; - }; - }; agent?: { name: string; }; + service?: { + runtime?: { + name?: string; + }; + }; } export async function getServiceAgent({ @@ -50,23 +50,23 @@ export async function getServiceAgent({ }, body: { size: 1, - _source: [SERVICE_RUNTIME_NAME, AGENT_NAME], + _source: [AGENT_NAME, SERVICE_RUNTIME_NAME], query: { bool: { filter: [ { term: { [SERVICE_NAME]: serviceName } }, ...rangeQuery(start, end), - { - exists: { - field: SERVICE_RUNTIME_NAME, - }, - }, { exists: { field: AGENT_NAME, }, }, ], + should: { + exists: { + field: SERVICE_RUNTIME_NAME, + }, + }, }, }, }, @@ -80,6 +80,6 @@ export async function getServiceAgent({ return {}; } - const { service, agent } = response.hits.hits[0]._source as ServiceAgent; - return { agentName: agent?.name, runtimeName: service?.runtime.name }; + const { agent, service } = response.hits.hits[0]._source as ServiceAgent; + return { agentName: agent?.name, runtimeName: service?.runtime?.name }; } diff --git a/x-pack/plugins/apm/typings/common.d.ts b/x-pack/plugins/apm/typings/common.d.ts index ea4bafad84619..b94eb6cd97b06 100644 --- a/x-pack/plugins/apm/typings/common.d.ts +++ b/x-pack/plugins/apm/typings/common.d.ts @@ -10,6 +10,7 @@ import '../../../typings/rison_node'; import '../../infra/types/eui'; // EUIBasicTable import '../../reporting/public/components/report_listing'; +import '../../reporting/server/lib/puid'; import './apm_rum_react'; // Allow unknown properties in an object diff --git a/x-pack/plugins/canvas/public/components/expression/expression.scss b/x-pack/plugins/canvas/public/components/expression/expression.scss index da95eca2b4f61..c649742077f2b 100644 --- a/x-pack/plugins/canvas/public/components/expression/expression.scss +++ b/x-pack/plugins/canvas/public/components/expression/expression.scss @@ -31,6 +31,15 @@ flex-direction: column; } + .canvasExpressionInput__editor { + height: auto; + position: absolute; + top: 0; + left: 0; + bottom: $euiSizeS * 7 + 1; + right: 0; + } + .canvasExpressionInput__inner { flex-grow: 1; display: flex; diff --git a/x-pack/plugins/canvas/public/components/expression/expression.tsx b/x-pack/plugins/canvas/public/components/expression/expression.tsx index ff3fed32c0ac0..a3ba18d541d76 100644 --- a/x-pack/plugins/canvas/public/components/expression/expression.tsx +++ b/x-pack/plugins/canvas/public/components/expression/expression.tsx @@ -171,7 +171,7 @@ export const Expression: FC = ({ - + {isCompact ? strings.getMaximizeButtonLabel() : strings.getMinimizeButtonLabel()} diff --git a/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot b/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot index 1b60db12f0311..7a35691ca1c42 100644 --- a/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/text_style_picker/__stories__/__snapshots__/text_style_picker.stories.storyshot @@ -334,7 +334,6 @@ exports[`Storyshots components/TextStylePicker default 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isSelected euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -371,7 +370,6 @@ exports[`Storyshots components/TextStylePicker default 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -408,7 +406,6 @@ exports[`Storyshots components/TextStylePicker default 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -783,7 +780,6 @@ exports[`Storyshots components/TextStylePicker interactive 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isSelected euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -820,7 +816,6 @@ exports[`Storyshots components/TextStylePicker interactive 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -857,7 +852,6 @@ exports[`Storyshots components/TextStylePicker interactive 1`] = ` className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isIconOnly" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, diff --git a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot index 056b98012f342..28d2d9b5a3718 100644 --- a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot @@ -164,7 +164,6 @@ Array [ className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small euiButtonGroupButton-isSelected" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, @@ -195,7 +194,6 @@ Array [ className="euiButtonGroupButton euiButtonGroupButton--text euiButtonGroupButton--small" disabled={false} htmlFor="generated-id" - onClick={[Function]} style={ Object { "minWidth": undefined, diff --git a/x-pack/plugins/cases/public/components/case_view/index.tsx b/x-pack/plugins/cases/public/components/case_view/index.tsx index b333d908fa77c..a44c2cb22010e 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.tsx @@ -105,7 +105,6 @@ export const CaseComponent = React.memo( const [initLoadingData, setInitLoadingData] = useState(true); const init = useRef(true); const timelineUi = useTimelineContext()?.ui; - const alertConsumers = useTimelineContext()?.alertConsumers; const { caseUserActions, @@ -487,9 +486,7 @@ export const CaseComponent = React.memo( - {timelineUi?.renderTimelineDetailsPanel - ? timelineUi.renderTimelineDetailsPanel({ alertConsumers }) - : null} + {timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null} ); } diff --git a/x-pack/plugins/cases/public/components/timeline_context/index.tsx b/x-pack/plugins/cases/public/components/timeline_context/index.tsx index 76952e638e198..727e4b64628d1 100644 --- a/x-pack/plugins/cases/public/components/timeline_context/index.tsx +++ b/x-pack/plugins/cases/public/components/timeline_context/index.tsx @@ -7,7 +7,6 @@ import React, { useState } from 'react'; import { EuiMarkdownEditorUiPlugin, EuiMarkdownAstNodePosition } from '@elastic/eui'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { Plugin } from 'unified'; /** * @description - manage the plugins, hooks, and ui components needed to enable timeline functionality within the cases plugin @@ -29,7 +28,6 @@ interface TimelineProcessingPluginRendererProps { } export interface CasesTimelineIntegration { - alertConsumers?: AlertConsumers[]; editor_plugins: { parsingPlugin: Plugin; processingPluginRenderer: React.FC< @@ -45,11 +43,7 @@ export interface CasesTimelineIntegration { }; ui?: { renderInvestigateInTimelineActionComponent?: (alertIds: string[]) => JSX.Element; - renderTimelineDetailsPanel?: ({ - alertConsumers, - }: { - alertConsumers?: AlertConsumers[]; - }) => JSX.Element; + renderTimelineDetailsPanel?: () => JSX.Element; }; } 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 bf7eeda7e0e2e..aec188037f095 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -29,7 +29,7 @@ import { SavedObjectsUpdateResponse, } from 'kibana/server'; import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { getNoneCaseConnector, CONNECTOR_ID_REFERENCE_NAME } from '../../common'; import { CasesService } from '.'; import { diff --git a/x-pack/plugins/cases/server/services/configure/index.test.ts b/x-pack/plugins/cases/server/services/configure/index.test.ts index 199b541d49f98..045142ea13e11 100644 --- a/x-pack/plugins/cases/server/services/configure/index.test.ts +++ b/x-pack/plugins/cases/server/services/configure/index.test.ts @@ -23,7 +23,7 @@ import { SavedObjectsUpdateResponse, } from 'kibana/server'; import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { CaseConfigureService } from '.'; import { ESCasesConfigureAttributes } from './types'; import { getNoneCaseConnector, CONNECTOR_ID_REFERENCE_NAME } from '../../common'; diff --git a/x-pack/plugins/cloud/README.md b/x-pack/plugins/cloud/README.md index 3fe0b3c8b8415..0ffecb6ca8829 100644 --- a/x-pack/plugins/cloud/README.md +++ b/x-pack/plugins/cloud/README.md @@ -1,11 +1,47 @@ # `cloud` plugin -The `cloud` plugin adds cloud specific features to Kibana. -The client-side plugin configures following values: -- `isCloudEnabled = true` for both ESS and ECE deployments -- `cloudId` is the ID of the Cloud deployment Kibana is running on -- `baseUrl` is the URL of the Cloud interface, for Elastic Cloud production environment the value is `https://cloud.elastic.co` -- `deploymentUrl` is the URL of the specific Cloud deployment Kibana is running on, the value is already concatenated with `baseUrl` -- `profileUrl` is the URL of the Cloud user profile page, the value is already concatenated with `baseUrl` -- `organizationUrl` is the URL of the Cloud account (& billing) page, the value is already concatenated with `baseUrl` -- `cname` value is the same as `baseUrl` on ESS but can be customized on ECE +The `cloud` plugin adds Cloud-specific features to Kibana. + +## Client-side API + +The client-side plugin provides the following interface. + +### `isCloudEnabled` + +This is set to `true` for both ESS and ECE deployments. + +### `cloudId` + +This is the ID of the Cloud deployment to which the Kibana instance belongs. + +**Example:** `eastus2.azure.elastic-cloud.com:9243$59ef636c6917463db140321484d63cfa$a8b109c08adc43279ef48f29af1a3911` + +### `baseUrl` + +This is the URL of the Cloud interface. + +**Example:** `https://cloud.elastic.co` (on the ESS production environment) + +### `deploymentUrl` + +This is the path to the Cloud deployment management page for the deployment to which the Kibana instance belongs. The value is already prepended with `baseUrl`. + +**Example:** `{baseUrl}/deployments/bfdad4ef99a24212a06d387593686d63` + +### `profileUrl` + +This is the path to the Cloud User Profile page. The value is already prepended with `baseUrl`. + +**Example:** `{baseUrl}/user/settings/` + +### `organizationUrl` + +This is the path to the Cloud Account and Billing page. The value is already prepended with `baseUrl`. + +**Example:** `{baseUrl}/account/` + +### `cname` + +This value is the same as `baseUrl` on ESS but can be customized on ECE. + +**Example:** `cloud.elastic.co` (on ESS) \ No newline at end of file diff --git a/x-pack/plugins/dashboard_mode/README.md b/x-pack/plugins/dashboard_mode/README.md deleted file mode 100644 index 4e244afb97fdf..0000000000000 --- a/x-pack/plugins/dashboard_mode/README.md +++ /dev/null @@ -1 +0,0 @@ -The deprecated dashboard only mode. \ No newline at end of file diff --git a/x-pack/plugins/dashboard_mode/jest.config.js b/x-pack/plugins/dashboard_mode/jest.config.js deleted file mode 100644 index 7d0585938d166..0000000000000 --- a/x-pack/plugins/dashboard_mode/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/x-pack/plugins/dashboard_mode'], -}; diff --git a/x-pack/plugins/dashboard_mode/kibana.json b/x-pack/plugins/dashboard_mode/kibana.json deleted file mode 100644 index 6fc59ce9a7fa1..0000000000000 --- a/x-pack/plugins/dashboard_mode/kibana.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "dashboardMode", - "owner": { - "name": "Kibana Presentation", - "githubTeam": "kibana-presentation" - }, - "version": "8.0.0", - "kibanaVersion": "kibana", - "configPath": ["xpack", "dashboard_mode"], - "optionalPlugins": ["security"], - "requiredPlugins": ["kibanaLegacy", "urlForwarding", "dashboard"], - "server": true, - "ui": true -} diff --git a/x-pack/plugins/dashboard_mode/public/plugin.ts b/x-pack/plugins/dashboard_mode/public/plugin.ts deleted file mode 100644 index efad46ab47fe7..0000000000000 --- a/x-pack/plugins/dashboard_mode/public/plugin.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { trimStart } from 'lodash'; -import { CoreSetup } from 'kibana/public'; -import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; -import { UrlForwardingStart } from '../../../../src/plugins/url_forwarding/public'; -import { - createDashboardEditUrl, - DashboardConstants, -} from '../../../../src/plugins/dashboard/public'; -import { AppNavLinkStatus } from '../../../../src/core/public'; - -function defaultUrl(defaultAppId: string) { - const isDashboardId = defaultAppId.startsWith(dashboardAppIdPrefix()); - return isDashboardId ? `/${defaultAppId}` : DashboardConstants.LANDING_PAGE_PATH; -} - -function dashboardAppIdPrefix() { - return trimStart(createDashboardEditUrl(''), '/'); -} - -function migratePath( - currentHash: string, - kibanaLegacy: KibanaLegacyStart, - urlForwarding: UrlForwardingStart -) { - if (currentHash === '' || currentHash === '#' || currentHash === '#/') { - return `#${defaultUrl(kibanaLegacy.config.defaultAppId || '')}`; - } - if (!currentHash.startsWith('#/dashboard')) { - return currentHash; - } - - const forwards = urlForwarding.getForwards(); - - if (currentHash.startsWith('#/dashboards')) { - const { rewritePath: migrateListingPath } = forwards.find( - ({ legacyAppId }) => legacyAppId === 'dashboards' - )!; - return migrateListingPath(currentHash); - } - - const { rewritePath: migrateDetailPath } = forwards.find( - ({ legacyAppId }) => legacyAppId === 'dashboard' - )!; - return migrateDetailPath(currentHash); -} - -export const plugin = () => ({ - setup(core: CoreSetup<{ kibanaLegacy: KibanaLegacyStart; urlForwarding: UrlForwardingStart }>) { - core.application.register({ - id: 'dashboard_mode', - title: 'Dashboard mode', - navLinkStatus: AppNavLinkStatus.hidden, - mount: async () => { - const [coreStart, { kibanaLegacy, urlForwarding }] = await core.getStartServices(); - kibanaLegacy.dashboardConfig.turnHideWriteControlsOn(); - coreStart.chrome.navLinks.showOnly('dashboards'); - setTimeout(() => { - coreStart.application.navigateToApp('dashboards', { - path: migratePath(window.location.hash, kibanaLegacy, urlForwarding), - }); - }, 0); - return () => {}; - }, - }); - }, - start() {}, -}); diff --git a/x-pack/plugins/dashboard_mode/server/index.ts b/x-pack/plugins/dashboard_mode/server/index.ts deleted file mode 100644 index 8be8ec09a2898..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server'; -import { schema } from '@kbn/config-schema'; - -import { DashboardModeServerPlugin } from './plugin'; - -export const config: PluginConfigDescriptor = { - schema: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), -}; - -export function plugin(initializerContext: PluginInitializerContext) { - return new DashboardModeServerPlugin(initializerContext); -} - -export { DashboardModeServerPlugin as Plugin }; diff --git a/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.test.ts b/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.test.ts deleted file mode 100644 index e2e2c8c005670..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { parse as parseUrl } from 'url'; -import { - OnPostAuthHandler, - OnPostAuthToolkit, - KibanaRequest, - LifecycleResponseFactory, - IUiSettingsClient, -} from 'kibana/server'; -import { coreMock } from '../../../../../src/core/server/mocks'; - -import { AuthenticatedUser } from '../../../security/server'; -import { securityMock } from '../../../security/server/mocks'; - -import { setupDashboardModeRequestInterceptor } from './dashboard_mode_request_interceptor'; - -const DASHBOARD_ONLY_MODE_ROLE = 'test_dashboard_only_mode_role'; - -describe('DashboardOnlyModeRequestInterceptor', () => { - const core = coreMock.createSetup(); - const security = securityMock.createSetup(); - - let interceptor: OnPostAuthHandler; - let toolkit: OnPostAuthToolkit; - let uiSettingsMock: any; - - beforeEach(() => { - toolkit = { - next: jest.fn(), - }; - interceptor = setupDashboardModeRequestInterceptor({ - http: core.http, - security, - getUiSettingsClient: () => - (Promise.resolve({ - get: () => Promise.resolve(uiSettingsMock), - }) as unknown) as Promise, - }); - }); - - test('should not redirects for not app/* requests', async () => { - const request = ({ - url: { - pathname: 'api/test', - }, - } as unknown) as KibanaRequest; - - interceptor(request, {} as LifecycleResponseFactory, toolkit); - - expect(toolkit.next).toHaveBeenCalled(); - }); - - test('should not redirects not authenticated users', async () => { - const request = ({ - url: { - pathname: '/app/home', - }, - } as unknown) as KibanaRequest; - - interceptor(request, {} as LifecycleResponseFactory, toolkit); - - expect(toolkit.next).toHaveBeenCalled(); - }); - - describe('request for dashboard-only user', () => { - function testRedirectToDashboardModeApp(url: string) { - describe(`requests to url:"${url}"`, () => { - test('redirects to the dashboard_mode app instead', async () => { - const { pathname, search, hash } = parseUrl(url); - const request = ({ - url: { pathname, search, hash }, - credentials: { - roles: [DASHBOARD_ONLY_MODE_ROLE], - }, - } as unknown) as KibanaRequest; - - const response = ({ - redirected: jest.fn(), - } as unknown) as LifecycleResponseFactory; - - security.authc.getCurrentUser = jest.fn( - (r: KibanaRequest) => - (({ - roles: [DASHBOARD_ONLY_MODE_ROLE], - } as unknown) as AuthenticatedUser) - ); - - uiSettingsMock = [DASHBOARD_ONLY_MODE_ROLE]; - - await interceptor(request, response, toolkit); - - expect(response.redirected).toHaveBeenCalledWith({ - headers: { location: `/mock-server-basepath/app/dashboard_mode` }, - }); - }); - }); - } - - testRedirectToDashboardModeApp('/app/kibana'); - testRedirectToDashboardModeApp('/app/kibana#/foo/bar'); - testRedirectToDashboardModeApp('/app/kibana/foo/bar'); - testRedirectToDashboardModeApp('/app/kibana?foo=bar'); - testRedirectToDashboardModeApp('/app/dashboards?foo=bar'); - testRedirectToDashboardModeApp('/app/home?foo=bar'); - }); -}); diff --git a/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.ts b/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.ts deleted file mode 100644 index c177d24de0009..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/interceptors/dashboard_mode_request_interceptor.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { HttpServiceSetup, OnPostAuthHandler, IUiSettingsClient } from 'kibana/server'; -import { SecurityPluginSetup } from '../../../security/server'; -import { UI_SETTINGS } from '../../common'; - -const superuserRole = 'superuser'; - -interface DashboardModeRequestInterceptorDependencies { - http: HttpServiceSetup; - security: SecurityPluginSetup; - getUiSettingsClient: () => Promise; -} - -export const setupDashboardModeRequestInterceptor = ({ - http, - security, - getUiSettingsClient, -}: DashboardModeRequestInterceptorDependencies) => - (async (request, response, toolkit) => { - const path = request.url.pathname; - const isAppRequest = path.startsWith('/app/'); - - if (!isAppRequest) { - return toolkit.next(); - } - - const authenticatedUser = security.authc.getCurrentUser(request); - const roles = authenticatedUser?.roles || []; - - if (!authenticatedUser || roles.length === 0) { - return toolkit.next(); - } - - const uiSettings = await getUiSettingsClient(); - const dashboardOnlyModeRoles = await uiSettings.get( - UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES - ); - - if (!dashboardOnlyModeRoles) { - return toolkit.next(); - } - - const isDashboardOnlyModeUser = roles.find((role) => dashboardOnlyModeRoles.includes(role)); - const isSuperUser = roles.find((role) => role === superuserRole); - - const enforceDashboardOnlyMode = isDashboardOnlyModeUser && !isSuperUser; - - if (enforceDashboardOnlyMode) { - if ( - path.startsWith('/app/home') || - path.startsWith('/app/kibana') || - path.startsWith('/app/dashboards') - ) { - const dashBoardModeUrl = `${http.basePath.get(request)}/app/dashboard_mode`; - // If the user is in "Dashboard only mode" they should only be allowed to see - // the dashboard app and none others. - - return response.redirected({ - headers: { - location: dashBoardModeUrl, - }, - }); - } - - if (path.startsWith('/app/dashboard_mode')) { - // let through requests to the dashboard_mode app - return toolkit.next(); - } - - return response.notFound(); - } - - return toolkit.next(); - }) as OnPostAuthHandler; diff --git a/x-pack/plugins/dashboard_mode/server/interceptors/index.ts b/x-pack/plugins/dashboard_mode/server/interceptors/index.ts deleted file mode 100644 index 5736fb5c86e22..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/interceptors/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { setupDashboardModeRequestInterceptor } from './dashboard_mode_request_interceptor'; diff --git a/x-pack/plugins/dashboard_mode/server/plugin.ts b/x-pack/plugins/dashboard_mode/server/plugin.ts deleted file mode 100644 index 2c526ba03ef56..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/plugin.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - PluginInitializerContext, - CoreSetup, - CoreStart, - Plugin, - SavedObjectsClient, - Logger, -} from '../../../../src/core/server'; - -import { SecurityPluginSetup } from '../../security/server'; -import { setupDashboardModeRequestInterceptor } from './interceptors'; - -import { getUiSettings } from './ui_settings'; - -interface DashboardModeServerSetupDependencies { - security?: SecurityPluginSetup; -} - -export class DashboardModeServerPlugin implements Plugin { - private initializerContext: PluginInitializerContext; - private logger?: Logger; - - constructor(initializerContext: PluginInitializerContext) { - this.initializerContext = initializerContext; - } - - public setup(core: CoreSetup, { security }: DashboardModeServerSetupDependencies) { - this.logger = this.initializerContext.logger.get(); - - core.uiSettings.register(getUiSettings()); - - const getUiSettingsClient = async () => { - const [coreStart] = await core.getStartServices(); - const { savedObjects, uiSettings } = coreStart; - const savedObjectsClient = new SavedObjectsClient(savedObjects.createInternalRepository()); - - return uiSettings.asScopedToClient(savedObjectsClient); - }; - - if (security) { - const dashboardModeRequestInterceptor = setupDashboardModeRequestInterceptor({ - http: core.http, - security, - getUiSettingsClient, - }); - - core.http.registerOnPostAuth(dashboardModeRequestInterceptor); - - this.logger.debug(`registered DashboardModeRequestInterceptor`); - } - } - - public start(core: CoreStart) {} - - public stop() {} -} diff --git a/x-pack/plugins/dashboard_mode/server/ui_settings.ts b/x-pack/plugins/dashboard_mode/server/ui_settings.ts deleted file mode 100644 index a0678b7b27bdd..0000000000000 --- a/x-pack/plugins/dashboard_mode/server/ui_settings.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; -import { UiSettingsParams } from 'kibana/server'; -import { UI_SETTINGS } from '../common'; - -const DASHBOARD_ONLY_USER_ROLE = 'kibana_dashboard_only_user'; - -export function getUiSettings(): Record> { - return { - [UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES]: { - name: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle', { - defaultMessage: 'Dashboards only roles', - }), - description: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription', { - defaultMessage: 'Roles that belong to View Dashboards Only mode', - }), - value: [DASHBOARD_ONLY_USER_ROLE], - category: ['dashboard'], - sensitive: true, - deprecation: { - message: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation', { - defaultMessage: 'This setting is deprecated and will be removed in Kibana 8.0.', - }), - docLinksKey: 'dashboardSettings', - }, - schema: schema.arrayOf(schema.string()), - }, - }; -} diff --git a/x-pack/plugins/dashboard_mode/tsconfig.json b/x-pack/plugins/dashboard_mode/tsconfig.json deleted file mode 100644 index 8094e70e96b60..0000000000000 --- a/x-pack/plugins/dashboard_mode/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true, - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "../../../typings/**/*" - ], - "references": [ - { "path": "../../../src/core/tsconfig.json" }, - { "path": "../../../src/plugins/dashboard/tsconfig.json" }, - { "path": "../../../src/plugins/kibana_legacy/tsconfig.json" }, - { "path": "../../../src/plugins/url_forwarding/tsconfig.json" }, - { "path": "../security/tsconfig.json" } - ] -} diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/abstract_explore_data_action.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/abstract_explore_data_action.ts index 44ea53fe0b870..c1c84c040f51e 100644 --- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/abstract_explore_data_action.ts +++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/abstract_explore_data_action.ts @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { DiscoverStart } from '../../../../../../src/plugins/discover/public'; import { ViewMode, IEmbeddable } from '../../../../../../src/plugins/embeddable/public'; import { StartServicesGetter } from '../../../../../../src/plugins/kibana_utils/public'; -import { KibanaLegacyStart } from '../../../../../../src/plugins/kibana_legacy/public'; import { CoreStart } from '../../../../../../src/core/public'; import { KibanaLocation } from '../../../../../../src/plugins/share/public'; import * as shared from './shared'; @@ -18,11 +17,6 @@ export const ACTION_EXPLORE_DATA = 'ACTION_EXPLORE_DATA'; export interface PluginDeps { discover: Pick; - kibanaLegacy?: { - dashboardConfig: { - getHideWriteControls: KibanaLegacyStart['dashboardConfig']['getHideWriteControls']; - }; - }; } export interface CoreDeps { @@ -53,11 +47,6 @@ export abstract class AbstractExploreDataAction { const core = coreMock.createStart(); @@ -65,11 +63,6 @@ const setup = ( discover: { locator, }, - kibanaLegacy: { - dashboardConfig: { - getHideWriteControls: () => dashboardOnlyMode, - }, - }, }; const params: Params = { @@ -193,13 +186,6 @@ describe('"Explore underlying data" panel action', () => { expect(isCompatible).toBe(false); }); - test('return false for dashboard_only mode', async () => { - const { action, context } = setup({ dashboardOnlyMode: true }); - const isCompatible = await action.isCompatible(context); - - expect(isCompatible).toBe(false); - }); - test('returns false if Discover app is disabled', async () => { const { action, context, core } = setup(); diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts index e0a8cf20ee943..334898ada6a3e 100644 --- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts +++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts @@ -28,7 +28,7 @@ afterEach(() => { i18nTranslateSpy.mockClear(); }); -const setup = ({ dashboardOnlyMode = false }: { dashboardOnlyMode?: boolean } = {}) => { +const setup = () => { const core = coreMock.createStart(); const locator: DiscoverAppLocator = { getLocation: jest.fn(() => @@ -51,11 +51,6 @@ const setup = ({ dashboardOnlyMode = false }: { dashboardOnlyMode?: boolean } = discover: { locator, }, - kibanaLegacy: { - dashboardConfig: { - getHideWriteControls: () => dashboardOnlyMode, - }, - }, }; const params: Params = { @@ -177,13 +172,6 @@ describe('"Explore underlying data" panel action', () => { expect(isCompatible).toBe(false); }); - test('return false for dashboard_only mode', async () => { - const { action, context } = setup({ dashboardOnlyMode: true }); - const isCompatible = await action.isCompatible(context); - - expect(isCompatible).toBe(false); - }); - test('returns false if Discover app is disabled', async () => { const { action, context, core } = setup(); diff --git a/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.test.tsx b/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.test.tsx index bd475acbb4feb..01ec166b74afc 100644 --- a/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.test.tsx +++ b/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.test.tsx @@ -12,13 +12,17 @@ import { createFleetTestRendererMock } from '../../mock'; import { HostsInput } from './hosts_input'; -function renderInput(value = ['http://host1.com']) { +function renderInput( + value = ['http://host1.com'], + errors: Array<{ message: string; index?: number }> = [], + mockOnChange: (...args: any[]) => void = jest.fn() +) { const renderer = createFleetTestRendererMock(); - const mockOnChange = jest.fn(); const utils = renderer.render( { fireEvent.change(inputEl, { target: { value: 'http://newhost.com' } }); expect(mockOnChange).toHaveBeenCalledWith(['http://newhost.com']); }); + +test('Should display single indexed error message', async () => { + const { utils } = renderInput(['bad host'], [{ message: 'Invalid URL', index: 0 }]); + const inputEl = await utils.findByText('Invalid URL'); + expect(inputEl).toBeDefined(); +}); + +test('Should display errors in order', async () => { + const { utils } = renderInput( + ['bad host 1', 'bad host 2', 'bad host 3'], + [ + { message: 'Error 1', index: 0 }, + { message: 'Error 2', index: 1 }, + { message: 'Error 3', index: 2 }, + ] + ); + await act(async () => { + const errors = await utils.queryAllByText(/Error [1-3]/); + expect(errors[0]).toHaveTextContent('Error 1'); + expect(errors[1]).toHaveTextContent('Error 2'); + expect(errors[2]).toHaveTextContent('Error 3'); + }); +}); + +test('Should remove error when item deleted', async () => { + const mockOnChange = jest.fn(); + const errors = [ + { message: 'Error 1', index: 0 }, + { message: 'Error 2', index: 1 }, + { message: 'Error 3', index: 2 }, + ]; + + const { utils } = renderInput(['bad host 1', 'bad host 2', 'bad host 3'], errors, mockOnChange); + + mockOnChange.mockImplementation((newValue) => { + utils.rerender( + + ); + }); + + await act(async () => { + const deleteRowButtons = await utils.container.querySelectorAll('[aria-label="Delete host"]'); + if (deleteRowButtons.length !== 3) { + throw new Error('Delete host buttons not found'); + } + + fireEvent.click(deleteRowButtons[1]); + expect(mockOnChange).toHaveBeenCalled(); + + const renderedErrors = await utils.queryAllByText(/Error [1-3]/); + expect(renderedErrors).toHaveLength(2); + expect(renderedErrors[0]).toHaveTextContent('Error 1'); + expect(renderedErrors[1]).toHaveTextContent('Error 3'); + }); +}); diff --git a/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.tsx b/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.tsx index c99cad93b7ec6..49cff905d167f 100644 --- a/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.tsx +++ b/x-pack/plugins/fleet/public/components/settings_flyout/hosts_input.tsx @@ -158,11 +158,31 @@ export const HostsInput: FunctionComponent = ({ [value, onChange] ); + const indexedErrors = useMemo(() => { + if (!errors) { + return []; + } + return errors.reduce((acc, err) => { + if (err.index === undefined) { + return acc; + } + + if (!acc[err.index]) { + acc[err.index] = []; + } + + acc[err.index].push(err.message); + + return acc; + }, []); + }, [errors]); + const onDelete = useCallback( (idx: number) => { + indexedErrors.splice(idx, 1); onChange([...value.slice(0, idx), ...value.slice(idx + 1)]); }, - [value, onChange] + [value, onChange, indexedErrors] ); const addRowHandler = useCallback(() => { @@ -174,36 +194,19 @@ export const HostsInput: FunctionComponent = ({ ({ source, destination }) => { if (source && destination) { const items = euiDragDropReorder(value, source.index, destination.index); - + const sourceErrors = indexedErrors[source.index]; + indexedErrors.splice(source.index, 1); + indexedErrors.splice(destination.index, 0, sourceErrors); onChange(items); } }, - [value, onChange] + [value, onChange, indexedErrors] ); const globalErrors = useMemo(() => { return errors && errors.filter((err) => err.index === undefined).map(({ message }) => message); }, [errors]); - const indexedErrors = useMemo(() => { - if (!errors) { - return []; - } - return errors.reduce((acc, err) => { - if (err.index === undefined) { - return acc; - } - - if (!acc[err.index]) { - acc[err.index] = []; - } - - acc[err.index].push(err.message); - - return acc; - }, [] as string[][]); - }, [errors]); - const isSortable = rows.length > 1; return ( diff --git a/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx b/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx index 3d3a4dda60632..e42733bbd2da0 100644 --- a/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx @@ -58,9 +58,11 @@ const CodeEditorPlaceholder = styled(EuiTextColor).attrs((props) => ({ }))` position: absolute; top: 0; - right: 0; + left: 0; // Matches monaco editor font-family: Menlo, Monaco, 'Courier New', monospace; + font-size: 12px; + line-height: 21px; pointer-events: none; `; @@ -102,6 +104,7 @@ function useSettingsForm(outputId: string | undefined, onSuccess: () => void) { } const res: Array<{ message: string; index: number }> = []; + const hostIndexes: { [key: string]: number[] } = {}; value.forEach((val, idx) => { if (!val.match(URL_REGEX)) { res.push({ @@ -111,7 +114,23 @@ function useSettingsForm(outputId: string | undefined, onSuccess: () => void) { index: idx, }); } + const curIndexes = hostIndexes[val] || []; + hostIndexes[val] = [...curIndexes, idx]; }); + + Object.values(hostIndexes) + .filter(({ length }) => length > 1) + .forEach((indexes) => { + indexes.forEach((index) => + res.push({ + message: i18n.translate('xpack.fleet.settings.fleetServerHostsDuplicateError', { + defaultMessage: 'Duplicate URL', + }), + index, + }) + ); + }); + if (res.length) { return res; } @@ -132,6 +151,7 @@ function useSettingsForm(outputId: string | undefined, onSuccess: () => void) { const elasticsearchUrlInput = useComboInput('esHostsComboxBox', [], (value) => { const res: Array<{ message: string; index: number }> = []; + const urlIndexes: { [key: string]: number[] } = {}; value.forEach((val, idx) => { if (!val.match(URL_REGEX)) { res.push({ @@ -141,7 +161,23 @@ function useSettingsForm(outputId: string | undefined, onSuccess: () => void) { index: idx, }); } + const curIndexes = urlIndexes[val] || []; + urlIndexes[val] = [...curIndexes, idx]; }); + + Object.values(urlIndexes) + .filter(({ length }) => length > 1) + .forEach((indexes) => { + indexes.forEach((index) => + res.push({ + message: i18n.translate('xpack.fleet.settings.elasticHostDuplicateError', { + defaultMessage: 'Duplicate URL', + }), + index, + }) + ); + }); + if (res.length) { return res; } @@ -162,11 +198,11 @@ function useSettingsForm(outputId: string | undefined, onSuccess: () => void) { }); const validate = useCallback(() => { - if ( - !fleetServerHostsInput.validate() || - !elasticsearchUrlInput.validate() || - !additionalYamlConfigInput.validate() - ) { + const fleetServerHostsValid = fleetServerHostsInput.validate(); + const elasticsearchUrlsValid = elasticsearchUrlInput.validate(); + const additionalYamlConfigValid = additionalYamlConfigInput.validate(); + + if (!fleetServerHostsValid || !elasticsearchUrlsValid || !additionalYamlConfigValid) { return false; } diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts index 86bf36984b9fd..e49a1c5c94f90 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts @@ -136,4 +136,44 @@ describe(' request flyout', () => { expect(json).toBe(expected); }); + + test('renders _meta field', async () => { + const defaultPolicy = getDefaultHotPhasePolicy(); + const policyWithMetaField = { + ...defaultPolicy, + policy: { + ...defaultPolicy.policy, + _meta: { + description: 'test meta description', + someObject: { + test: 'test', + }, + }, + }, + }; + httpRequestsMockHelpers.setLoadPolicies([policyWithMetaField]); + + await act(async () => { + testBed = await setupRequestFlyoutTestBed(); + }); + + const { component, actions } = testBed; + component.update(); + + await actions.openRequestFlyout(); + + const json = actions.getRequestJson(); + const expected = `PUT _ilm/policy/${policyWithMetaField.name}\n${JSON.stringify( + { + policy: { + phases: { ...policyWithMetaField.policy.phases }, + _meta: { ...policyWithMetaField.policy._meta }, + }, + }, + null, + 2 + )}`; + + expect(json).toBe(expected); + }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts index a0bed0e1644e6..ce5b8b234e088 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts @@ -14,15 +14,21 @@ const toggleDeletePhase = async (testBed: TestBed) => { const { find, component } = testBed; let button = find('disableDeletePhaseButton'); + let action = 'disable'; if (!button.length) { button = find('enableDeletePhaseButton'); + action = 'enable'; } if (!button.length) { throw new Error(`Button to enable/disable delete phase was not found.`); } await act(async () => { - button.simulate('click'); + if (action === 'disable') { + button.simulate('click'); + } else { + button.find('input').simulate('change'); + } }); component.update(); }; diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index 76b38eacba2d1..3a338c80fa56c 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -18,6 +18,7 @@ export type PhaseExceptDelete = keyof Omit; export interface SerializedPolicy { name: string; phases: Phases; + _meta?: Record; } export interface Phases { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx index f510090323e1f..ae7b1ebaffc02 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx @@ -29,6 +29,7 @@ import { useFormContext, useFormData } from '../../../../shared_imports'; import { i18nTexts } from '../i18n_texts'; import { FormInternal } from '../types'; +type PolicyJson = Omit; interface Props { close: () => void; policyName: string; @@ -37,48 +38,50 @@ interface Props { /** * Ensure that the JSON we get from the from has phases in the correct order. */ -const prettifyFormJson = (policy: SerializedPolicy): SerializedPolicy => ({ - ...policy, - phases: { - hot: policy.phases.hot, - warm: policy.phases.warm, - cold: policy.phases.cold, - frozen: policy.phases.frozen, - delete: policy.phases.delete, - }, -}); +const prettifyFormJson = (policy: SerializedPolicy): PolicyJson => { + return { + phases: { + hot: policy.phases.hot, + warm: policy.phases.warm, + cold: policy.phases.cold, + frozen: policy.phases.frozen, + delete: policy.phases.delete, + }, + _meta: policy._meta, + }; +}; export const PolicyJsonFlyout: React.FunctionComponent = ({ policyName, close }) => { /** * policy === undefined: we are checking validity * policy === null: we have determined the policy is invalid - * policy === {@link SerializedPolicy} we have determined the policy is valid + * policy === {@link PolicyJson} we have determined the policy is valid */ - const [policy, setPolicy] = useState(undefined); + const [policyJson, setPolicyJson] = useState(undefined); const { validate: validateForm, getErrors } = useFormContext(); const [, getFormData] = useFormData(); const updatePolicy = useCallback(async () => { - setPolicy(undefined); + setPolicyJson(undefined); const isFormValid = await validateForm(); const errorMessages = getErrors(); const isOnlyMissingPolicyName = errorMessages.length === 1 && errorMessages[0] === i18nTexts.editPolicy.errors.policyNameRequiredMessage; if (isFormValid || isOnlyMissingPolicyName) { - setPolicy(prettifyFormJson(getFormData())); + setPolicyJson(prettifyFormJson(getFormData())); } else { - setPolicy(null); + setPolicyJson(null); } - }, [setPolicy, getFormData, validateForm, getErrors]); + }, [setPolicyJson, getFormData, validateForm, getErrors]); useEffect(() => { updatePolicy(); }, [updatePolicy]); let content: React.ReactNode; - switch (policy) { + switch (policyJson) { case undefined: content = ; break; @@ -100,12 +103,10 @@ export const PolicyJsonFlyout: React.FunctionComponent = ({ policyName, c ); break; default: - const { phases } = policy; - const json = JSON.stringify( { policy: { - phases, + ...policyJson, }, }, null, diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts index 7a4795f8e370b..bc27a3b909c85 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts @@ -11,12 +11,12 @@ import { ElasticsearchClient } from 'kibana/server'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '../../../services'; -async function createPolicy(client: ElasticsearchClient, name: string, phases: any): Promise { - const body = { - policy: { - phases, - }, - }; +async function createPolicy( + client: ElasticsearchClient, + name: string, + policy: Omit +): Promise { + const body = { policy }; const options = { ignore: [404], }; @@ -40,6 +40,7 @@ const bodySchema = schema.object({ frozen: schema.maybe(schema.any()), delete: schema.maybe(schema.any()), }), + _meta: schema.maybe(schema.any()), }); export function registerCreateRoute({ @@ -51,10 +52,10 @@ export function registerCreateRoute({ { path: addBasePath('/policies'), validate: { body: bodySchema } }, license.guardApiRoute(async (context, request, response) => { const body = request.body as typeof bodySchema.type; - const { name, phases } = body; + const { name, ...rest } = body; try { - await createPolicy(context.core.elasticsearch.client.asCurrentUser, name, phases); + await createPolicy(context.core.elasticsearch.client.asCurrentUser, name, rest); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts index dcb72455e0ee9..ca9d830816dbc 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts @@ -8,7 +8,7 @@ import type { Capabilities, HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; -import { Ast } from '@kbn/interpreter/target/common'; +import { Ast } from '@kbn/interpreter/common'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { IndexPatternsContract, TimefilterContract } from '../../../../../src/plugins/data/public'; import { ReactExpressionRendererType } from '../../../../../src/plugins/expressions/public'; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx index d38afc17b2b07..c666d27e780b5 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx @@ -174,6 +174,17 @@ export const HeatmapComponent: FC = ({ minMaxByColumnId[args.valueAccessor!] ); + const bands = ranges.map((start, index, array) => { + return { + // with the default continuity:above the every range is left-closed + start, + // with the default continuity:above the last range is right-open + end: index === array.length - 1 ? Infinity : array[index + 1], + // the current colors array contains a duplicated color at the beginning that we need to skip + color: colors[index + 1], + }; + }); + const onElementClick = ((e: HeatmapElementEvent[]) => { const cell = e[0][0]; const { x, y } = cell.datum; @@ -331,9 +342,10 @@ export const HeatmapComponent: FC = ({ , - "name": "Edit layer settings", - "onClick": [Function], - "toolTipContent": null, - }, ], "title": "Layer actions", }, diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index ed0946e526c80..322c0540528d7 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -174,19 +174,19 @@ export class TOCEntryActionsPopover extends Component { }, }); } - actionItems.push({ - disabled: this.props.isEditButtonDisabled, - name: EDIT_LAYER_SETTINGS_LABEL, - icon: , - 'data-test-subj': 'layerSettingsButton', - toolTipContent: null, - onClick: () => { - this._closePopover(); - this.props.openLayerSettings(); - }, - }); if (!this.props.isReadOnly) { + actionItems.push({ + disabled: this.props.isEditButtonDisabled, + name: EDIT_LAYER_SETTINGS_LABEL, + icon: , + 'data-test-subj': 'layerSettingsButton', + toolTipContent: null, + onClick: () => { + this._closePopover(); + this.props.openLayerSettings(); + }, + }); if (this.state.supportsFeatureEditing) { actionItems.push({ name: EDIT_FEATURES_LABEL, diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 5245b374b9fec..3f6e1fdbe8475 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -16,9 +16,26 @@ "references": [ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/maps_ems/tsconfig.json" }, + { "path": "../../../src/plugins/dashboard/tsconfig.json" }, + { "path": "../../../src/plugins/inspector/tsconfig.json" }, + { "path": "../../../src/plugins/data/tsconfig.json" }, + { "path": "../../../src/plugins/ui_actions/tsconfig.json" }, + { "path": "../../../src/plugins/navigation/tsconfig.json" }, + { "path": "../../../src/plugins/expressions/tsconfig.json" }, + { "path": "../../../src/plugins/visualizations/tsconfig.json" }, + { "path": "../../../src/plugins/embeddable/tsconfig.json" }, + { "path": "../../../src/plugins/saved_objects/tsconfig.json" }, + { "path": "../../../src/plugins/share/tsconfig.json" }, + { "path": "../../../src/plugins/presentation_util/tsconfig.json" }, + { "path": "../../../src/plugins/home/tsconfig.json" }, + { "path": "../../../src/plugins/charts/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, { "path": "../file_upload/tsconfig.json" }, { "path": "../saved_objects_tagging/tsconfig.json" }, + { "path": "../security/tsconfig.json" } ] } diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx index a405f0486430c..82ae65b69d33a 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx @@ -9,13 +9,12 @@ import React, { FC, useCallback, useState } from 'react'; import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public'; import { JobType, ML_SAVED_OBJECT_TYPE, SavedObjectResult, } from '../../../../common/types/saved_objects'; -import type { SpacesPluginStart } from '../../../../../spaces/public'; +import type { SpacesPluginStart, ShareToSpaceFlyoutProps } from '../../../../../spaces/public'; import { ml } from '../../services/ml_api_service'; import { useToastNotificationService } from '../../services/toast_notification_service'; @@ -94,7 +93,11 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, return ( <> - setShowFlyout(true)} style={{ height: 'auto' }}> + setShowFlyout(true)} + style={{ height: 'auto' }} + data-test-subj="mlJobListRowManageSpacesButton" + > {showFlyout && } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx index 664acce325e9a..d3d2370acd55e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx @@ -299,6 +299,7 @@ export const useColumns = ( /> ) : null, width: '90px', + 'data-test-subj': 'mlAnalyticsTableColumnSpaces', }); } // Remove actions if Ml not enabled in current space diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index c58ced33b277d..e49163de9e680 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -427,22 +427,36 @@ export const SwimlaneContainer: FC = ({ ), + 'data-test-subj': 'mlJobListColumnSpaces', }); } // Remove actions if Ml not enabled in current space diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index a1528b91d5abb..8dccbe973318b 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -22,7 +22,6 @@ import { EuiFlexItem, } from '@elastic/eui'; -import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; import type { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import { PLUGIN_ID } from '../../../../../../common/constants/app'; @@ -41,7 +40,7 @@ import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/a import { AccessDeniedPage } from '../access_denied_page'; import { InsufficientLicensePage } from '../insufficient_license_page'; import type { SharePluginStart } from '../../../../../../../../../src/plugins/share/public'; -import type { SpacesPluginStart } from '../../../../../../../spaces/public'; +import type { SpacesPluginStart, SpacesContextProps } from '../../../../../../../spaces/public'; import { JobSpacesSyncFlyout } from '../../../../components/job_spaces_sync'; import { getDefaultAnomalyDetectionJobsListState } from '../../../../jobs/jobs_list/jobs'; import { getMlGlobalServices } from '../../../../app'; diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index c8e7285d8a34d..dacdb5e5d5d10 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -144,7 +144,7 @@ export function drawLineChartDots(data, lineChartGroup, lineChartValuesLine, rad } // this replicates Kibana's filterAxisLabels() behavior -// which can be found in src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js +// which can be found in src/plugins/vis_types/vislib/public/vislib/lib/axis/axis_labels.js // axis labels which overflow the chart's boundaries will be removed export function filterAxisLabels(selection, chartWidth) { if (selection === undefined || selection.selectAll === undefined) { diff --git a/x-pack/plugins/monitoring/public/alerts/enable_alerts_modal.tsx b/x-pack/plugins/monitoring/public/alerts/enable_alerts_modal.tsx index 827ce958deb11..780997ca98191 100644 --- a/x-pack/plugins/monitoring/public/alerts/enable_alerts_modal.tsx +++ b/x-pack/plugins/monitoring/public/alerts/enable_alerts_modal.tsx @@ -42,7 +42,7 @@ export const EnableAlertsModal: React.FC = ({ alerts }: Props) => { { id: 'create-alerts', label: i18n.translate('xpack.monitoring.alerts.modal.yesOption', { - defaultMessage: 'Yes (Recommended - create default rules in this kibana spaces)', + defaultMessage: 'Yes (Recommended - create default rules in this kibana space)', }), }, { diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts b/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts new file mode 100644 index 0000000000000..49f6464b2ce3e --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/hooks/use_clusters.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, useEffect } from 'react'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants'; + +export function useClusters(codePaths?: string[], fetchAllClusters?: boolean, ccs?: any) { + const clusterUuid = fetchAllClusters ? null : ''; + const { services } = useKibana<{ data: any }>(); + + const bounds = services.data?.query.timefilter.timefilter.getBounds(); + const [min] = useState(bounds.min.toISOString()); + const [max] = useState(bounds.max.toISOString()); + + const [clusters, setClusters] = useState([]); + const [loaded, setLoaded] = useState(false); + + let url = '../api/monitoring/v1/clusters'; + if (clusterUuid) { + url += `/${clusterUuid}`; + } + + useEffect(() => { + const fetchClusters = async () => { + try { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min, + max, + }, + codePaths, + }), + }); + + setClusters(formatClusters(response)); + } catch (err) { + // TODO: handle errors + } finally { + setLoaded(null); + } + }; + + fetchClusters(); + }, [ccs, services.http, codePaths, url, min, max]); + + return { clusters, loaded }; +} + +function formatClusters(clusters: any) { + return clusters.map(formatCluster); +} + +function formatCluster(cluster: any) { + if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) { + cluster.cluster_name = 'Standalone Cluster'; + } + return cluster; +} diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_title.ts b/x-pack/plugins/monitoring/public/application/hooks/use_title.ts new file mode 100644 index 0000000000000..25cc2c5b40dff --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/hooks/use_title.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; + +// TODO: verify that works for all pages +export function useTitle(cluster: string, suffix: string) { + const { services } = useKibana(); + let clusterName = get(cluster, 'cluster_name'); + clusterName = clusterName ? `- ${clusterName}` : ''; + suffix = suffix ? `- ${suffix}` : ''; + + services.chrome?.docTitle.change( + i18n.translate('xpack.monitoring.stackMonitoringDocTitle', { + defaultMessage: 'Stack Monitoring {clusterName} {suffix}', + values: { clusterName, suffix }, + }) + ); +} diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx new file mode 100644 index 0000000000000..a0c9afd73f0ce --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart, AppMountParameters } from 'kibana/public'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Route, Switch, Redirect, HashRouter } from 'react-router-dom'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { LoadingPage } from './pages/loading_page'; +import { MonitoringStartPluginDependencies } from '../types'; + +export const renderApp = ( + core: CoreStart, + plugins: MonitoringStartPluginDependencies, + { element }: AppMountParameters +) => { + ReactDOM.render(, element); + + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +}; + +const MonitoringApp: React.FC<{ + core: CoreStart; + plugins: MonitoringStartPluginDependencies; +}> = ({ core, plugins }) => { + return ( + + + + + + + + + + + + ); +}; + +const NoData: React.FC<{}> = () => { + return
No data page
; +}; + +const Home: React.FC<{}> = () => { + return
Home page (Cluster listing)
; +}; + +const ClusterOverview: React.FC<{}> = () => { + return
Cluster overview page
; +}; diff --git a/x-pack/plugins/monitoring/public/application/pages/loading_page.tsx b/x-pack/plugins/monitoring/public/application/pages/loading_page.tsx new file mode 100644 index 0000000000000..4bd09f73ac75a --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/loading_page.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Redirect } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { PageTemplate } from './page_template'; +import { PageLoading } from '../../components'; +import { useClusters } from '../hooks/use_clusters'; +import { CODE_PATH_ELASTICSEARCH } from '../../../common/constants'; + +const CODE_PATHS = [CODE_PATH_ELASTICSEARCH]; + +export const LoadingPage = () => { + const { clusters, loaded } = useClusters(CODE_PATHS, true); + const title = i18n.translate('xpack.monitoring.loading.pageTitle', { + defaultMessage: 'Loading', + }); + + return ( + + {loaded === false ? : renderRedirections(clusters)} + + ); +}; + +const renderRedirections = (clusters: any) => { + if (!clusters || !clusters.length) { + return ; + } + if (clusters.length === 1) { + // Bypass the cluster listing if there is just 1 cluster + return ; + } + + return ; +}; diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx new file mode 100644 index 0000000000000..fb766af6c8cbe --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useTitle } from '../hooks/use_title'; + +interface PageTemplateProps { + title: string; + children: React.ReactNode; +} + +export const PageTemplate = ({ title, children }: PageTemplateProps) => { + useTitle('', title); + + return
{children}
; +}; diff --git a/x-pack/plugins/dashboard_mode/public/index.ts b/x-pack/plugins/monitoring/public/components/index.d.ts similarity index 83% rename from x-pack/plugins/dashboard_mode/public/index.ts rename to x-pack/plugins/monitoring/public/components/index.d.ts index 2418c0592acc1..d027298c81c4c 100644 --- a/x-pack/plugins/dashboard_mode/public/index.ts +++ b/x-pack/plugins/monitoring/public/components/index.d.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { plugin } from './plugin'; +export const PageLoading: FunctionComponent; diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index 710b453e7f21e..df0496d438013 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -94,6 +94,7 @@ export class MonitoringPlugin mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); const { AngularApp } = await import('./angular'); + const externalConfig = this.getExternalConfig(); const deps: MonitoringStartPluginDependencies = { navigation: pluginsStart.navigation, kibanaLegacy: pluginsStart.kibanaLegacy, @@ -102,27 +103,33 @@ export class MonitoringPlugin data: pluginsStart.data, isCloud: Boolean(plugins.cloud?.isCloudEnabled), pluginInitializerContext: this.initializerContext, - externalConfig: this.getExternalConfig(), + externalConfig, triggersActionsUi: pluginsStart.triggersActionsUi, usageCollection: plugins.usageCollection, appMountParameters: params, }; - const monitoringApp = new AngularApp(deps); - const removeHistoryListener = params.history.listen((location) => { - if (location.pathname === '' && location.hash === '') { - monitoringApp.applyScope(); - } - }); - - const removeHashChange = this.setInitialTimefilter(deps); - return () => { - if (removeHashChange) { - removeHashChange(); - } - removeHistoryListener(); - monitoringApp.destroy(); - }; + const config = Object.fromEntries(externalConfig); + if (config.renderReactApp) { + const { renderApp } = await import('./application'); + return renderApp(coreStart, pluginsStart, params); + } else { + const monitoringApp = new AngularApp(deps); + const removeHistoryListener = params.history.listen((location) => { + if (location.pathname === '' && location.hash === '') { + monitoringApp.applyScope(); + } + }); + + const removeHashChange = this.setInitialTimefilter(deps); + return () => { + if (removeHashChange) { + removeHashChange(); + } + removeHistoryListener(); + monitoringApp.destroy(); + }; + } }, }; @@ -163,6 +170,7 @@ export class MonitoringPlugin ['showLicenseExpiration', monitoring.ui.show_license_expiration], ['showCgroupMetricsElasticsearch', monitoring.ui.container.elasticsearch.enabled], ['showCgroupMetricsLogstash', monitoring.ui.container.logstash.enabled], + ['renderReactApp', monitoring.ui.render_react_app], ]; } diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 9a5699189241f..8e7f4d0f13a83 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -106,6 +106,7 @@ describe('config schema', () => { "index": "metricbeat-*", }, "min_interval_seconds": 10, + "render_react_app": false, "show_license_expiration": true, }, } diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 98fd02b03539c..5c2bdc1424f93 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -51,6 +51,7 @@ export const configSchema = schema.object({ }), min_interval_seconds: schema.number({ defaultValue: 10 }), show_license_expiration: schema.boolean({ defaultValue: true }), + render_react_app: schema.boolean({ defaultValue: false }), }), kibana: schema.object({ collection: schema.object({ diff --git a/x-pack/plugins/observability/common/typings.ts b/x-pack/plugins/observability/common/typings.ts index 305a18903fe7e..71337075e1617 100644 --- a/x-pack/plugins/observability/common/typings.ts +++ b/x-pack/plugins/observability/common/typings.ts @@ -8,8 +8,12 @@ import * as t from 'io-ts'; export type Maybe = T | null | undefined; -export const alertStatusRt = t.union([t.literal('all'), t.literal('open'), t.literal('closed')]); -export type AlertStatus = t.TypeOf; +export const alertWorkflowStatusRt = t.keyof({ + open: null, + acknowledged: null, + closed: null, +}); +export type AlertWorkflowStatus = t.TypeOf; export interface ApmIndicesConfig { /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts index f7df2939d9909..1af4e83ed1f54 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts @@ -7,7 +7,7 @@ import rison, { RisonValue } from 'rison-node'; import type { SeriesUrl, UrlFilter } from '../types'; import type { AllSeries, AllShortSeries } from '../hooks/use_series_storage'; -import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; +import { IndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; import { esFilters, ExistsFilter } from '../../../../../../../../src/plugins/data/public'; import { URL_KEYS } from './constants/url_constants'; import { PersistableFilter } from '../../../../../../lens/common'; @@ -53,7 +53,7 @@ export function createExploratoryViewUrl(allSeries: AllSeries, baseHref = '') { ); } -export function buildPhraseFilter(field: string, value: string, indexPattern: IIndexPattern) { +export function buildPhraseFilter(field: string, value: string, indexPattern: IndexPattern) { const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { return [esFilters.buildPhraseFilter(fieldMeta, value, indexPattern)]; @@ -61,7 +61,7 @@ export function buildPhraseFilter(field: string, value: string, indexPattern: II return []; } -export function buildPhrasesFilter(field: string, value: string[], indexPattern: IIndexPattern) { +export function buildPhrasesFilter(field: string, value: string[], indexPattern: IndexPattern) { const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { return [esFilters.buildPhrasesFilter(fieldMeta, value, indexPattern)]; @@ -69,7 +69,7 @@ export function buildPhrasesFilter(field: string, value: string[], indexPattern: return []; } -export function buildExistsFilter(field: string, indexPattern: IIndexPattern) { +export function buildExistsFilter(field: string, indexPattern: IndexPattern) { const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { return [esFilters.buildExistsFilter(fieldMeta, indexPattern)]; @@ -86,7 +86,7 @@ export function urlFilterToPersistedFilter({ }: { urlFilters: UrlFilter[]; initFilters: FiltersType; - indexPattern: IIndexPattern; + indexPattern: IndexPattern; }) { const parsedFilters: FiltersType = initFilters ? [...initFilters] : []; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx index 7a5f12a72b1f0..e508990ea25a4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx @@ -13,14 +13,14 @@ import { ObservabilityPublicPluginsStart } from '../../../../plugin'; import { ObservabilityIndexPatterns } from '../utils/observability_index_patterns'; import { getDataHandler } from '../../../../data_handler'; -export interface IIndexPatternContext { +export interface IndexPatternContext { loading: boolean; indexPatterns: IndexPatternState; hasAppData: HasAppDataState; loadIndexPattern: (params: { dataType: AppDataType }) => void; } -export const IndexPatternContext = createContext>({}); +export const IndexPatternContext = createContext>({}); interface ProviderProps { children: JSX.Element; @@ -46,7 +46,7 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { services: { data }, } = useKibana(); - const loadIndexPattern: IIndexPatternContext['loadIndexPattern'] = useCallback( + const loadIndexPattern: IndexPatternContext['loadIndexPattern'] = useCallback( async ({ dataType }) => { if (hasAppData[dataType] === null && !loading[dataType]) { setLoading((prevState) => ({ ...prevState, [dataType]: true })); @@ -101,7 +101,7 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { export const useAppIndexPatternContext = (dataType?: AppDataType) => { const { loading, hasAppData, loadIndexPattern, indexPatterns } = useContext( - (IndexPatternContext as unknown) as Context + (IndexPatternContext as unknown) as Context ); if (dataType && !indexPatterns?.[dataType] && !loading) { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index fbda2f4ff62e2..9817899412ce3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -17,7 +17,7 @@ import { } from '../../../../../lens/public'; import { PersistableFilter } from '../../../../../lens/common'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../../../src/plugins/data/public'; export const ReportViewTypes = { dist: 'data-distribution', @@ -91,7 +91,7 @@ export interface UrlFilter { } export interface ConfigProps { - indexPattern: IIndexPattern; + indexPattern: IndexPattern; series?: SeriesUrl; } diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_search_bar.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_search_bar.tsx index f32088e2646b3..01bb01857eaf0 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_search_bar.tsx @@ -5,19 +5,20 @@ * 2.0. */ +import { IndexPatternBase } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React, { useMemo, useState } from 'react'; -import { IIndexPattern, SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public'; +import { SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; export function AlertsSearchBar({ - dynamicIndexPattern, + dynamicIndexPatterns, rangeFrom, rangeTo, onQueryChange, query, }: { - dynamicIndexPattern: IIndexPattern[]; + dynamicIndexPatterns: IndexPatternBase[]; rangeFrom?: string; rangeTo?: string; query?: string; @@ -31,9 +32,19 @@ export function AlertsSearchBar({ }, []); const [queryLanguage, setQueryLanguage] = useState<'lucene' | 'kuery'>('kuery'); + const compatibleIndexPatterns = useMemo( + () => + dynamicIndexPatterns.map((dynamicIndexPattern) => ({ + title: dynamicIndexPattern.title ?? '', + id: dynamicIndexPattern.id ?? '', + fields: dynamicIndexPattern.fields, + })), + [dynamicIndexPatterns] + ); + return ( 75', })} diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx index bbbd81b4e49ea..e93f7cb127a65 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx @@ -11,29 +11,29 @@ * This way plugins can do targeted imports to reduce the final code bundle */ import { - AlertConsumers as AlertConsumersTyped, ALERT_DURATION as ALERT_DURATION_TYPED, - ALERT_STATUS as ALERT_STATUS_TYPED, ALERT_REASON as ALERT_REASON_TYPED, ALERT_RULE_CONSUMER, + ALERT_STATUS as ALERT_STATUS_TYPED, + ALERT_WORKFLOW_STATUS as ALERT_WORKFLOW_STATUS_TYPED, } from '@kbn/rule-data-utils'; +// @ts-expect-error importing from a place other than root because we want to limit what we import from this package +import { AlertConsumers as AlertConsumersNonTyped } from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac'; import { ALERT_DURATION as ALERT_DURATION_NON_TYPED, - ALERT_STATUS as ALERT_STATUS_NON_TYPED, ALERT_REASON as ALERT_REASON_NON_TYPED, + ALERT_STATUS as ALERT_STATUS_NON_TYPED, + ALERT_WORKFLOW_STATUS as ALERT_WORKFLOW_STATUS_NON_TYPED, TIMESTAMP, // @ts-expect-error importing from a place other than root because we want to limit what we import from this package } from '@kbn/rule-data-utils/target_node/technical_field_names'; -// @ts-expect-error importing from a place other than root because we want to limit what we import from this package -import { AlertConsumers as AlertConsumersNonTyped } from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac'; - import { EuiButtonIcon, EuiDataGridColumn, EuiFlexGroup, EuiFlexItem, - EuiContextMenu, + EuiContextMenuPanel, EuiPopover, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -47,7 +47,7 @@ import type { TopAlert } from './'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import type { ActionProps, - AlertStatus, + AlertWorkflowStatus, ColumnHeaderOptions, RowRenderer, } from '../../../../timelines/common'; @@ -61,23 +61,23 @@ import { LazyAlertsFlyout } from '../..'; import { parseAlert } from './parse_alert'; import { CoreStart } from '../../../../../../src/core/public'; -const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped; const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED; -const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED; const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED; +const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED; +const ALERT_WORKFLOW_STATUS: typeof ALERT_WORKFLOW_STATUS_TYPED = ALERT_WORKFLOW_STATUS_NON_TYPED; interface AlertsTableTGridProps { - indexName: string; + indexNames: string[]; rangeFrom: string; rangeTo: string; kuery: string; - status: string; + workflowStatus: AlertWorkflowStatus; setRefetch: (ref: () => void) => void; addToQuery: (value: string) => void; } interface ObservabilityActionsProps extends ActionProps { - currentStatus: AlertStatus; + currentStatus: AlertWorkflowStatus; setFlyoutAlert: React.Dispatch>; } @@ -145,13 +145,6 @@ const NO_ROW_RENDER: RowRenderer[] = []; const trailingControlColumns: never[] = []; -const OBSERVABILITY_ALERT_CONSUMERS = [ - AlertConsumers.APM, - AlertConsumers.LOGS, - AlertConsumers.INFRASTRUCTURE, - AlertConsumers.UPTIME, -]; - function ObservabilityActions({ data, eventId, @@ -219,26 +212,21 @@ function ObservabilityActions({ onUpdateFailure: onAlertStatusUpdated, }); - const actionsPanels = useMemo(() => { + const actionsMenuItems = useMemo(() => { return [ - { - id: 0, - content: [ - timelines.getAddToExistingCaseButton({ - event, - casePermissions, - appId: observabilityFeatureId, - onClose: afterCaseSelection, - }), - timelines.getAddToNewCaseButton({ - event, - casePermissions, - appId: observabilityFeatureId, - onClose: afterCaseSelection, - }), - ...(alertPermissions.crud ? statusActionItems : []), - ], - }, + timelines.getAddToExistingCaseButton({ + event, + casePermissions, + appId: observabilityFeatureId, + onClose: afterCaseSelection, + }), + timelines.getAddToNewCaseButton({ + event, + casePermissions, + appId: observabilityFeatureId, + onClose: afterCaseSelection, + }), + ...(alertPermissions.crud ? statusActionItems : []), ]; }, [afterCaseSelection, casePermissions, timelines, event, statusActionItems, alertPermissions]); @@ -262,33 +250,35 @@ function ObservabilityActions({ aria-label="View alert in app" /> - - toggleActionsPopover(eventId)} - /> - } - isOpen={openActionsPopoverId === eventId} - closePopover={closeActionsPopover} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - + {actionsMenuItems.length > 0 && ( + + toggleActionsPopover(eventId)} + /> + } + isOpen={openActionsPopoverId === eventId} + closePopover={closeActionsPopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + )} ); } export function AlertsTableTGrid(props: AlertsTableTGridProps) { - const { indexName, rangeFrom, rangeTo, kuery, status, setRefetch, addToQuery } = props; + const { indexNames, rangeFrom, rangeTo, kuery, workflowStatus, setRefetch, addToQuery } = props; const { timelines } = useKibana<{ timelines: TimelinesUIStart }>().services; const [flyoutAlert, setFlyoutAlert] = useState(undefined); @@ -313,20 +303,19 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { return ( ); }, }, ]; - }, [status]); + }, [workflowStatus]); const tGridProps = useMemo(() => { const type: TGridType = 'standalone'; const sortDirection: SortDirection = 'desc'; return { - alertConsumers: OBSERVABILITY_ALERT_CONSUMERS, appId: observabilityFeatureId, casePermissions, type, @@ -335,7 +324,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { defaultCellActions: getDefaultCellActions({ addToQuery }), end: rangeTo, filters: [], - indexNames: [indexName], + indexNames, itemsPerPage: 10, itemsPerPageOptions: [10, 25, 50], loadingText: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', { @@ -345,7 +334,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { defaultMessage: 'alerts', }), query: { - query: `${ALERT_STATUS}: ${status}${kuery !== '' ? ` and ${kuery}` : ''}`, + query: `${ALERT_WORKFLOW_STATUS}: ${workflowStatus}${kuery !== '' ? ` and ${kuery}` : ''}`, language: 'kuery', }, renderCellValue: getRenderCellValue({ rangeFrom, rangeTo, setFlyoutAlert }), @@ -359,7 +348,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { sortDirection, }, ], - filterStatus: status as AlertStatus, + filterStatus: workflowStatus as AlertWorkflowStatus, leadingControlColumns, trailingControlColumns, unit: (totalAlerts: number) => @@ -370,13 +359,13 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { }; }, [ casePermissions, - indexName, + indexNames, kuery, leadingControlColumns, rangeFrom, rangeTo, setRefetch, - status, + workflowStatus, addToQuery, ]); const handleFlyoutClose = () => setFlyoutAlert(undefined); diff --git a/x-pack/plugins/observability/public/pages/alerts/index.tsx b/x-pack/plugins/observability/public/pages/alerts/index.tsx index b3ff3f94dc4db..751307196fa19 100644 --- a/x-pack/plugins/observability/public/pages/alerts/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/index.tsx @@ -7,19 +7,21 @@ import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useRef } from 'react'; import { useHistory } from 'react-router-dom'; +import useAsync from 'react-use/lib/useAsync'; +import { IndexPatternBase } from '@kbn/es-query'; import { ParsedTechnicalFields } from '../../../../rule_registry/common/parse_technical_fields'; -import type { AlertStatus } from '../../../common/typings'; +import type { AlertWorkflowStatus } from '../../../common/typings'; import { ExperimentalBadge } from '../../components/shared/experimental_badge'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useFetcher } from '../../hooks/use_fetcher'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { RouteParams } from '../../routes'; +import { callObservabilityApi } from '../../services/call_observability_api'; import { AlertsSearchBar } from './alerts_search_bar'; import { AlertsTableTGrid } from './alerts_table_t_grid'; -import { StatusFilter } from './status_filter'; -import { useFetcher } from '../../hooks/use_fetcher'; -import { callObservabilityApi } from '../../services/call_observability_api'; +import { WorkflowStatusFilter } from './workflow_status_filter'; import './styles.scss'; export interface TopAlert { @@ -35,7 +37,7 @@ interface AlertsPageProps { } export function AlertsPage({ routeParams }: AlertsPageProps) { - const { core, ObservabilityPageTemplate } = usePluginContext(); + const { core, plugins, ObservabilityPageTemplate } = usePluginContext(); const { prepend } = core.http.basePath; const history = useHistory(); const refetch = useRef<() => void>(); @@ -44,7 +46,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { rangeFrom = 'now-15m', rangeTo = 'now', kuery = 'kibana.alert.status: "open"', // TODO change hardcoded values as part of another PR - status = 'open', + workflowStatus = 'open', }, } = routeParams; @@ -60,22 +62,46 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { // observability. For now link to the settings page. const manageRulesHref = prepend('/app/management/insightsAndAlerting/triggersActions/alerts'); - const { data: dynamicIndexPatternResp } = useFetcher(({ signal }) => { + const { data: indexNames = NO_INDEX_NAMES } = useFetcher(({ signal }) => { return callObservabilityApi({ signal, endpoint: 'GET /api/observability/rules/alerts/dynamic_index_pattern', + params: { + query: { + namespace: 'default', + registrationContexts: [ + 'observability.apm', + 'observability.logs', + 'observability.infrastructure', + 'observability.metrics', + 'observability.uptime', + ], + }, + }, }); }, []); - const dynamicIndexPattern = useMemo( - () => (dynamicIndexPatternResp ? [dynamicIndexPatternResp] : []), - [dynamicIndexPatternResp] - ); + const dynamicIndexPatternsAsyncState = useAsync(async (): Promise => { + if (indexNames.length === 0) { + return []; + } + + return [ + { + id: 'dynamic-observability-alerts-table-index-pattern', + title: indexNames.join(','), + fields: await plugins.data.indexPatterns.getFieldsForWildcard({ + pattern: indexNames.join(','), + allowNoIndex: true, + }), + }, + ]; + }, [indexNames]); - const setStatusFilter = useCallback( - (value: AlertStatus) => { + const setWorkflowStatusFilter = useCallback( + (value: AlertWorkflowStatus) => { const nextSearchParams = new URLSearchParams(history.location.search); - nextSearchParams.set('status', value); + nextSearchParams.set('workflowStatus', value); history.push({ ...history.location, search: nextSearchParams.toString(), @@ -165,36 +191,37 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { + - - - - - - - - - - 0 ? dynamicIndexPattern[0].title : ''} - rangeFrom={rangeFrom} - rangeTo={rangeTo} - kuery={kuery} - status={status} - setRefetch={setRefetch} - addToQuery={addToQuery} - /> + + + + + + + ); } + +const NO_INDEX_NAMES: string[] = []; +const NO_INDEX_PATTERNS: IndexPatternBase[] = []; diff --git a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx index c85ea0b1086fa..691bfc984b9cb 100644 --- a/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/render_cell_value.tsx @@ -6,7 +6,7 @@ */ import { EuiLink, EuiHealth, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useEffect } from 'react'; +import React from 'react'; /** * We need to produce types and code transpilation at different folders during the build of the package. * We have types and code at different imports because we don't want to import the whole package in the resulting webpack bundle for the plugin. @@ -77,16 +77,6 @@ export const getRenderCellValue = ({ fieldName: columnId, })?.reduce((x) => x[0]); - useEffect(() => { - if (columnId === ALERT_STATUS) { - setCellProps({ - style: { - textAlign: 'center', - }, - }); - } - }, [columnId, setCellProps]); - const theme = useTheme(); switch (columnId) { diff --git a/x-pack/plugins/observability/public/pages/alerts/status_filter.tsx b/x-pack/plugins/observability/public/pages/alerts/status_filter.tsx deleted file mode 100644 index 26169717d2967..0000000000000 --- a/x-pack/plugins/observability/public/pages/alerts/status_filter.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import type { AlertStatus } from '../../../common/typings'; - -export interface StatusFilterProps { - status: AlertStatus; - onChange: (value: AlertStatus) => void; -} - -export function StatusFilter({ status = 'open', onChange }: StatusFilterProps) { - return ( - - onChange('open')} - withNext={true} - > - {i18n.translate('xpack.observability.alerts.statusFilter.openButtonLabel', { - defaultMessage: 'Open', - })} - - onChange('closed')} - withNext={true} - > - {i18n.translate('xpack.observability.alerts.statusFilter.closedButtonLabel', { - defaultMessage: 'Closed', - })} - - onChange('all')} - > - {i18n.translate('xpack.observability.alerts.statusFilter.allButtonLabel', { - defaultMessage: 'All', - })} - - - ); -} diff --git a/x-pack/plugins/observability/public/pages/alerts/status_filter.stories.tsx b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.stories.tsx similarity index 65% rename from x-pack/plugins/observability/public/pages/alerts/status_filter.stories.tsx rename to x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.stories.tsx index 851e0cb6c3ddd..e06b5d333a9a6 100644 --- a/x-pack/plugins/observability/public/pages/alerts/status_filter.stories.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.stories.tsx @@ -6,24 +6,24 @@ */ import React, { ComponentProps, useState } from 'react'; -import type { AlertStatus } from '../../../common/typings'; -import { StatusFilter } from './status_filter'; +import type { AlertWorkflowStatus } from '../../../common/typings'; +import { WorkflowStatusFilter } from './workflow_status_filter'; -type Args = ComponentProps; +type Args = ComponentProps; export default { title: 'app/Alerts/StatusFilter', - component: StatusFilter, + component: WorkflowStatusFilter, argTypes: { onChange: { action: 'change' }, }, }; export function Example({ onChange }: Args) { - const [status, setStatus] = useState('open'); + const [status, setStatus] = useState('open'); return ( - { setStatus(value); diff --git a/x-pack/plugins/observability/public/pages/alerts/status_filter.test.tsx b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.test.tsx similarity index 55% rename from x-pack/plugins/observability/public/pages/alerts/status_filter.test.tsx rename to x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.test.tsx index 72e07ebb8cadf..cc16f1c5a44a1 100644 --- a/x-pack/plugins/observability/public/pages/alerts/status_filter.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.test.tsx @@ -6,30 +6,32 @@ */ import { render } from '@testing-library/react'; +import { Simulate } from 'react-dom/test-utils'; import React from 'react'; -import type { AlertStatus } from '../../../common/typings'; -import { StatusFilter } from './status_filter'; +import type { AlertWorkflowStatus } from '../../../common/typings'; +import { WorkflowStatusFilter } from './workflow_status_filter'; describe('StatusFilter', () => { describe('render', () => { it('renders', () => { const onChange = jest.fn(); - const status: AlertStatus = 'all'; + const status: AlertWorkflowStatus = 'open'; const props = { onChange, status }; - expect(() => render()).not.toThrowError(); + expect(() => render()).not.toThrowError(); }); - (['all', 'open', 'closed'] as AlertStatus[]).map((status) => { + (['open', 'acknowledged', 'closed'] as AlertWorkflowStatus[]).map((status) => { describe(`when clicking the ${status} button`, () => { it('calls the onChange callback with "${status}"', () => { const onChange = jest.fn(); const props = { onChange, status }; - const { getByTestId } = render(); - const button = getByTestId(`StatusFilter ${status} button`); + const { getByTestId } = render(); + const button = getByTestId(`WorkflowStatusFilter ${status} button`); + const input = button.querySelector('input') as Element; - button.click(); + Simulate.change(input); expect(onChange).toHaveBeenCalledWith(status); }); diff --git a/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.tsx b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.tsx new file mode 100644 index 0000000000000..475ba17a9d2f5 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/workflow_status_filter.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButtonGroup, EuiButtonGroupOptionProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import type { AlertWorkflowStatus } from '../../../common/typings'; + +export interface WorkflowStatusFilterProps { + status: AlertWorkflowStatus; + onChange: (value: AlertWorkflowStatus) => void; +} + +const options: Array = [ + { + id: 'open', + label: i18n.translate('xpack.observability.alerts.workflowStatusFilter.openButtonLabel', { + defaultMessage: 'Open', + }), + 'data-test-subj': 'WorkflowStatusFilter open button', + }, + { + id: 'acknowledged', + label: i18n.translate( + 'xpack.observability.alerts.workflowStatusFilter.acknowledgedButtonLabel', + { + defaultMessage: 'Acknowledged', + } + ), + 'data-test-subj': 'WorkflowStatusFilter acknowledged button', + }, + { + id: 'closed', + label: i18n.translate('xpack.observability.alerts.workflowStatusFilter.closedButtonLabel', { + defaultMessage: 'Closed', + }), + 'data-test-subj': 'WorkflowStatusFilter closed button', + }, +]; + +export function WorkflowStatusFilter({ status = 'open', onChange }: WorkflowStatusFilterProps) { + return ( + onChange(id as AlertWorkflowStatus)} + /> + ); +} diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index f97e3fb996441..00e487da7f9b7 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import React from 'react'; -import { alertStatusRt } from '../../common/typings'; +import { alertWorkflowStatusRt } from '../../common/typings'; import { ExploratoryViewPage } from '../components/shared/exploratory_view'; import { AlertsPage } from '../pages/alerts'; import { AllCasesPage } from '../pages/cases/all_cases'; @@ -93,7 +93,7 @@ export const routes = { rangeFrom: t.string, rangeTo: t.string, kuery: t.string, - status: alertStatusRt, + workflowStatus: alertWorkflowStatusRt, refreshPaused: jsonRt.pipe(t.boolean), refreshInterval: jsonRt.pipe(t.number), }), diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index f653117113737..cb4f85a44d12c 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -18,7 +18,7 @@ import { ScopedAnnotationsClientFactory, AnnotationsAPI, } from './lib/annotations/bootstrap_annotations'; -import { Dataset, RuleRegistryPluginSetupContract } from '../../rule_registry/server'; +import { RuleRegistryPluginSetupContract } from '../../rule_registry/server'; import { PluginSetupContract as FeaturesSetup } from '../../features/server'; import { uiSettings } from './ui_settings'; import { registerRoutes } from './routes/register_routes'; @@ -101,16 +101,6 @@ export class ObservabilityPlugin implements Plugin { const start = () => core.getStartServices().then(([coreStart]) => coreStart); const { ruleDataService } = plugins.ruleRegistry; - const ruleDataClient = ruleDataService.initializeIndex({ - feature: 'observability', - registrationContext: 'observability', - dataset: Dataset.alerts, - componentTemplateRefs: [], - componentTemplates: [], - indexTemplate: { - version: 0, - }, - }); registerRoutes({ core: { @@ -119,7 +109,7 @@ export class ObservabilityPlugin implements Plugin { }, logger: this.initContext.logger.get(), repository: getGlobalObservabilityServerRouteRepository(), - ruleDataClient, + ruleDataService, }); return { diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index 43b47f26dcdd1..660c38edb8e9d 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -13,7 +13,7 @@ import { import { CoreSetup, CoreStart, Logger, RouteRegistrar } from 'kibana/server'; import Boom from '@hapi/boom'; import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors'; -import { IRuleDataClient } from '../../../rule_registry/server'; +import { RuleDataPluginService } from '../../../rule_registry/server'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; @@ -21,7 +21,7 @@ export function registerRoutes({ repository, core, logger, - ruleDataClient, + ruleDataService, }: { core: { setup: CoreSetup; @@ -29,7 +29,7 @@ export function registerRoutes({ }; repository: AbstractObservabilityServerRouteRepository; logger: Logger; - ruleDataClient: IRuleDataClient; + ruleDataService: RuleDataPluginService; }) { const routes = repository.getRoutes(); @@ -62,7 +62,7 @@ export function registerRoutes({ core, logger, params: decodedParams, - ruleDataClient, + ruleDataService, })) as any; return response.ok({ body: data }); diff --git a/x-pack/plugins/observability/server/routes/rules.ts b/x-pack/plugins/observability/server/routes/rules.ts index 6ca4dc58147f2..a9039e4a66211 100644 --- a/x-pack/plugins/observability/server/routes/rules.ts +++ b/x-pack/plugins/observability/server/routes/rules.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { observabilityFeatureId } from '../../common'; +import * as t from 'io-ts'; import { createObservabilityServerRoute } from './create_observability_server_route'; import { createObservabilityServerRouteRepository } from './create_observability_server_route_repository'; @@ -14,10 +14,27 @@ const alertsDynamicIndexPatternRoute = createObservabilityServerRoute({ options: { tags: [], }, - handler: async ({ ruleDataClient }) => { - const reader = ruleDataClient.getReader({ namespace: observabilityFeatureId }); + params: t.type({ + query: t.type({ + registrationContexts: t.array(t.string), + namespace: t.string, + }), + }), + handler: async ({ ruleDataService, params }) => { + const { namespace, registrationContexts } = params.query; + const indexNames = registrationContexts.flatMap((registrationContext) => { + const indexName = ruleDataService + .getRegisteredIndexInfo(registrationContext) + ?.getPrimaryAlias(namespace); - return reader.getDynamicIndexPattern(); + if (indexName != null) { + return [indexName]; + } else { + return []; + } + }); + + return indexNames; }, }); diff --git a/x-pack/plugins/observability/server/routes/types.ts b/x-pack/plugins/observability/server/routes/types.ts index 15a3274087d0e..5075b21fdf1fa 100644 --- a/x-pack/plugins/observability/server/routes/types.ts +++ b/x-pack/plugins/observability/server/routes/types.ts @@ -12,7 +12,7 @@ import type { ServerRouteRepository, } from '@kbn/server-route-repository'; import { CoreSetup, CoreStart, KibanaRequest, Logger } from 'kibana/server'; -import { IRuleDataClient } from '../../../rule_registry/server'; +import { RuleDataPluginService } from '../../../rule_registry/server'; import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository'; import { ObservabilityRequestHandlerContext } from '../types'; @@ -24,7 +24,7 @@ export interface ObservabilityRouteHandlerResources { start: () => Promise; setup: CoreSetup; }; - ruleDataClient: IRuleDataClient; + ruleDataService: RuleDataPluginService; request: KibanaRequest; context: ObservabilityRequestHandlerContext; logger: Logger; diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index 4ba406a14bafc..842d7d1eb4b8d 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -79,6 +79,8 @@ export const CSV_JOB_TYPE_DEPRECATED = 'csv'; export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE]; +export const DEPRECATED_JOB_TYPES = [CSV_JOB_TYPE_DEPRECATED]; + // Licenses export const LICENSE_TYPE_TRIAL = 'trial'; export const LICENSE_TYPE_BASIC = 'basic'; diff --git a/x-pack/plugins/reporting/common/types.ts b/x-pack/plugins/reporting/common/types.ts index 745bc11a8c855..c324cf363faa1 100644 --- a/x-pack/plugins/reporting/common/types.ts +++ b/x-pack/plugins/reporting/common/types.ts @@ -73,7 +73,12 @@ export interface ReportSource { jobtype: string; // refers to `ExportTypeDefinition.jobType` created_by: string | false; // username or `false` if security is disabled. Used for ensuring users can only access the reports they've created. payload: BasePayload; - meta: { objectType: string; layout?: string }; // for telemetry + meta: { + // for telemetry + objectType: string; + layout?: string; + isDeprecated?: boolean; + }; migration_version: string; // for reminding the user to update their POST URL attempts: number; // initially populated as 0 created_at: string; // timestamp in UTC diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.test.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.test.ts index 8cfea1b010dfe..50103c8806fe0 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.test.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.test.ts @@ -97,6 +97,7 @@ describe('Enqueue Job', () => { "kibana_name": undefined, "max_attempts": undefined, "meta": Object { + "isDeprecated": undefined, "layout": undefined, "objectType": "cool_object_type", }, diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 998e4edf26a38..129c474fd134a 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -47,8 +47,10 @@ export async function enqueueJob( created_by: user ? user.username : false, payload: job, meta: { + // telemetry fields objectType: jobParams.objectType, layout: jobParams.layout?.id, + isDeprecated: job.isDeprecated, }, }) ); diff --git a/x-pack/plugins/reporting/server/lib/store/mapping.ts b/x-pack/plugins/reporting/server/lib/store/mapping.ts index 7a7a16c7bc7ea..a43b4494fe913 100644 --- a/x-pack/plugins/reporting/server/lib/store/mapping.ts +++ b/x-pack/plugins/reporting/server/lib/store/mapping.ts @@ -29,6 +29,9 @@ export const mapping = { }, }, }, + isDeprecated: { + type: 'boolean', + }, }, }, browser_type: { type: 'keyword' }, diff --git a/x-pack/plugins/reporting/server/routes/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/deprecations.test.ts new file mode 100644 index 0000000000000..5367b6bd531ed --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/deprecations.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 { of } from 'rxjs'; +import { UnwrapPromise } from '@kbn/utility-types'; +import { setupServer } from 'src/core/server/test_utils'; +import { API_GET_ILM_POLICY_STATUS } from '../../common/constants'; +import { securityMock } from '../../../security/server/mocks'; + +import supertest from 'supertest'; + +import { + createMockConfigSchema, + createMockPluginSetup, + createMockReportingCore, + createMockLevelLogger, +} from '../test_helpers'; + +import { registerDeprecationsRoutes } from './deprecations'; + +type SetupServerReturn = UnwrapPromise>; + +describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { + const reportingSymbol = Symbol('reporting'); + let server: SetupServerReturn['server']; + let httpSetup: SetupServerReturn['httpSetup']; + + const createReportingCore = ({ + security, + }: { + security?: ReturnType; + }) => + createMockReportingCore( + createMockConfigSchema({ + queue: { + indexInterval: 'year', + timeout: 10000, + pollEnabled: true, + }, + index: '.reporting', + }), + createMockPluginSetup({ + security, + router: httpSetup.createRouter(''), + licensing: { license$: of({ isActive: true, isAvailable: true, type: 'gold' }) }, + }) + ); + + beforeEach(async () => { + jest.clearAllMocks(); + ({ server, httpSetup } = await setupServer(reportingSymbol)); + }); + + it('correctly handles authz when security is unavailable', async () => { + const core = await createReportingCore({}); + + registerDeprecationsRoutes(core, createMockLevelLogger()); + await server.start(); + + await supertest(httpSetup.server.listener) + .get(API_GET_ILM_POLICY_STATUS) + .expect(200) + .then(/* Ignore result */); + }); + + it('correctly handles authz when security is disabled', async () => { + const security = securityMock.createSetup(); + security.license.isEnabled.mockReturnValue(false); + const core = await createReportingCore({ security }); + + registerDeprecationsRoutes(core, createMockLevelLogger()); + await server.start(); + + await supertest(httpSetup.server.listener) + .get(API_GET_ILM_POLICY_STATUS) + .expect(200) + .then(/* Ignore result */); + }); +}); diff --git a/x-pack/plugins/reporting/server/routes/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations.ts index 0daa56274cc00..874885e2258ae 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations.ts @@ -22,7 +22,7 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log const authzWrapper = (handler: RequestHandler): RequestHandler => { return async (ctx, req, res) => { const { security } = reporting.getPluginSetupDeps(); - if (!security) { + if (!security?.license.isEnabled()) { return handler(ctx, req, res); } diff --git a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index 150154fa996c5..12e89f19e6248 100644 --- a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -1,9 +1,757 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Ready for collection observable converts observable to promise 1`] = ` +Object { + "fetch": [Function], + "isReady": [Function], + "schema": Object { + "PNG": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "_all": Object { + "type": "long", + }, + "available": Object { + "type": "boolean", + }, + "browser_type": Object { + "type": "keyword", + }, + "csv": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "enabled": Object { + "type": "boolean", + }, + "last7Days": Object { + "PNG": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "_all": Object { + "type": "long", + }, + "csv": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "total": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "app": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "layout": Object { + "preserve_layout": Object { + "type": "long", + }, + "print": Object { + "type": "long", + }, + }, + "total": Object { + "type": "long", + }, + }, + "status": Object { + "completed": Object { + "type": "long", + }, + "completed_with_warnings": Object { + "type": "long", + }, + "failed": Object { + "type": "long", + }, + "pending": Object { + "type": "long", + }, + "processing": Object { + "type": "long", + }, + }, + "statuses": Object { + "completed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed_with_warnings": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "failed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "pending": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "processing": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + }, + }, + "printable_pdf": Object { + "app": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "available": Object { + "type": "boolean", + }, + "deprecated": Object { + "type": "long", + }, + "layout": Object { + "preserve_layout": Object { + "type": "long", + }, + "print": Object { + "type": "long", + }, + }, + "total": Object { + "type": "long", + }, + }, + "status": Object { + "completed": Object { + "type": "long", + }, + "completed_with_warnings": Object { + "type": "long", + }, + "failed": Object { + "type": "long", + }, + "pending": Object { + "type": "long", + }, + "processing": Object { + "type": "long", + }, + }, + "statuses": Object { + "completed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed_with_warnings": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "failed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "pending": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "processing": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv_searchsource": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + }, + }, + "type": "reporting", +} +`; + +exports[`data modeling usage data with meta.isDeprecated jobTypes 1`] = ` +Object { + "PNG": Object { + "available": true, + "deprecated": 0, + "total": 0, + }, + "_all": 9, + "available": true, + "browser_type": undefined, + "csv": Object { + "available": true, + "deprecated": 4, + "total": 4, + }, + "csv_searchsource": Object { + "available": true, + "deprecated": 0, + "total": 5, + }, + "enabled": true, + "last7Days": Object { + "PNG": Object { + "available": true, + "deprecated": 0, + "total": 0, + }, + "_all": 9, + "csv": Object { + "available": true, + "deprecated": 4, + "total": 4, + }, + "csv_searchsource": Object { + "available": true, + "deprecated": 0, + "total": 5, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "deprecated": 0, + "layout": Object { + "preserve_layout": 0, + "print": 0, + }, + "total": 0, + }, + "status": Object { + "completed": 9, + "failed": 0, + }, + "statuses": Object { + "completed": Object { + "csv": Object { + "search": 4, + }, + "csv_searchsource": Object { + "search": 5, + }, + }, + }, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "deprecated": 0, + "layout": Object { + "preserve_layout": 0, + "print": 0, + }, + "total": 0, + }, + "status": Object { + "completed": 9, + "failed": 0, + }, + "statuses": Object { + "completed": Object { + "csv": Object { + "search": 4, + }, + "csv_searchsource": Object { + "search": 5, + }, + }, + }, +} +`; + exports[`data modeling with empty data 1`] = ` Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 0, }, "_all": 0, @@ -11,25 +759,30 @@ Object { "browser_type": undefined, "csv": Object { "available": true, + "deprecated": 0, "total": 0, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "enabled": true, "last7Days": Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 0, }, "_all": 0, "csv": Object { "available": true, + "deprecated": 0, "total": 0, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "printable_pdf": Object { @@ -38,6 +791,7 @@ Object { "visualization": 0, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 0, "print": 0, @@ -56,6 +810,7 @@ Object { "visualization": 0, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 0, "print": 0, @@ -74,6 +829,7 @@ exports[`data modeling with normal looking usage data 1`] = ` Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 3, }, "_all": 12, @@ -81,25 +837,30 @@ Object { "browser_type": undefined, "csv": Object { "available": true, + "deprecated": 0, "total": 0, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "enabled": true, "last7Days": Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 1, }, "_all": 1, "csv": Object { "available": true, + "deprecated": 0, "total": 0, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "printable_pdf": Object { @@ -108,6 +869,7 @@ Object { "visualization": 0, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 0, "print": 0, @@ -134,6 +896,7 @@ Object { "visualization": 3, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 9, "print": 0, @@ -173,6 +936,7 @@ exports[`data modeling with sparse data 1`] = ` Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 1, }, "_all": 4, @@ -180,25 +944,30 @@ Object { "browser_type": undefined, "csv": Object { "available": true, + "deprecated": 1, "total": 1, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "enabled": true, "last7Days": Object { "PNG": Object { "available": true, + "deprecated": 0, "total": 1, }, "_all": 4, "csv": Object { "available": true, + "deprecated": 1, "total": 1, }, "csv_searchsource": Object { "available": true, + "deprecated": 0, "total": 0, }, "printable_pdf": Object { @@ -208,6 +977,7 @@ Object { "visualization": 0, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 2, "print": 0, @@ -238,6 +1008,7 @@ Object { "visualization": 0, }, "available": true, + "deprecated": 0, "layout": Object { "preserve_layout": 2, "print": 0, diff --git a/x-pack/plugins/reporting/server/usage/decorate_range_stats.test.ts b/x-pack/plugins/reporting/server/usage/decorate_range_stats.test.ts new file mode 100644 index 0000000000000..ca1677c2379fc --- /dev/null +++ b/x-pack/plugins/reporting/server/usage/decorate_range_stats.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { decorateRangeStats } from './decorate_range_stats'; +import { FeatureAvailabilityMap } from './types'; + +let featureMap: FeatureAvailabilityMap; + +beforeEach(() => { + featureMap = { PNG: true, csv: true, csv_searchsource: true, printable_pdf: true }; +}); + +test('Model of job status and status-by-pdf-app', () => { + const result = decorateRangeStats( + { + status: { completed: 0, processing: 1, pending: 2, failed: 3 }, + statuses: { + processing: { printable_pdf: { 'canvas workpad': 1 } }, + pending: { printable_pdf: { dashboard: 1, 'canvas workpad': 1 } }, + failed: { printable_pdf: { visualization: 2, dashboard: 2, 'canvas workpad': 1 } }, + }, + }, + featureMap + ); + + expect(result.status).toMatchInlineSnapshot(` + Object { + "completed": 0, + "failed": 3, + "pending": 2, + "processing": 1, + } + `); + expect(result.statuses).toMatchInlineSnapshot(` + Object { + "failed": Object { + "printable_pdf": Object { + "canvas workpad": 1, + "dashboard": 2, + "visualization": 2, + }, + }, + "pending": Object { + "printable_pdf": Object { + "canvas workpad": 1, + "dashboard": 1, + }, + }, + "processing": Object { + "printable_pdf": Object { + "canvas workpad": 1, + }, + }, + } + `); +}); + +test('Model of jobTypes', () => { + const result = decorateRangeStats( + { + PNG: { available: true, total: 3 }, + printable_pdf: { + available: true, + total: 3, + app: { dashboard: 0, visualization: 0, 'canvas workpad': 3 }, + layout: { preserve_layout: 3, print: 0 }, + }, + csv_searchsource: { available: true, total: 3 }, + }, + featureMap + ); + + expect(result.PNG).toMatchInlineSnapshot(` + Object { + "available": true, + "deprecated": 0, + "total": 3, + } + `); + expect(result.csv).toMatchInlineSnapshot(` + Object { + "available": true, + "deprecated": 0, + "total": 0, + } + `); + expect(result.csv_searchsource).toMatchInlineSnapshot(` + Object { + "available": true, + "deprecated": 0, + "total": 3, + } + `); + expect(result.printable_pdf).toMatchInlineSnapshot(` + Object { + "app": Object { + "canvas workpad": 3, + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "deprecated": 0, + "layout": Object { + "preserve_layout": 3, + "print": 0, + }, + "total": 3, + } + `); +}); + +test('PNG counts, provided count of deprecated jobs explicitly', () => { + const result = decorateRangeStats( + { PNG: { available: true, total: 15, deprecated: 5 } }, + featureMap + ); + expect(result.PNG).toMatchInlineSnapshot(` + Object { + "available": true, + "deprecated": 5, + "total": 15, + } + `); +}); + +test('CSV counts, provides all jobs implicitly deprecated due to jobtype', () => { + const result = decorateRangeStats( + { csv: { available: true, total: 15, deprecated: 0 } }, + featureMap + ); + expect(result.csv).toMatchInlineSnapshot(` + Object { + "available": true, + "deprecated": 15, + "total": 15, + } + `); +}); diff --git a/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts index 9fc0141ab742e..99d4b7d934579 100644 --- a/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/plugins/reporting/server/usage/decorate_range_stats.ts @@ -9,11 +9,14 @@ import { uniq } from 'lodash'; import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED, + DEPRECATED_JOB_TYPES, PDF_JOB_TYPE, PNG_JOB_TYPE, } from '../../common/constants'; import { AvailableTotal, ExportType, FeatureAvailabilityMap, RangeStats } from './types'; +const jobTypeIsDeprecated = (jobType: string) => DEPRECATED_JOB_TYPES.includes(jobType); + function getForFeature( range: Partial, typeKey: ExportType, @@ -21,7 +24,7 @@ function getForFeature( additional?: any ): AvailableTotal & typeof additional { const isAvailable = (feature: ExportType) => !!featureAvailability[feature]; - const jobType = range[typeKey] || { total: 0, ...additional }; + const jobType = range[typeKey] || { total: 0, ...additional, deprecated: 0 }; // merge the additional stats for the jobType type AdditionalType = { [K in keyof typeof additional]: K }; @@ -32,9 +35,13 @@ function getForFeature( }); } + // if the type itself is deprecated, all jobs are deprecated, otherwise only some of them might be + const deprecated = jobTypeIsDeprecated(typeKey) ? jobType.total : jobType.deprecated || 0; + return { available: isAvailable(typeKey), total: jobType.total, + deprecated, ...filledAdditional, }; } diff --git a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts index f07da188f3e62..e5801b30caff6 100644 --- a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts @@ -5,21 +5,21 @@ * 2.0. */ import type { estypes } from '@elastic/elasticsearch'; +import type { ElasticsearchClient } from 'kibana/server'; import { get } from 'lodash'; -import { ElasticsearchClient } from 'kibana/server'; -import { ReportingConfig } from '../'; -import { ExportTypesRegistry } from '../lib/export_types_registry'; -import { GetLicense } from './'; +import type { ReportingConfig } from '../'; +import type { ExportTypesRegistry } from '../lib/export_types_registry'; +import type { GetLicense } from './'; import { decorateRangeStats } from './decorate_range_stats'; import { getExportTypesHandler } from './get_export_type_handler'; -import { +import type { AggregationResultBuckets, + AvailableTotal, FeatureAvailabilityMap, JobTypes, KeyCountBucket, RangeStats, ReportingUsageType, - // ReportingUsageSearchResponse, StatusByAppBucket, } from './types'; @@ -28,6 +28,7 @@ const JOB_TYPES_FIELD = 'jobtype'; const LAYOUT_TYPES_KEY = 'layoutTypes'; const LAYOUT_TYPES_FIELD = 'meta.layout.keyword'; const OBJECT_TYPES_KEY = 'objectTypes'; +const OBJECT_TYPE_DEPRECATED_KEY = 'meta.isDeprecated'; const OBJECT_TYPES_FIELD = 'meta.objectType.keyword'; const STATUS_TYPES_KEY = 'statusTypes'; const STATUS_BY_APP_KEY = 'statusByApp'; @@ -64,12 +65,15 @@ const getAppStatuses = (buckets: StatusByAppBucket[]) => function getAggStats(aggs: AggregationResultBuckets): Partial { const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY]; - const jobTypes = jobBuckets.reduce( - (accum: JobTypes, { key, doc_count: count }: { key: string; doc_count: number }) => { - return { ...accum, [key]: { total: count } }; - }, - {} as JobTypes - ); + const jobTypes = jobBuckets.reduce((accum: JobTypes, bucket) => { + const { key, doc_count: count, isDeprecated } = bucket; + const deprecatedCount = isDeprecated?.doc_count; + const total: Omit = { + total: count, + deprecated: deprecatedCount, + }; + return { ...accum, [key]: total }; + }, {} as JobTypes); // merge pdf stats into pdf jobtype key const pdfJobs = jobTypes[PRINTABLE_PDF_JOBTYPE]; @@ -141,7 +145,10 @@ export async function getReportingUsage( }, }, aggs: { - [JOB_TYPES_KEY]: { terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, + [JOB_TYPES_KEY]: { + terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE }, + aggs: { isDeprecated: { filter: { term: { [OBJECT_TYPE_DEPRECATED_KEY]: true } } } }, + }, [STATUS_TYPES_KEY]: { terms: { field: STATUS_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, [STATUS_BY_APP_KEY]: { terms: { field: 'status', size: DEFAULT_TERMS_SIZE }, diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 226704b255ab3..72824f6aeeb38 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -238,6 +238,147 @@ describe('data modeling', () => { expect(usageStats).toMatchSnapshot(); }); + test('usage data with meta.isDeprecated jobTypes', async () => { + const plugins = getPluginsMock(); + const collector = getReportingUsageCollector( + mockCore, + plugins.usageCollection, + getLicenseMock(), + exportTypesRegistry, + function isReady() { + return Promise.resolve(true); + } + ); + collectorFetchContext = getMockFetchClients( + getResponseMock({ + aggregations: { + ranges: { + buckets: { + all: { + doc_count: 9, + layoutTypes: { + doc_count: 0, + pdf: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + statusByApp: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'completed', + doc_count: 9, + jobTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'csv_searchsource', + doc_count: 5, + appNames: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'search', doc_count: 5 }], + }, + }, + { + key: 'csv', + doc_count: 4, + appNames: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'search', doc_count: 4 }], + }, + }, + ], + }, + }, + ], + }, + objectTypes: { + doc_count: 0, + pdf: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + statusTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'completed', doc_count: 9 }], + }, + jobTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'csv_searchsource', doc_count: 5, isDeprecated: { doc_count: 0 } }, + { key: 'csv', doc_count: 4, isDeprecated: { doc_count: 4 } }, + ], + }, + }, + last7Days: { + doc_count: 9, + layoutTypes: { + doc_count: 0, + pdf: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + statusByApp: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'completed', + doc_count: 9, + jobTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'csv_searchsource', + doc_count: 5, + appNames: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'search', doc_count: 5 }], + }, + }, + { + key: 'csv', + doc_count: 4, + appNames: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'search', doc_count: 4 }], + }, + }, + ], + }, + }, + ], + }, + objectTypes: { + doc_count: 0, + pdf: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + statusTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'completed', doc_count: 9 }], + }, + jobTypes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'csv_searchsource', doc_count: 5, isDeprecated: { doc_count: 0 } }, + { key: 'csv', doc_count: 4, isDeprecated: { doc_count: 4 } }, + ], + }, + }, + }, + }, + }, + }) + ); + const usageStats = await collector.fetch(collectorFetchContext); + expect(usageStats).toMatchSnapshot(); + }); + test('with sparse data', async () => { const plugins = getPluginsMock(); const collector = getReportingUsageCollector( @@ -462,730 +603,7 @@ describe('Ready for collection observable', () => { registerReportingUsageCollector(mockReporting, plugins); const [args] = makeCollectorSpy.firstCall.args; - expect(args).toMatchInlineSnapshot(` - Object { - "fetch": [Function], - "isReady": [Function], - "schema": Object { - "PNG": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "_all": Object { - "type": "long", - }, - "available": Object { - "type": "boolean", - }, - "browser_type": Object { - "type": "keyword", - }, - "csv": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "enabled": Object { - "type": "boolean", - }, - "last7Days": Object { - "PNG": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "_all": Object { - "type": "long", - }, - "csv": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "available": Object { - "type": "boolean", - }, - "total": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "app": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "available": Object { - "type": "boolean", - }, - "layout": Object { - "preserve_layout": Object { - "type": "long", - }, - "print": Object { - "type": "long", - }, - }, - "total": Object { - "type": "long", - }, - }, - "status": Object { - "cancelled": Object { - "type": "long", - }, - "completed": Object { - "type": "long", - }, - "completed_with_warnings": Object { - "type": "long", - }, - "failed": Object { - "type": "long", - }, - "pending": Object { - "type": "long", - }, - "processing": Object { - "type": "long", - }, - }, - "statuses": Object { - "cancelled": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "completed": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "completed_with_warnings": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "failed": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "pending": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "processing": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - }, - }, - "printable_pdf": Object { - "app": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "available": Object { - "type": "boolean", - }, - "layout": Object { - "preserve_layout": Object { - "type": "long", - }, - "print": Object { - "type": "long", - }, - }, - "total": Object { - "type": "long", - }, - }, - "status": Object { - "cancelled": Object { - "type": "long", - }, - "completed": Object { - "type": "long", - }, - "completed_with_warnings": Object { - "type": "long", - }, - "failed": Object { - "type": "long", - }, - "pending": Object { - "type": "long", - }, - "processing": Object { - "type": "long", - }, - }, - "statuses": Object { - "cancelled": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "completed": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "completed_with_warnings": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "failed": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "pending": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - "processing": Object { - "PNG": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "csv_searchsource": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - "printable_pdf": Object { - "canvas workpad": Object { - "type": "long", - }, - "dashboard": Object { - "type": "long", - }, - "visualization": Object { - "type": "long", - }, - }, - }, - }, - }, - "type": "reporting", - } - `); + expect(args).toMatchSnapshot(); await expect(args.isReady()).resolves.toBe(true); }); diff --git a/x-pack/plugins/reporting/server/usage/schema.ts b/x-pack/plugins/reporting/server/usage/schema.ts index 8528543b09e07..2060fdcb1f01e 100644 --- a/x-pack/plugins/reporting/server/usage/schema.ts +++ b/x-pack/plugins/reporting/server/usage/schema.ts @@ -6,7 +6,14 @@ */ import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; -import { AppCounts, AvailableTotal, JobTypes, RangeStats, ReportingUsageType } from './types'; +import { + AppCounts, + AvailableTotal, + ByAppCounts, + JobTypes, + RangeStats, + ReportingUsageType, +} from './types'; const appCountsSchema: MakeSchemaFrom = { 'canvas workpad': { type: 'long' }, @@ -14,7 +21,7 @@ const appCountsSchema: MakeSchemaFrom = { visualization: { type: 'long' }, }; -const byAppCountsSchema: MakeSchemaFrom = { +const byAppCountsSchema: MakeSchemaFrom = { csv: appCountsSchema, csv_searchsource: appCountsSchema, PNG: appCountsSchema, @@ -24,6 +31,7 @@ const byAppCountsSchema: MakeSchemaFrom = { const availableTotalSchema: MakeSchemaFrom = { available: { type: 'boolean' }, total: { type: 'long' }, + deprecated: { type: 'long' }, }; const jobTypesSchema: MakeSchemaFrom = { @@ -44,7 +52,6 @@ const rangeStatsSchema: MakeSchemaFrom = { ...jobTypesSchema, _all: { type: 'long' }, status: { - cancelled: { type: 'long' }, completed: { type: 'long' }, completed_with_warnings: { type: 'long' }, failed: { type: 'long' }, @@ -52,7 +59,6 @@ const rangeStatsSchema: MakeSchemaFrom = { processing: { type: 'long' }, }, statuses: { - cancelled: byAppCountsSchema, completed: byAppCountsSchema, completed_with_warnings: byAppCountsSchema, failed: byAppCountsSchema, diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index 58def60a24ccb..aae8c0ff42710 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -8,6 +8,9 @@ export interface KeyCountBucket { key: string; doc_count: number; + isDeprecated?: { + doc_count: number; + }; } export interface AggregationBuckets { @@ -57,9 +60,11 @@ export interface SearchResponse { export interface AvailableTotal { available: boolean; total: number; + deprecated?: number; } type BaseJobTypes = 'csv' | 'csv_searchsource' | 'PNG' | 'printable_pdf'; + export interface LayoutCounts { print: number; preserve_layout: number; @@ -77,20 +82,15 @@ export type JobTypes = { [K in BaseJobTypes]: AvailableTotal } & { }; }; -type Statuses = - | 'cancelled' - | 'completed' - | 'completed_with_warnings' - | 'failed' - | 'pending' - | 'processing'; +export type ByAppCounts = { [J in BaseJobTypes]?: AppCounts }; + +type Statuses = 'completed' | 'completed_with_warnings' | 'failed' | 'pending' | 'processing'; + type StatusCounts = { [S in Statuses]?: number; }; type StatusByAppCounts = { - [S in Statuses]?: { - [J in BaseJobTypes]?: AppCounts; - }; + [S in Statuses]?: ByAppCounts; }; export type RangeStats = JobTypes & { @@ -109,44 +109,6 @@ export type ReportingUsageType = RangeStats & { export type ExportType = 'csv' | 'csv_searchsource' | 'printable_pdf' | 'PNG'; export type FeatureAvailabilityMap = { [F in ExportType]: boolean }; -export interface KeyCountBucket { - key: string; - doc_count: number; -} - -export interface AggregationBuckets { - buckets: KeyCountBucket[]; -} - -export interface StatusByAppBucket { - key: string; - doc_count: number; - jobTypes: { - buckets: Array<{ - doc_count: number; - key: string; - appNames: AggregationBuckets; - }>; - }; -} - -export interface AggregationResultBuckets { - jobTypes: AggregationBuckets; - layoutTypes: { - doc_count: number; - pdf: AggregationBuckets; - }; - objectTypes: { - doc_count: number; - pdf: AggregationBuckets; - }; - statusTypes: AggregationBuckets; - statusByApp: { - buckets: StatusByAppBucket[]; - }; - doc_count: number; -} - export interface ReportingUsageSearchResponse { aggregations: { ranges: { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index d2a8b914d10b6..090978949e2fd 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -650,6 +650,8 @@ export class AlertsClient { public async getAuthorizedAlertsIndices(featureIds: string[]): Promise { try { + // ATTENTION FUTURE DEVELOPER when you are a super user the augmentedRuleTypes.authorizedRuleTypes will + // return all of the features that you can access and does not care about your featureIds const augmentedRuleTypes = await this.authorization.getAugmentedRuleTypesWithAuthorization( featureIds, [ReadOperations.Find, ReadOperations.Get, WriteOperations.Update], @@ -665,7 +667,7 @@ export class AlertsClient { } const toReturn = Array.from(authorizedFeatures).flatMap((feature) => { - if (isValidFeatureId(feature)) { + if (featureIds.includes(feature) && isValidFeatureId(feature)) { if (feature === 'siem') { return `${mapConsumerToIndexName[feature]}-${this.spaceId}`; } else { diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts index 467d6816d0443..f95ed8c6994b6 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts @@ -12,6 +12,7 @@ type Schema = PublicMethodsOf; const createRuleDataPluginService = () => { const mocked: jest.Mocked = { + getRegisteredIndexInfo: jest.fn(), getResourcePrefix: jest.fn(), getResourceName: jest.fn(), isWriteEnabled: jest.fn(), diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts index b95effd4801b9..734518e86d172 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts @@ -29,6 +29,7 @@ export class RuleDataPluginService { private readonly resourceInstaller: ResourceInstaller; private installCommonResources: Promise>; private isInitialized: boolean; + private registeredIndices: Map = new Map(); constructor(private readonly options: ConstructorOptions) { this.resourceInstaller = new ResourceInstaller({ @@ -105,6 +106,8 @@ export class RuleDataPluginService { indexOptions, }); + this.registeredIndices.set(indexOptions.registrationContext, indexInfo); + const waitUntilClusterClientAvailable = async (): Promise => { try { const clusterClient = await this.options.getClusterClient(); @@ -148,4 +151,13 @@ export class RuleDataPluginService { waitUntilReadyForWriting, }); } + + /** + * Looks up the index information associated with the given `registrationContext`. + * @param registrationContext + * @returns the IndexInfo or undefined + */ + public getRegisteredIndexInfo(registrationContext: string): IndexInfo | undefined { + return this.registeredIndices.get(registrationContext); + } } diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts index 236f3b41d689d..2d0ca3e328a13 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { elasticsearchServiceMock, savedObjectsClientMock, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index e6db167d8c6b6..abdffd2aa03cc 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -24,16 +24,17 @@ import { ALERT_DURATION, ALERT_END, ALERT_ID, + ALERT_RULE_CONSUMER, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, ALERT_START, ALERT_STATUS, ALERT_UUID, + ALERT_WORKFLOW_STATUS, EVENT_ACTION, EVENT_KIND, - ALERT_RULE_CONSUMER, - ALERT_RULE_TYPE_ID, - ALERT_RULE_UUID, - TIMESTAMP, SPACE_IDS, + TIMESTAMP, } from '../../common/technical_rule_data_field_names'; import { IRuleDataClient } from '../rule_data_client'; import { AlertExecutorOptionsWithExtraServices } from '../types'; @@ -263,6 +264,7 @@ export const createLifecycleExecutor = ( event[ALERT_START] = started; event[ALERT_UUID] = alertUuid; + event[ALERT_WORKFLOW_STATUS] = event[ALERT_WORKFLOW_STATUS] ?? 'open'; // not sure why typescript needs the non-null assertion here // we already assert the value is not undefined with the ternary diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index 962a37a2991b0..7d798effcb9e5 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { ALERT_DURATION, ALERT_STATUS, ALERT_UUID } from '@kbn/rule-data-utils'; -import { loggerMock } from '@kbn/logging/target/mocks'; +import { loggerMock } from '@kbn/logging/mocks'; import { castArray, omit, mapValues } from 'lodash'; import { RuleDataClient } from '../rule_data_client'; import { createRuleDataClientMock } from '../rule_data_client/rule_data_client.mock'; @@ -199,6 +199,7 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.alert.rule.uuid": "alertId", "kibana.alert.start": "2021-06-16T09:01:00.000Z", "kibana.alert.status": "open", + "kibana.alert.workflow_status": "open", "kibana.space_ids": Array [ "spaceId", ], @@ -221,6 +222,7 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.alert.rule.uuid": "alertId", "kibana.alert.start": "2021-06-16T09:01:00.000Z", "kibana.alert.status": "open", + "kibana.alert.workflow_status": "open", "kibana.space_ids": Array [ "spaceId", ], diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index ef1338ab9d971..3a66b6d80c615 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -35,12 +35,11 @@ import type { ScopedHistory, } from 'src/core/public'; import type { IndexPatternsContract } from 'src/plugins/data/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public'; import type { KibanaFeature } from '../../../../../features/common'; import type { FeaturesPluginStart } from '../../../../../features/public'; -import type { Space } from '../../../../../spaces/public'; +import type { Space, SpacesApiUi } from '../../../../../spaces/public'; import type { SecurityLicense } from '../../../../common/licensing'; import type { BuiltinESPrivileges, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx index 486b1c8bc1d03..c9c7df222df29 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx @@ -8,9 +8,8 @@ import React, { Component } from 'react'; import type { Capabilities } from 'src/core/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; -import type { Space } from '../../../../../../../spaces/public'; +import type { Space, SpacesApiUi } from '../../../../../../../spaces/public'; import type { Role } from '../../../../../../common/model'; import type { KibanaPrivileges } from '../../../model'; import { CollapsiblePanel } from '../../collapsible_panel'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx index 48a0d18653053..27bf246c0596f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx @@ -17,9 +17,8 @@ import { import React, { Fragment, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; -import type { Space } from '../../../../../../../../spaces/public'; +import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public'; import type { Role } from '../../../../../../../common/model'; import type { KibanaPrivileges } from '../../../../model'; import { PrivilegeSummaryTable } from './privilege_summary_table'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx index 582a7d6c5427e..56c841f68f504 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx @@ -20,9 +20,8 @@ import { import React, { Fragment, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; -import type { Space } from '../../../../../../../../spaces/public'; +import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public'; import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; import type { KibanaPrivileges, SecuredFeature } from '../../../../model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx index fd535d20de557..38c122ba10086 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx @@ -9,9 +9,8 @@ import React, { Fragment, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; +import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public'; import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { SpacesPopoverList } from '../../../spaces_popover_list'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index 9ca41a018cd33..6492ca6e01af0 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -20,9 +20,8 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { Capabilities } from 'src/core/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; -import type { Space } from '../../../../../../../../spaces/public'; +import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public'; import type { Role } from '../../../../../../../common/model'; import { isRoleReserved } from '../../../../../../../common/model'; import type { KibanaPrivileges } from '../../../../model'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx index 2925866a5752f..fb21fac3006b8 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx @@ -17,8 +17,8 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { coreMock } from 'src/core/public/mocks'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../../../../spaces/public'; import { SpaceAvatarInternal } from '../../../../../../spaces/public/space_avatar/space_avatar_internal'; import { spacesManagerMock } from '../../../../../../spaces/public/spaces_manager/mocks'; import { getUiApi } from '../../../../../../spaces/public/ui_api'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx index 9861b008beb9f..e715cb217ae67 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx @@ -19,10 +19,9 @@ import React, { Component, memo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../spaces/common'; +import type { Space, SpacesApiUi } from '../../../../../../spaces/public'; interface Props { spaces: Space[]; diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index e5a2340aba3f0..2f622d9e8a0e1 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -779,17 +779,6 @@ describe('#find', () => { ); }); - test(`throws BadRequestError when searching across namespaces when pit is provided`, async () => { - const options = { - type: [type1, type2], - pit: { id: 'abc123' }, - namespaces: ['some-ns', 'another-ns'], - }; - await expect(client.find(options)).rejects.toThrowErrorMatchingInlineSnapshot( - `"_find across namespaces is not permitted when using the \`pit\` option."` - ); - }); - test(`checks privileges for user, actions, and namespaces`, async () => { const options = { type: [type1, type2], namespaces }; await expectPrivilegeCheck(client.find, { options }, namespaces); @@ -884,7 +873,7 @@ describe('#openPointInTimeForType', () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.openPointInTimeForType.mockReturnValue(apiCallReturnValue as any); - const options = { namespace }; + const options = { namespaces: [namespace] }; const result = await expectSuccess(client.openPointInTimeForType, { type, options }); expect(result).toBe(apiCallReturnValue); }); @@ -892,18 +881,113 @@ describe('#openPointInTimeForType', () => { test(`adds audit event when successful`, async () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.openPointInTimeForType.mockReturnValue(apiCallReturnValue as any); - const options = { namespace }; + const options = { namespaces: [namespace] }; await expectSuccess(client.openPointInTimeForType, { type, options }); expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1); expectAuditEvent('saved_object_open_point_in_time', 'unknown'); }); - test(`adds audit event when not successful`, async () => { - clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error()); - await expect(() => client.openPointInTimeForType(type, { namespace })).rejects.toThrow(); + test(`throws an error when unauthorized`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( + getMockCheckPrivilegesFailure + ); + const options = { namespaces: [namespace] }; + await expect(() => client.openPointInTimeForType(type, options)).rejects.toThrowError( + 'unauthorized' + ); + }); + + test(`adds audit event when unauthorized`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( + getMockCheckPrivilegesFailure + ); + const options = { namespaces: [namespace] }; + await expect(() => client.openPointInTimeForType(type, options)).rejects.toThrow(); expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1); expectAuditEvent('saved_object_open_point_in_time', 'failure'); }); + + test(`filters types based on authorization`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockResolvedValue({ + hasAllRequested: false, + username: USERNAME, + privileges: { + kibana: [ + { + resource: 'some-ns', + privilege: 'mock-saved_object:foo/open_point_in_time', + authorized: true, + }, + { + resource: 'some-ns', + privilege: 'mock-saved_object:bar/open_point_in_time', + authorized: true, + }, + { + resource: 'some-ns', + privilege: 'mock-saved_object:baz/open_point_in_time', + authorized: false, + }, + { + resource: 'some-ns', + privilege: 'mock-saved_object:qux/open_point_in_time', + authorized: false, + }, + { + resource: 'another-ns', + privilege: 'mock-saved_object:foo/open_point_in_time', + authorized: true, + }, + { + resource: 'another-ns', + privilege: 'mock-saved_object:bar/open_point_in_time', + authorized: false, + }, + { + resource: 'another-ns', + privilege: 'mock-saved_object:baz/open_point_in_time', + authorized: true, + }, + { + resource: 'another-ns', + privilege: 'mock-saved_object:qux/open_point_in_time', + authorized: false, + }, + { + resource: 'forbidden-ns', + privilege: 'mock-saved_object:foo/open_point_in_time', + authorized: false, + }, + { + resource: 'forbidden-ns', + privilege: 'mock-saved_object:bar/open_point_in_time', + authorized: false, + }, + { + resource: 'forbidden-ns', + privilege: 'mock-saved_object:baz/open_point_in_time', + authorized: false, + }, + { + resource: 'forbidden-ns', + privilege: 'mock-saved_object:qux/open_point_in_time', + authorized: false, + }, + ], + }, + }); + + await client.openPointInTimeForType(['foo', 'bar', 'baz', 'qux'], { + namespaces: ['some-ns', 'another-ns', 'forbidden-ns'], + }); + + expect(clientOpts.baseClient.openPointInTimeForType).toHaveBeenCalledWith( + ['foo', 'bar', 'baz'], + { + namespaces: ['some-ns', 'another-ns', 'forbidden-ns'], + } + ); + }); }); describe('#closePointInTime', () => { diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts index a3bd215211983..6f2b8d28a5601 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts @@ -29,7 +29,7 @@ import type { SavedObjectsUpdateOptions, } from 'src/core/server'; -import { SavedObjectsUtils } from '../../../../../src/core/server'; +import { SavedObjectsErrorHelpers, SavedObjectsUtils } from '../../../../../src/core/server'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; import type { AuditLogger, SecurityAuditLogger } from '../audit'; import { SavedObjectAction, savedObjectEvent } from '../audit'; @@ -75,10 +75,12 @@ interface LegacyEnsureAuthorizedResult { status: 'fully_authorized' | 'partially_authorized' | 'unauthorized'; typeMap: Map; } + interface LegacyEnsureAuthorizedTypeResult { authorizedSpaces: string[]; isGloballyAuthorized?: boolean; } + export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContract { private readonly actions: Actions; private readonly legacyAuditLogger: PublicMethodsOf; @@ -236,11 +238,6 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra `_find across namespaces is not permitted when the Spaces plugin is disabled.` ); } - if (options.pit && Array.isArray(options.namespaces) && options.namespaces.length > 1) { - throw this.errors.createBadRequestError( - '_find across namespaces is not permitted when using the `pit` option.' - ); - } const args = { options }; const { status, typeMap } = await this.legacyEnsureAuthorized( @@ -508,22 +505,27 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra type: string | string[], options: SavedObjectsOpenPointInTimeOptions ) { - try { - const args = { type, options }; - await this.legacyEnsureAuthorized(type, 'open_point_in_time', options?.namespace, { + const args = { type, options }; + const { status, typeMap } = await this.legacyEnsureAuthorized( + type, + 'open_point_in_time', + options?.namespaces, + { args, // Partial authorization is acceptable in this case because this method is only designed // to be used with `find`, which already allows for partial authorization. requireFullAuthorization: false, - }); - } catch (error) { + } + ); + + if (status === 'unauthorized') { this.auditLogger.log( savedObjectEvent({ action: SavedObjectAction.OPEN_POINT_IN_TIME, - error, + error: new Error(status), }) ); - throw error; + throw SavedObjectsErrorHelpers.decorateForbiddenError(new Error(status)); } this.auditLogger.log( @@ -533,7 +535,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra }) ); - return await this.baseClient.openPointInTimeForType(type, options); + const allowedTypes = [...typeMap.keys()]; // only allow the user to open a PIT against indices for type(s) they are authorized to access + return await this.baseClient.openPointInTimeForType(allowedTypes, options); } public async closePointInTime(id: string, options?: SavedObjectsClosePointInTimeOptions) { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/acknowledged.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/acknowledged.spec.ts index f933c5a4ed0a2..d81c444824a2a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/acknowledged.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/acknowledged.spec.ts @@ -26,7 +26,7 @@ import { refreshPage } from '../../tasks/security_header'; import { ALERTS_URL } from '../../urls/navigation'; -describe('Marking alerts as acknowledged', () => { +describe.skip('Marking alerts as acknowledged', () => { beforeEach(() => { cleanKibana(); loginAndWaitForPage(ALERTS_URL); @@ -53,7 +53,7 @@ describe('Marking alerts as acknowledged', () => { refreshPage(); waitForAlertsToBeLoaded(); goToOpenedAlerts(); - + waitForAlertsToBeLoaded(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedAcknowledged; cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alerts`); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index c255702e8de86..3ec616127f243 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { getCaseDetailsUrl, getCaseDetailsUrlWithCommentId, @@ -53,14 +52,11 @@ export interface CaseProps extends Props { updateCase: (newCase: Case) => void; } -const SECURITY_SOLUTION_ALERT_CONSUMERS: AlertConsumers[] = [AlertConsumers.SIEM]; - -const TimelineDetailsPanel = ({ alertConsumers }: { alertConsumers?: AlertConsumers[] }) => { +const TimelineDetailsPanel = () => { const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.detections); return ( { wrapper.find(`[data-test-subj="legend-item-${legendItem.dataProviderId}"]`).first().text() ).toEqual(legendItem.value); }); + + it('always hides the Top N action for legend items', () => { + expect( + wrapper.find(`[data-test-subj="legend-item-${legendItem.dataProviderId}"]`).prop('hideTopN') + ).toEqual(true); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx index b4b12437f8660..0cf580db67237 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx @@ -36,6 +36,7 @@ const DraggableLegendItemComponent: React.FC<{ = ({ dataProvider, + hideTopN = false, onFilterAdded, render, timelineId, @@ -147,6 +149,7 @@ const DraggableOnWrapperComponent: React.FC = ({ showTopN, } = useHoverActions({ dataProvider, + hideTopN, onFilterAdded, render, timelineId, @@ -304,6 +307,7 @@ const DraggableOnWrapperComponent: React.FC = ({ const DraggableWrapperComponent: React.FC = ({ dataProvider, + hideTopN = false, isDraggable = false, onFilterAdded, render, @@ -319,6 +323,7 @@ const DraggableWrapperComponent: React.FC = ({ showTopN, } = useHoverActions({ dataProvider, + hideTopN, isDraggable, onFilterAdded, render, @@ -363,6 +368,7 @@ const DraggableWrapperComponent: React.FC = ({ return ( { allowTopN({ browserField: aggregatableAllowedType, fieldName: aggregatableAllowedType.name, + hideTopN: false, }) ).toBe(true); }); @@ -664,6 +665,7 @@ describe('helpers', () => { allowTopN({ browserField: undefined, fieldName: 'signal.rule.name', + hideTopN: false, }) ).toBe(true); }); @@ -678,6 +680,7 @@ describe('helpers', () => { allowTopN({ browserField: nonAggregatableAllowedType, fieldName: nonAggregatableAllowedType.name, + hideTopN: false, }) ).toBe(false); }); @@ -692,6 +695,7 @@ describe('helpers', () => { allowTopN({ browserField: aggregatableNotAllowedType, fieldName: aggregatableNotAllowedType.name, + hideTopN: false, }) ).toBe(false); }); @@ -703,6 +707,7 @@ describe('helpers', () => { allowTopN({ browserField: missingAggregatable, fieldName: missingAggregatable.name, + hideTopN: false, }) ).toBe(false); }); @@ -714,6 +719,7 @@ describe('helpers', () => { allowTopN({ browserField: missingType, fieldName: missingType.name, + hideTopN: false, }) ).toBe(false); }); @@ -723,6 +729,17 @@ describe('helpers', () => { allowTopN({ browserField: undefined, fieldName: 'non-allowlisted', + hideTopN: false, + }) + ).toBe(false); + }); + + test('it returns false when hideTopN is true', () => { + expect( + allowTopN({ + browserField: aggregatableAllowedType, + fieldName: aggregatableAllowedType.name, + hideTopN: true, // <-- the Top N action shall not be shown for this (otherwise valid) field }) ).toBe(false); }); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/helpers.ts b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/helpers.ts index 9717e1e1eda91..bca6c15d86140 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/helpers.ts @@ -92,9 +92,11 @@ export const addProviderToTimeline = ({ export const allowTopN = ({ browserField, fieldName, + hideTopN, }: { browserField: Partial | undefined; fieldName: string; + hideTopN: boolean; }): boolean => { const isAggregatable = browserField?.aggregatable ?? false; const fieldType = browserField?.type ?? ''; @@ -181,5 +183,9 @@ export const allowTopN = ({ 'signal.status', ].includes(fieldName); + if (hideTopN) { + return false; + } + return isAllowlistedNonBrowserField || (isAggregatable && isAllowedType); }; diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap index 6b27cf5969f1a..3cbb0d27a0e2f 100644 --- a/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap @@ -36,6 +36,7 @@ exports[`draggables rendering it renders the default DefaultDraggable 1`] = ` }, } } + hideTopN={false} isDraggable={true} render={[Function]} /> diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx index 6ac1746d77709..e33a8e42e6a39 100644 --- a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx @@ -19,6 +19,7 @@ import { import { Provider } from '../../../timelines/components/timeline/data_providers/provider'; export interface DefaultDraggableType { + hideTopN?: boolean; id: string; isDraggable?: boolean; field: string; @@ -88,9 +89,11 @@ Content.displayName = 'Content'; * @param tooltipContent - defaults to displaying `field`, pass `null` to * prevent a tooltip from being displayed, or pass arbitrary content * @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data + * @param hideTopN - defaults to `false`, when true, the option to aggregate this field will be hidden */ export const DefaultDraggable = React.memo( ({ + hideTopN = false, id, isDraggable = true, field, @@ -137,6 +140,7 @@ export const DefaultDraggable = React.memo( return ( React.ReactNode; additionalFilters?: React.ReactNode; hasAlertsCrud?: boolean; + unit?: (n: number) => string; } type Props = OwnProps & PropsFromRedux; @@ -105,6 +106,7 @@ const StatefulEventsViewerComponent: React.FC = ({ // If truthy, the graph viewer (Resolver) is showing graphEventId, hasAlertsCrud = false, + unit, }) => { const { timelines: timelinesUi } = useKibana().services; const { @@ -187,6 +189,7 @@ const StatefulEventsViewerComponent: React.FC = ({ leadingControlColumns, trailingControlColumns, tGridEventRenderedViewEnabled, + unit, }) ) : ( ` - min-width: 138px; + min-width: ${({ $hideTopN }) => `${$hideTopN ? '112px' : '138px'}`}; padding: ${(props) => `0 ${props.theme.eui.paddingSizes.s}`}; display: flex; @@ -91,6 +92,7 @@ interface Props { enableOverflowButton?: boolean; field: string; goGetTimelineId?: (args: boolean) => void; + hideTopN?: boolean; isObjectArray: boolean; onFilterAdded?: () => void; ownFocus: boolean; @@ -129,6 +131,7 @@ export const HoverActions: React.FC = React.memo( field, goGetTimelineId, isObjectArray, + hideTopN = false, onFilterAdded, ownFocus, showOwnFocus = true, @@ -207,6 +210,7 @@ export const HoverActions: React.FC = React.memo( enableOverflowButton, field, handleHoverActionClicked, + hideTopN, isObjectArray, isOverflowPopoverOpen, onFilterAdded, @@ -231,6 +235,7 @@ export const HoverActions: React.FC = React.memo( onKeyDown={onKeyDown} $showTopN={showTopN} $showOwnFocus={showOwnFocus} + $hideTopN={hideTopN} $isActive={isActive} className={isActive ? 'hoverActions-active' : ''} > diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.test.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.test.tsx index 2ef72571cf307..b70d520f14219 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.test.tsx @@ -22,6 +22,7 @@ describe('useHoverActionItems', () => { defaultFocusedButtonRef: null, field: 'signal.rule.name', handleHoverActionClicked: jest.fn(), + hideTopN: false, isObjectArray: true, ownFocus: false, showTopN: false, @@ -112,4 +113,21 @@ describe('useHoverActionItems', () => { ); }); }); + + test('it should hide the Top N action when hideTopN is true', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => { + const testProps = { + ...defaultProps, + hideTopN: true, // <-- hide the Top N action + }; + return useHoverActionItems(testProps); + }); + await waitForNextUpdate(); + + result.current.allActionItems.forEach((item) => { + expect(item.props['data-test-subj']).not.toEqual('hover-actions-show-top-n'); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx index a7e4a528ca1b8..ad91e1f56d475 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx @@ -13,7 +13,7 @@ import { isEmpty } from 'lodash'; import { useKibana } from '../../lib/kibana'; import { getAllFieldsByName } from '../../containers/source'; -import { allowTopN } from './utils'; +import { allowTopN } from '../drag_and_drop/helpers'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { ColumnHeaderOptions, DataProvider, TimelineId } from '../../../../common/types/timeline'; import { SourcererScopeName } from '../../store/sourcerer/model'; @@ -29,6 +29,7 @@ export interface UseHoverActionItemsProps { enableOverflowButton?: boolean; field: string; handleHoverActionClicked: () => void; + hideTopN: boolean; isObjectArray: boolean; isOverflowPopoverOpen?: boolean; itemsToShow?: number; @@ -56,6 +57,7 @@ export const useHoverActionItems = ({ enableOverflowButton, field, handleHoverActionClicked, + hideTopN, isObjectArray, isOverflowPopoverOpen, itemsToShow = 2, @@ -182,6 +184,7 @@ export const useHoverActionItems = ({ allowTopN({ browserField: getAllFieldsByName(browserFields)[field], fieldName: field, + hideTopN, }) ? ( | undefined; - fieldName: string; -}): boolean => { - const isAggregatable = browserField?.aggregatable ?? false; - const fieldType = browserField?.type ?? ''; - const isAllowedType = [ - 'boolean', - 'geo-point', - 'geo-shape', - 'ip', - 'keyword', - 'number', - 'numeric', - 'string', - ].includes(fieldType); - - // TODO: remove this explicit allowlist when the ECS documentation includes alerts - const isAllowlistedNonBrowserField = [ - 'signal.ancestors.depth', - 'signal.ancestors.id', - 'signal.ancestors.rule', - 'signal.ancestors.type', - 'signal.original_event.action', - 'signal.original_event.category', - 'signal.original_event.code', - 'signal.original_event.created', - 'signal.original_event.dataset', - 'signal.original_event.duration', - 'signal.original_event.end', - 'signal.original_event.hash', - 'signal.original_event.id', - 'signal.original_event.kind', - 'signal.original_event.module', - 'signal.original_event.original', - 'signal.original_event.outcome', - 'signal.original_event.provider', - 'signal.original_event.risk_score', - 'signal.original_event.risk_score_norm', - 'signal.original_event.sequence', - 'signal.original_event.severity', - 'signal.original_event.start', - 'signal.original_event.timezone', - 'signal.original_event.type', - 'signal.original_time', - 'signal.parent.depth', - 'signal.parent.id', - 'signal.parent.index', - 'signal.parent.rule', - 'signal.parent.type', - 'signal.rule.created_by', - 'signal.rule.description', - 'signal.rule.enabled', - 'signal.rule.false_positives', - 'signal.rule.filters', - 'signal.rule.from', - 'signal.rule.id', - 'signal.rule.immutable', - 'signal.rule.index', - 'signal.rule.interval', - 'signal.rule.language', - 'signal.rule.max_signals', - 'signal.rule.name', - 'signal.rule.note', - 'signal.rule.output_index', - 'signal.rule.query', - 'signal.rule.references', - 'signal.rule.risk_score', - 'signal.rule.rule_id', - 'signal.rule.saved_id', - 'signal.rule.severity', - 'signal.rule.size', - 'signal.rule.tags', - 'signal.rule.threat', - 'signal.rule.threat.tactic.id', - 'signal.rule.threat.tactic.name', - 'signal.rule.threat.tactic.reference', - 'signal.rule.threat.technique.id', - 'signal.rule.threat.technique.name', - 'signal.rule.threat.technique.reference', - 'signal.rule.timeline_id', - 'signal.rule.timeline_title', - 'signal.rule.to', - 'signal.rule.type', - 'signal.rule.updated_by', - 'signal.rule.version', - 'signal.status', - ].includes(fieldName); - - return isAllowlistedNonBrowserField || (isAggregatable && isAllowedType); -}; diff --git a/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx b/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx index 085b2098cde35..745c7d5a2e9b0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx @@ -137,6 +137,7 @@ export const defaultCellActions: TGridCellAction[] = [ {allowTopN({ browserField: getAllFieldsByName(browserFields)[columnId], fieldName: columnId, + hideTopN: false, }) && ( {'Add to case'}
), getAddToCaseAction: jest.fn(), - getAddToExistingCaseButton: jest.fn(), - getAddToNewCaseButton: jest.fn(), + getAddToExistingCaseButton: jest.fn().mockReturnValue( +
+ {'Add to existing case'} +
+ ), + getAddToNewCaseButton: jest.fn().mockReturnValue( +
+ {'Add to new case'} +
+ ), }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx index 7355c237fb680..f64e279fb2755 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx @@ -39,9 +39,12 @@ const getAlertsCountTableColumns = ( render: function DraggableStackOptionField(value: string) { return ( ); }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx index c5a04e3a626df..1ef57a3499922 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx @@ -6,7 +6,11 @@ */ import { ExistsFilter, Filter } from '@kbn/es-query'; -import { buildAlertsRuleIdFilter, buildThreatMatchFilter } from './default_config'; +import { + buildAlertsRuleIdFilter, + buildAlertStatusFilter, + buildThreatMatchFilter, +} from './default_config'; jest.mock('./actions'); @@ -61,6 +65,65 @@ describe('alerts default_config', () => { }); }); + describe('buildAlertStatusFilter', () => { + test('when status is acknowledged, filter will build for both `in-progress` and `acknowledged`', () => { + const filters = buildAlertStatusFilter('acknowledged'); + const expected = { + meta: { + alias: null, + disabled: false, + key: 'signal.status', + negate: false, + params: { + query: 'acknowledged', + }, + type: 'phrase', + }, + query: { + bool: { + should: [ + { + term: { + 'signal.status': 'acknowledged', + }, + }, + { + term: { + 'signal.status': 'in-progress', + }, + }, + ], + }, + }, + }; + expect(filters).toHaveLength(1); + expect(filters[0]).toEqual(expected); + }); + + test('when status is `open` or `closed`, filter will build for solely that status', () => { + const filters = buildAlertStatusFilter('open'); + const expected = { + meta: { + alias: null, + disabled: false, + key: 'signal.status', + negate: false, + params: { + query: 'open', + }, + type: 'phrase', + }, + query: { + term: { + 'signal.status': 'open', + }, + }, + }; + expect(filters).toHaveLength(1); + expect(filters[0]).toEqual(expected); + }); + }); + // TODO: move these tests to ../timelines/components/timeline/body/events/event_column_view.tsx // describe.skip('getAlertActions', () => { // let setEventsLoading: ({ eventIds, isLoading }: SetEventsLoadingProps) => void; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx index 75bd41037934b..1c58c339cb5b2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx @@ -26,25 +26,47 @@ import { SubsetTimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { columns } from '../../configurations/security_solution_detections/columns'; -export const buildAlertStatusFilter = (status: Status): Filter[] => [ - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: 'signal.status', - params: { - query: status, - }, - }, - query: { - term: { - 'signal.status': status, +export const buildAlertStatusFilter = (status: Status): Filter[] => { + const combinedQuery = + status === 'acknowledged' + ? { + bool: { + should: [ + { + term: { + 'signal.status': status, + }, + }, + { + term: { + 'signal.status': 'in-progress', + }, + }, + ], + }, + } + : { + term: { + 'signal.status': status, + }, + }; + + return [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'signal.status', + params: { + query: status, + }, }, + query: combinedQuery, }, - }, -]; + ]; +}; export const buildAlertsRuleIdFilter = (ruleId: string | null): Filter[] => ruleId @@ -139,25 +161,47 @@ export const requiredFieldsForActions = [ ]; // TODO: Once we are past experimental phase this code should be removed -export const buildAlertStatusFilterRuleRegistry = (status: Status): Filter[] => [ - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: ALERT_STATUS, - params: { - query: status, - }, - }, - query: { - term: { - [ALERT_STATUS]: status, +export const buildAlertStatusFilterRuleRegistry = (status: Status): Filter[] => { + const combinedQuery = + status === 'acknowledged' + ? { + bool: { + should: [ + { + term: { + [ALERT_STATUS]: status, + }, + }, + { + term: { + [ALERT_STATUS]: 'in-progress', + }, + }, + ], + }, + } + : { + term: { + [ALERT_STATUS]: status, + }, + }; + + return [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: ALERT_STATUS, + params: { + query: status, + }, }, + query: combinedQuery, }, - }, -]; + ]; +}; export const buildShowBuildingBlockFilterRuleRegistry = ( showBuildingBlockAlerts: boolean diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index fc3e1e7f2d69b..3c0bb0e38b153 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -5,34 +5,47 @@ * 2.0. */ -import { EuiPanel, EuiLoadingContent } from '@elastic/eui'; +import { EuiLoadingContent, EuiPanel } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { connect, ConnectedProps, useDispatch } from 'react-redux'; import { Dispatch } from 'redux'; +import { esQuery, Filter } from '../../../../../../../src/plugins/data/public'; import { Status } from '../../../../common/detection_engine/schemas/common/schemas'; -import { Filter, esQuery } from '../../../../../../../src/plugins/data/public'; import { RowRendererId, TimelineIdLiteral } from '../../../../common/types/timeline'; -import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { StatefulEventsViewer } from '../../../common/components/events_viewer'; import { HeaderSection } from '../../../common/components/header_section'; +import { + displayErrorToast, + displaySuccessToast, + useStateToaster, +} from '../../../common/components/toasters'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; +import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; -import { combineQueries } from '../../../timelines/components/timeline/helpers'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; +import { defaultCellActions } from '../../../common/lib/cell_actions/default_cell_actions'; import { useKibana } from '../../../common/lib/kibana'; -import { inputsSelectors, State, inputsModel } from '../../../common/store'; +import { inputsModel, inputsSelectors, State } from '../../../common/store'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; +import * as i18nCommon from '../../../common/translations'; +import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; +import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; +import { combineQueries } from '../../../timelines/components/timeline/helpers'; import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; -import { TimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { TimelineModel } from '../../../timelines/store/timeline/model'; +import { columns, RenderCellValue } from '../../configurations/security_solution_detections'; import { updateAlertStatusAction } from './actions'; +import { AditionalFiltersAction, AlertsUtilityBar } from './alerts_utility_bar'; import { - requiredFieldsForActions, alertsDefaultModel, - buildAlertStatusFilter, alertsDefaultModelRuleRegistry, + buildAlertStatusFilter, buildAlertStatusFilterRuleRegistry, + requiredFieldsForActions, } from './default_config'; -import { AditionalFiltersAction, AlertsUtilityBar } from './alerts_utility_bar'; -import * as i18nCommon from '../../../common/translations'; +import { buildTimeRangeFilter } from './helpers'; import * as i18n from './translations'; import { SetEventsDeletedProps, @@ -40,19 +53,6 @@ import { UpdateAlertsStatusCallback, UpdateAlertsStatusProps, } from './types'; -import { - useStateToaster, - displaySuccessToast, - displayErrorToast, -} from '../../../common/components/toasters'; -import { SourcererScopeName } from '../../../common/store/sourcerer/model'; -import { useSourcererScope } from '../../../common/containers/sourcerer'; -import { buildTimeRangeFilter } from './helpers'; -import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; -import { columns, RenderCellValue } from '../../configurations/security_solution_detections'; -import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; -import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; -import { defaultCellActions } from '../../../common/lib/cell_actions/default_cell_actions'; interface OwnProps { defaultFilters?: Filter[]; @@ -165,7 +165,7 @@ export const AlertsTableComponent: React.FC = ({ text: i18nCommon.UPDATE_ALERT_STATUS_FAILED_DETAILED(updated, conflicts), }); } else { - let title: string; + let title = ''; switch (status) { case 'closed': title = i18n.CLOSED_ALERT_SUCCESS_TOAST(updated); @@ -173,7 +173,6 @@ export const AlertsTableComponent: React.FC = ({ case 'open': title = i18n.OPENED_ALERT_SUCCESS_TOAST(updated); break; - case 'in-progress': case 'acknowledged': title = i18n.ACKNOWLEDGED_ALERT_SUCCESS_TOAST(updated); } @@ -185,7 +184,7 @@ export const AlertsTableComponent: React.FC = ({ const onAlertStatusUpdateFailure = useCallback( (status: Status, error: Error) => { - let title: string; + let title = ''; switch (status) { case 'closed': title = i18n.CLOSED_ALERT_FAILED_TOAST; @@ -193,7 +192,6 @@ export const AlertsTableComponent: React.FC = ({ case 'open': title = i18n.OPENED_ALERT_FAILED_TOAST; break; - case 'in-progress': case 'acknowledged': title = i18n.ACKNOWLEDGED_ALERT_FAILED_TOAST; } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_endpoint_exception.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_endpoint_exception.tsx deleted file mode 100644 index 6639a7f3129c9..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_endpoint_exception.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import * as i18n from '../translations'; - -interface AddEndpointExceptionProps { - onClick: () => void; - disabled?: boolean; -} - -const AddEndpointExceptionComponent: React.FC = ({ - onClick, - disabled, -}) => { - return ( - - {i18n.ACTION_ADD_ENDPOINT_EXCEPTION} - - ); -}; - -export const AddEndpointException = React.memo(AddEndpointExceptionComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_event_filter.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_event_filter.tsx deleted file mode 100644 index 9b14c01371c9b..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_event_filter.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import * as i18n from '../translations'; - -interface AddEventFilterProps { - onClick: () => void; - disabled?: boolean; -} - -const AddEventFilterComponent: React.FC = ({ onClick, disabled }) => { - return ( - - {i18n.ACTION_ADD_EVENT_FILTER} - - ); -}; - -export const AddEventFilter = React.memo(AddEventFilterComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_exception.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_exception.tsx deleted file mode 100644 index af3d15184a686..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/add_exception.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import * as i18n from '../translations'; - -interface AddExceptionProps { - disabled?: boolean; - onClick: () => void; -} - -const AddExceptionComponent: React.FC = ({ disabled, onClick }) => { - return ( - - {i18n.ACTION_ADD_EXCEPTION} - - ); -}; - -export const AddException = React.memo(AddExceptionComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx index 101ba99f0bba6..eb31a59f0ca87 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx @@ -56,7 +56,8 @@ jest.mock('../../../../common/lib/kibana', () => ({ })); const actionMenuButton = '[data-test-subj="timeline-context-menu-button"] button'; -const addToCaseButton = '[data-test-subj="attach-alert-to-case-button"]'; +const addToExistingCaseButton = '[data-test-subj="add-to-existing-case-action"]'; +const addToNewCaseButton = '[data-test-subj="add-to-new-case-action"]'; describe('InvestigateInResolverAction', () => { test('it render AddToCase context menu item if timelineId === TimelineId.detectionsPage', () => { @@ -65,7 +66,8 @@ describe('InvestigateInResolverAction', () => { }); wrapper.find(actionMenuButton).simulate('click'); - expect(wrapper.find(addToCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToExistingCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToNewCaseButton).first().exists()).toEqual(true); }); test('it render AddToCase context menu item if timelineId === TimelineId.detectionsRulesDetailsPage', () => { @@ -77,7 +79,8 @@ describe('InvestigateInResolverAction', () => { ); wrapper.find(actionMenuButton).simulate('click'); - expect(wrapper.find(addToCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToExistingCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToNewCaseButton).first().exists()).toEqual(true); }); test('it render AddToCase context menu item if timelineId === TimelineId.active', () => { @@ -86,7 +89,8 @@ describe('InvestigateInResolverAction', () => { }); wrapper.find(actionMenuButton).simulate('click'); - expect(wrapper.find(addToCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToExistingCaseButton).first().exists()).toEqual(true); + expect(wrapper.find(addToNewCaseButton).first().exists()).toEqual(true); }); test('it does NOT render AddToCase context menu item when timelineId is not in the allowed list', () => { @@ -94,6 +98,7 @@ describe('InvestigateInResolverAction', () => { wrappingComponent: TestProviders, }); wrapper.find(actionMenuButton).simulate('click'); - expect(wrapper.find(addToCaseButton).first().exists()).toEqual(false); + expect(wrapper.find(addToExistingCaseButton).first().exists()).toEqual(false); + expect(wrapper.find(addToNewCaseButton).first().exists()).toEqual(false); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index c6243b0e8d709..7ad2166bd193c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -7,16 +7,11 @@ import React, { useCallback, useMemo, useState } from 'react'; -import { EuiButtonIcon, EuiContextMenu, EuiPopover, EuiToolTip } from '@elastic/eui'; +import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover, EuiToolTip } from '@elastic/eui'; import { indexOf } from 'lodash'; import { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types'; import { get, getOr } from 'lodash/fp'; -import { - EuiContextMenuPanelDescriptor, - EuiContextMenuPanelItemDescriptor, -} from '@elastic/eui/src/components/context_menu/context_menu'; -import styled from 'styled-components'; import { buildGetAlertByIdQuery } from '../../../../common/components/exceptions/helpers'; import { EventsTdContent } from '../../../../timelines/components/timeline/styles'; import { DEFAULT_ICON_BUTTON_WIDTH } from '../../../../timelines/components/timeline/helpers'; @@ -54,11 +49,6 @@ interface AlertContextMenuProps { onRuleChange?: () => void; timelineId: string; } -export const NestedWrapper = styled.span` - button.euiContextMenuItem { - padding: 0; - } -`; const AlertContextMenuComponent: React.FC = ({ ariaLabel = i18n.MORE_ACTIONS, @@ -99,27 +89,18 @@ const AlertContextMenuComponent: React.FC = ({ ] ); const hasWritePermissions = useGetUserCasesPermissions()?.crud ?? false; - const addToCaseAction = useMemo( + const addToCaseActionItems = useMemo( () => [ TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage, TimelineId.active, ].includes(timelineId as TimelineId) && hasWritePermissions - ? { - actionItem: [ - { - name: i18n.ACTION_ADD_TO_CASE, - panel: 2, - 'data-test-subj': 'attach-alert-to-case-button', - }, - ], - content: [ - timelinesUi.getAddToExistingCaseButton(addToCaseActionProps), - timelinesUi.getAddToNewCaseButton(addToCaseActionProps), - ], - } - : { actionItem: [], content: [] }, + ? [ + timelinesUi.getAddToExistingCaseButton(addToCaseActionProps), + timelinesUi.getAddToNewCaseButton(addToCaseActionProps), + ] + : [], [addToCaseActionProps, hasWritePermissions, timelineId, timelinesUi] ); @@ -179,7 +160,7 @@ const AlertContextMenuComponent: React.FC = ({ onAddEventFilterClick, } = useEventFilterModal(); - const { actionItems } = useAlertsActions({ + const { actionItems: statusActionItems } = useAlertsActions({ alertStatus, eventId: ecsRowData?._id, indexName: ecsRowData?._index ?? '', @@ -201,72 +182,59 @@ const AlertContextMenuComponent: React.FC = ({ closePopover(); }, [closePopover, onAddEventFilterClick]); - const { exceptionActions } = useExceptionActions({ + const { exceptionActionItems } = useExceptionActions({ isEndpointAlert, onAddExceptionTypeClick: handleOnAddExceptionTypeClick, }); - const investigateInResolverAction = useInvestigateInResolverContextItem({ + const investigateInResolverActionItems = useInvestigateInResolverContextItem({ timelineId, ecsData: ecsRowData, onClose: afterItemSelection, }); - const eventFilterAction = useEventFilterAction({ + const { eventFilterActionItems } = useEventFilterAction({ onAddEventFilterClick: handleOnAddEventFilterClick, }); - const items: EuiContextMenuPanelItemDescriptor[] = useMemo( + const items: React.ReactElement[] = useMemo( () => !isEvent && ruleId ? [ - ...investigateInResolverAction, - ...addToCaseAction.actionItem, - ...actionItems.map((aI) => ({ name: {aI} })), - ...exceptionActions, + ...investigateInResolverActionItems, + ...addToCaseActionItems, + ...statusActionItems, + ...exceptionActionItems, ] - : [...investigateInResolverAction, ...addToCaseAction.actionItem, eventFilterAction], + : [...investigateInResolverActionItems, ...addToCaseActionItems, ...eventFilterActionItems], [ - actionItems, - addToCaseAction.actionItem, - eventFilterAction, - exceptionActions, - investigateInResolverAction, + statusActionItems, + addToCaseActionItems, + eventFilterActionItems, + exceptionActionItems, + investigateInResolverActionItems, isEvent, ruleId, ] ); - const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => [ - { - id: 0, - items, - }, - { - id: 2, - title: i18n.ACTION_ADD_TO_CASE, - content: addToCaseAction.content, - }, - ], - [addToCaseAction.content, items] - ); - return ( <> {timelinesUi.getAddToCaseAction(addToCaseActionProps)} -
- - - - - -
+ {items.length > 0 && ( +
+ + + + + +
+ )} {exceptionModalType != null && ruleId != null && ruleName != null && diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/acknowledged_alert_status.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/acknowledged_alert_status.tsx deleted file mode 100644 index 1c97b304a74a3..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/acknowledged_alert_status.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import { FILTER_ACKNOWLEDGED } from '../../alerts_filter_group'; -import * as i18n from '../../translations'; - -interface AcknowledgedAlertStatusProps { - onClick: () => void; - disabled?: boolean; -} - -const AcknowledgedAlertStatusComponent: React.FC = ({ - onClick, - disabled, -}) => { - return ( - - {i18n.ACTION_ACKNOWLEDGED_ALERT} - - ); -}; - -export const AcknowledgedAlertStatus = React.memo(AcknowledgedAlertStatusComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/close_status.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/close_status.tsx deleted file mode 100644 index 28a34c549ef16..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/close_status.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import { FILTER_CLOSED } from '../../alerts_filter_group'; -import * as i18n from '../../translations'; - -interface CloseAlertActionProps { - onClick: () => void; - disabled?: boolean; -} - -const CloseAlertActionComponent: React.FC = ({ onClick, disabled }) => { - return ( - - {i18n.ACTION_CLOSE_ALERT} - - ); -}; - -export const CloseAlertAction = React.memo(CloseAlertActionComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/open_alert_status.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/open_alert_status.tsx deleted file mode 100644 index 2042acea4d604..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alerts_status_actions/open_alert_status.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; -import { FILTER_OPEN } from '../../alerts_filter_group'; -import * as i18n from '../../translations'; - -interface OpenAlertStatusProps { - onClick: () => void; - disabled?: boolean; -} - -const OpenAlertStatusComponent: React.FC = ({ onClick, disabled }) => { - return ( - - {i18n.ACTION_OPEN_ALERT} - - ); -}; - -export const OpenAlertStatus = React.memo(OpenAlertStatusComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx index 52ae9684157ae..2e23ecc648aee 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; +import { EuiContextMenuItem } from '@elastic/eui'; import { get } from 'lodash/fp'; import { setActiveTabTimeline, @@ -44,9 +45,12 @@ export const useInvestigateInResolverContextItem = ({ return isDisabled ? [] : [ - { - name: ACTION_INVESTIGATE_IN_RESOLVER, - onClick: handleClick, - }, + + {ACTION_INVESTIGATE_IN_RESOLVER} + , ]; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_actions.tsx index 9f1f699241e21..a56ed5b1235b9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_actions.tsx @@ -5,26 +5,13 @@ * 2.0. */ -import { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types'; import { useUserData } from '../../user_info'; import { ACTION_ADD_ENDPOINT_EXCEPTION, ACTION_ADD_EXCEPTION } from '../translations'; -interface ExceptionActions { - name: string; - onClick: () => void; - disabled: boolean; -} - -interface UseExceptionActions { - disabledAddEndpointException: boolean; - disabledAddException: boolean; - exceptionActions: ExceptionActions[]; - handleEndpointExceptionModal: () => void; - handleDetectionExceptionModal: () => void; -} - interface UseExceptionActionProps { isEndpointAlert: boolean; onAddExceptionTypeClick: (type: ExceptionListType) => void; @@ -33,7 +20,7 @@ interface UseExceptionActionProps { export const useExceptionActions = ({ isEndpointAlert, onAddExceptionTypeClick, -}: UseExceptionActionProps): UseExceptionActions => { +}: UseExceptionActionProps) => { const [{ canUserCRUD, hasIndexWrite }] = useUserData(); const handleDetectionExceptionModal = useCallback(() => { @@ -47,20 +34,25 @@ export const useExceptionActions = ({ const disabledAddEndpointException = !canUserCRUD || !hasIndexWrite || !isEndpointAlert; const disabledAddException = !canUserCRUD || !hasIndexWrite; - const exceptionActions = useMemo( + const exceptionActionItems = useMemo( () => [ - { - name: ACTION_ADD_ENDPOINT_EXCEPTION, - onClick: handleEndpointExceptionModal, - disabled: disabledAddEndpointException, - [`data-test-subj`]: 'add-endpoint-exception-menu-item', - }, - { - name: ACTION_ADD_EXCEPTION, - onClick: handleDetectionExceptionModal, - disabled: disabledAddException, - [`data-test-subj`]: 'add-exception-menu-item', - }, + + {ACTION_ADD_ENDPOINT_EXCEPTION} + , + + + {ACTION_ADD_EXCEPTION} + , ], [ disabledAddEndpointException, @@ -70,11 +62,5 @@ export const useExceptionActions = ({ ] ); - return { - disabledAddEndpointException, - disabledAddException, - exceptionActions, - handleEndpointExceptionModal, - handleDetectionExceptionModal, - }; + return { exceptionActionItems }; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_alerts_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_alerts_actions.tsx index 780cb65ed13d3..3568972aef2e9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_alerts_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_alerts_actions.tsx @@ -9,10 +9,11 @@ import { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { useGetUserAlertsPermissions } from '@kbn/alerts'; +import { useStatusBulkActionItems } from '../../../../../../timelines/public'; import { Status } from '../../../../../common/detection_engine/schemas/common/schemas'; import { timelineActions } from '../../../../timelines/store/timeline'; import { SetEventsDeletedProps, SetEventsLoadingProps } from '../types'; -import { useStatusBulkActionItems } from '../../../../../../timelines/public'; + import { useKibana } from '../../../../common/lib/kibana'; import { SERVER_APP_ID } from '../../../../../common/constants'; interface Props { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx index c24a1e0223ede..0a3d6fac70953 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { useMemo } from 'react'; +import React, { useMemo } from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; import { ACTION_ADD_EVENT_FILTER } from '../translations'; export const useEventFilterAction = ({ @@ -13,12 +14,17 @@ export const useEventFilterAction = ({ }: { onAddEventFilterClick: () => void; }) => { - const eventFilterActions = useMemo( - () => ({ - name: ACTION_ADD_EVENT_FILTER, - onClick: onAddEventFilterClick, - }), + const eventFilterActionItems = useMemo( + () => [ + + {ACTION_ADD_EVENT_FILTER} + , + ], [onAddEventFilterClick] ); - return eventFilterActions; + return { eventFilterActionItems }; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index 0671101f47a37..5cce2b915c7b6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useCallback } from 'react'; +import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; +import { EuiContextMenuItem } from '@elastic/eui'; import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; @@ -102,18 +103,21 @@ export const useInvestigateInTimeline = ({ updateTimelineIsLoading, ]); - const investigateInTimelineAction = showInvestigateInTimelineAction + const investigateInTimelineActionItems = showInvestigateInTimelineAction ? [ - { - name: ACTION_INVESTIGATE_IN_TIMELINE, - onClick: investigateInTimelineAlertClick, - disabled: isFetchingAlertEcs, - }, + + {ACTION_INVESTIGATE_IN_TIMELINE} + , ] : []; return { - investigateInTimelineAction, + investigateInTimelineActionItems, investigateInTimelineAlertClick, showInvestigateInTimelineAction, }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts index c6d9cd3eef7a3..385d07c5ee606 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts @@ -185,13 +185,6 @@ export const ACTION_ADD_EVENT_FILTER = i18n.translate( } ); -export const ACTION_ADD_TO_CASE = i18n.translate( - 'xpack.securitySolution.detectionEngine.alerts.actions.addToCase', - { - defaultMessage: 'Add to case', - } -); - export const ACTION_ADD_ENDPOINT_EXCEPTION = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.actions.addEndpointException', { diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx index c36d222d23ba1..808f70ec0e492 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; import type { TimelineEventsDetailsItem } from '../../../../common'; import { isIsolationSupported } from '../../../../common/endpoint/service/host_isolation/utils'; import { HostStatus } from '../../../../common/endpoint/types'; @@ -89,11 +90,14 @@ export const useHostIsolationAction = ({ isolationSupported && isHostIsolationPanelOpen === false ? [ - { - name: isolateHostTitle, - onClick: isolateHostHandler, - disabled: loadingHostIsolationStatus || agentStatus === HostStatus.UNENROLLED, - }, + + {isolateHostTitle} + , ] : [], [ diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/helpers.ts deleted file mode 100644 index 22f147494a2d6..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/helpers.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ACTION_ADD_TO_CASE } from '../alerts_table/translations'; - -export const addToCaseActionItem = (timelineId: string | null | undefined) => - ['detections-page', 'detections-rules-details-page', 'timeline-1'].includes(timelineId ?? '') - ? [ - { - name: ACTION_ADD_TO_CASE, - panel: 2, - }, - ] - : []; diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx index c40821b1b2949..f7461cb84198d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useCallback, useMemo } from 'react'; -import { EuiContextMenu, EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui'; +import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui'; import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types'; import { TAKE_ACTION } from '../alerts_table/alerts_utility_bar/translations'; @@ -15,13 +15,10 @@ import { TimelineEventsDetailsItem, TimelineNonEcsData } from '../../../../commo import { useExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions'; import { useAlertsActions } from '../alerts_table/timeline_actions/use_alerts_actions'; import { useInvestigateInTimeline } from '../alerts_table/timeline_actions/use_investigate_in_timeline'; -import { ACTION_ADD_TO_CASE } from '../alerts_table/translations'; import { useGetUserCasesPermissions, useKibana } from '../../../common/lib/kibana'; import { useInsertTimeline } from '../../../cases/components/use_insert_timeline'; -import { addToCaseActionItem } from './helpers'; import { useEventFilterAction } from '../alerts_table/timeline_actions/use_event_filter_action'; import { useHostIsolationAction } from '../host_isolation/use_host_isolation_action'; -import { CHANGE_ALERT_STATUS } from './translations'; import { getFieldValue } from '../host_isolation/helpers'; import type { Ecs } from '../../../../common/ecs'; import { Status } from '../../../../common/detection_engine/schemas/common/schemas'; @@ -121,7 +118,7 @@ export const TakeActionDropdown = React.memo( [onAddIsolationStatusClick] ); - const hostIsolationAction = useHostIsolationAction({ + const hostIsolationActionItems = useHostIsolationAction({ closePopover: closePopoverHandler, detailsData, onAddIsolationStatusClick: handleOnAddIsolationStatusClick, @@ -136,7 +133,7 @@ export const TakeActionDropdown = React.memo( [onAddExceptionTypeClick] ); - const { exceptionActions } = useExceptionActions({ + const { exceptionActionItems } = useExceptionActions({ isEndpointAlert, onAddExceptionTypeClick: handleOnAddExceptionTypeClick, }); @@ -146,7 +143,7 @@ export const TakeActionDropdown = React.memo( setIsPopoverOpen(false); }, [onAddEventFilterClick]); - const eventFilterActions = useEventFilterAction({ + const { eventFilterActionItems } = useEventFilterAction({ onAddEventFilterClick: handleOnAddEventFilterClick, }); @@ -154,7 +151,7 @@ export const TakeActionDropdown = React.memo( closePopoverHandler(); }, [closePopoverHandler]); - const { actionItems } = useAlertsActions({ + const { actionItems: statusActionItems } = useAlertsActions({ alertStatus: actionsData.alertStatus, eventId: actionsData.eventId, indexName, @@ -163,7 +160,7 @@ export const TakeActionDropdown = React.memo( closePopover: closePopoverAndFlyout, }); - const { investigateInTimelineAction } = useInvestigateInTimeline({ + const { investigateInTimelineActionItems } = useInvestigateInTimeline({ alertIds, ecsRowData: ecsData, onInvestigateInTimelineAlertClick: closePopoverHandler, @@ -172,15 +169,9 @@ export const TakeActionDropdown = React.memo( const alertsActionItems = useMemo( () => !isEvent && actionsData.ruleId - ? [ - { - name: CHANGE_ALERT_STATUS, - panel: 1, - }, - ...exceptionActions, - ] - : [eventFilterActions], - [eventFilterActions, exceptionActions, isEvent, actionsData.ruleId] + ? [...statusActionItems, ...exceptionActionItems] + : eventFilterActionItems, + [eventFilterActionItems, exceptionActionItems, statusActionItems, isEvent, actionsData.ruleId] ); const addToCaseProps = useMemo(() => { @@ -197,55 +188,35 @@ export const TakeActionDropdown = React.memo( } }, [afterCaseSelection, casePermissions, ecsData, insertTimelineHook]); - const panels = useMemo(() => { - if (tGridEnabled) { - return [ - { - id: 0, - items: [ - ...alertsActionItems, - ...addToCaseActionItem(timelineId), - ...hostIsolationAction, - ...investigateInTimelineAction, - ], - }, - { - id: 1, - title: CHANGE_ALERT_STATUS, - content: , - }, - { - id: 2, - title: ACTION_ADD_TO_CASE, - content: [ - <>{addToCaseProps && timelinesUi.getAddToExistingCaseButton(addToCaseProps)}, - <>{addToCaseProps && timelinesUi.getAddToNewCaseButton(addToCaseProps)}, - ], - }, - ]; - } else { - return [ - { - id: 0, - items: [...alertsActionItems, ...hostIsolationAction, ...investigateInTimelineAction], - }, - { - id: 1, - title: CHANGE_ALERT_STATUS, - content: , - }, - ]; - } - }, [ - addToCaseProps, - alertsActionItems, - hostIsolationAction, - investigateInTimelineAction, - timelineId, - timelinesUi, - actionItems, - tGridEnabled, - ]); + const addToCasesActionItems = useMemo( + () => + addToCaseProps && + ['detections-page', 'detections-rules-details-page', 'timeline-1'].includes( + timelineId ?? '' + ) + ? [ + timelinesUi.getAddToExistingCaseButton(addToCaseProps), + timelinesUi.getAddToNewCaseButton(addToCaseProps), + ] + : [], + [timelinesUi, addToCaseProps, timelineId] + ); + + const items: React.ReactElement[] = useMemo( + () => [ + ...(tGridEnabled ? addToCasesActionItems : []), + ...alertsActionItems, + ...hostIsolationActionItems, + ...investigateInTimelineActionItems, + ], + [ + tGridEnabled, + alertsActionItems, + addToCasesActionItems, + hostIsolationActionItems, + investigateInTimelineActionItems, + ] + ); const takeActionButton = useMemo(() => { return ( @@ -255,10 +226,10 @@ export const TakeActionDropdown = React.memo( ); }, [togglePopoverHandler]); - return panels[0].items?.length && !loadingEventDetails ? ( + return items.length && !loadingEventDetails ? ( <> - + ) : null; diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts index 89de83ab6e5cf..beeed344c31ef 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts @@ -26,7 +26,7 @@ export const columns: Array< { columnHeaderType: defaultColumnHeaderType, id: '@timestamp', - initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH + 5, + initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH + 10, }, { columnHeaderType: defaultColumnHeaderType, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx index b19c680fb9197..fb3e22260bd84 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx @@ -46,6 +46,7 @@ export const eventsStackByOptions: MatrixHistogramOption[] = [ ]; const DEFAULT_STACK_BY = 'event.action'; +const unit = (n: number) => i18n.EVENTS_UNIT(n); export const histogramConfigs: MatrixHistogramConfigs = { defaultStackByOption: @@ -119,6 +120,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ scopeId={SourcererScopeName.default} start={startDate} pageFilters={pageFilters} + unit={unit} /> ); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/translations.ts b/x-pack/plugins/security_solution/public/hosts/pages/translations.ts index b48a6d2193a30..5563dc285ad5a 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/translations.ts @@ -70,3 +70,9 @@ export const ERROR_FETCHING_EVENTS_DATA = i18n.translate( defaultMessage: 'Failed to query events data', } ); + +export const EVENTS_UNIT = (totalCount: number) => + i18n.translate('xpack.securitySolution.hosts.navigaton.eventsUnit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, + }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx index 8bfda22fd3701..eed5a1aedb218 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx @@ -51,7 +51,7 @@ export const BehaviorProtection = React.memo(() => { defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page." values={{ detectionRulesLink: ( - + { defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page." values={{ detectionRulesLink: ( - + @@ -82,6 +83,7 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot }, } } + hideTopN={false} isDraggable={false} render={[Function]} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/constants.ts b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/constants.ts new file mode 100644 index 0000000000000..0ff2713254894 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/constants.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { RowRendererId } from '../../../../../common/types/timeline'; +import * as i18n from './translations'; + +export const eventRendererNames: { [key in RowRendererId]: string } = { + [RowRendererId.alerts]: i18n.ALERTS_NAME, + [RowRendererId.auditd]: i18n.AUDITD_NAME, + [RowRendererId.auditd_file]: i18n.AUDITD_FILE_NAME, + [RowRendererId.library]: i18n.LIBRARY_NAME, + [RowRendererId.system_security_event]: i18n.AUTHENTICATION_NAME, + [RowRendererId.system_dns]: i18n.DNS_NAME, + [RowRendererId.netflow]: i18n.FLOW_NAME, + [RowRendererId.system]: i18n.SYSTEM_NAME, + [RowRendererId.system_endgame_process]: i18n.PROCESS, + [RowRendererId.registry]: i18n.REGISTRY_NAME, + [RowRendererId.system_fim]: i18n.FIM_NAME, + [RowRendererId.system_file]: i18n.FILE_NAME, + [RowRendererId.system_socket]: i18n.SOCKET_NAME, + [RowRendererId.suricata]: 'Suricata', + [RowRendererId.threat_match]: i18n.THREAT_MATCH_NAME, + [RowRendererId.zeek]: i18n.ZEEK_NAME, + [RowRendererId.plain]: '', +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx index 548dadf21b78b..e61da611323e1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx @@ -27,6 +27,7 @@ import { ThreatMatchExample, ZeekExample, } from '../examples'; +import { eventRendererNames } from './constants'; import * as i18n from './translations'; const Link = ({ children, url }: { children: React.ReactNode; url: string }) => ( @@ -40,26 +41,6 @@ const Link = ({ children, url }: { children: React.ReactNode; url: string }) => ); -export const eventRendererNames: { [key in RowRendererId]: string } = { - [RowRendererId.alerts]: i18n.ALERTS_NAME, - [RowRendererId.auditd]: i18n.AUDITD_NAME, - [RowRendererId.auditd_file]: i18n.AUDITD_FILE_NAME, - [RowRendererId.library]: i18n.LIBRARY_NAME, - [RowRendererId.system_security_event]: i18n.AUTHENTICATION_NAME, - [RowRendererId.system_dns]: i18n.DNS_NAME, - [RowRendererId.netflow]: i18n.FLOW_NAME, - [RowRendererId.system]: i18n.SYSTEM_NAME, - [RowRendererId.system_endgame_process]: i18n.PROCESS, - [RowRendererId.registry]: i18n.REGISTRY_NAME, - [RowRendererId.system_fim]: i18n.FIM_NAME, - [RowRendererId.system_file]: i18n.FILE_NAME, - [RowRendererId.system_socket]: i18n.SOCKET_NAME, - [RowRendererId.suricata]: 'Suricata', - [RowRendererId.threat_match]: i18n.THREAT_MATCH_NAME, - [RowRendererId.zeek]: i18n.ZEEK_NAME, - [RowRendererId.plain]: '', -}; - export interface RowRendererOption { id: RowRendererId; name: string; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 9db97bc22fb18..0866a927182f5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -17,7 +17,6 @@ import { import React, { useState, useCallback, useMemo } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { BrowserFields, DocValueFields } from '../../../../common/containers/source'; import { ExpandableEvent, ExpandableEventTitle } from './expandable_event'; import { useTimelineEventsDetails } from '../../../containers/details'; @@ -51,7 +50,6 @@ const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` `; interface EventDetailsPanelProps { - alertConsumers?: AlertConsumers[]; browserFields: BrowserFields; docValueFields: DocValueFields[]; entityType?: EntityType; @@ -68,10 +66,7 @@ interface EventDetailsPanelProps { timelineId: string; } -const SECURITY_SOLUTION_ALERT_CONSUMERS: AlertConsumers[] = [AlertConsumers.SIEM]; - const EventDetailsPanelComponent: React.FC = ({ - alertConsumers = SECURITY_SOLUTION_ALERT_CONSUMERS, // Default to Security Solution so only other applications have to pass this in browserFields, docValueFields, entityType = 'events', // Default to events so only alerts have to pass entityType in @@ -82,7 +77,6 @@ const EventDetailsPanelComponent: React.FC = ({ timelineId, }) => { const [loading, detailsData] = useTimelineEventsDetails({ - alertConsumers, docValueFields, entityType, indexName: expandedEvent.indexName ?? '', diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx index e264c7ec9fa04..97d9e4b492d6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { EuiFlyout, EuiFlyoutProps } from '@elastic/eui'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { timelineActions, timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; @@ -21,7 +20,6 @@ import { NetworkDetailsPanel } from './network_details'; import { EntityType } from '../../../../../timelines/common'; interface DetailsPanelProps { - alertConsumers?: AlertConsumers[]; browserFields: BrowserFields; docValueFields: DocValueFields[]; entityType?: EntityType; @@ -38,7 +36,6 @@ interface DetailsPanelProps { */ export const DetailsPanel = React.memo( ({ - alertConsumers, browserFields, docValueFields, entityType, @@ -77,7 +74,6 @@ export const DetailsPanel = React.memo( panelSize = 'm'; visiblePanel = ( ( = ({ - ecsData, - rowRenderers, - browserFields, - timelineId, - value, - fieldName, - isDraggable, - contextId, - eventId, -}) => { +}> = ({ ecsData, rowRenderers, browserFields, timelineId, value }) => { const [isOpen, setIsOpen] = useState(false); const rowRenderer = useMemo(() => getRowRenderer(ecsData, rowRenderers), [ecsData, rowRenderers]); @@ -111,7 +92,7 @@ const ReasonCell: React.FC<{ rowRenderer.renderRow({ browserFields, data: ecsData, - isDraggable: true, + isDraggable: false, timelineId, }) ); @@ -136,29 +117,21 @@ const ReasonCell: React.FC<{ return ( <> - - {rowRenderer && rowRender ? ( - - - {i18n.EVENT_RENDERER_POPOVER_TITLE(eventRendererNames[rowRenderer.id] ?? '')} - - {rowRender} - - ) : ( - value - )} - + {rowRenderer && rowRender ? ( + + + {i18n.EVENT_RENDERER_POPOVER_TITLE(eventRendererNames[rowRenderer.id] ?? '')} + + {rowRender} + + ) : ( + value + )} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx index ae31dbff7f063..9722f36e42eb0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.tsx @@ -157,7 +157,13 @@ export const SystemGenericFileLine = React.memo( processExecutable={processExecutable} /> - + { const myRequest = { ...(prevRequest ?? {}), - alertConsumers, docValueFields, entityType, indexName, @@ -124,7 +118,7 @@ export const useTimelineEventsDetails = ({ } return prevRequest; }); - }, [alertConsumers, docValueFields, entityType, eventId, indexName]); + }, [docValueFields, entityType, eventId, indexName]); useEffect(() => { timelineDetailsSearch(timelineDetailsRequest); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 610c394614c32..7c07410a2789a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -157,6 +157,7 @@ export const addTimelineToStore = ({ [id]: { ...timeline, isLoading: timelineById[id].isLoading, + initialized: timelineById[id].initialized, dateRange: timeline.status === TimelineStatus.immutable && timeline.timelineType === TimelineType.template diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index 7a16b62cd45e6..a2d7e2300d171 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -72,6 +72,7 @@ export type TimelineModel = TGridModelForTimeline & { /** timeline is saving */ isSaving: boolean; version: string | null; + initialized?: boolean; }; export type SubsetTimelineModel = Readonly< diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index a67337d3b779d..ae2ebc787451b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -46,7 +46,7 @@ export const buildBulkBody = ( const filteredSource = filterSource(mergedDoc); const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ mergedDoc, rule, timestamp }); + const reason = buildReasonMessage({ mergedDoc, rule }); if (isSourceDoc(mergedDoc)) { return { ...filteredSource, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts index 626dcb2fe83ff..a1f63a6d4e0c6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts @@ -42,7 +42,7 @@ export const buildBulkBody = ( const mergedDoc = getMergeStrategy(mergeStrategy)({ doc }); const rule = buildRuleWithOverrides(ruleSO, mergedDoc._source ?? {}); const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ mergedDoc, rule, timestamp }); + const reason = buildReasonMessage({ mergedDoc, rule }); const signal: Signal = { ...buildSignal([mergedDoc], rule, reason), ...additionalSignalFields(mergedDoc), @@ -122,7 +122,7 @@ export const buildSignalFromSequence = ( const rule = buildRuleWithoutOverrides(ruleSO); const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ rule, timestamp }); + const reason = buildReasonMessage({ rule }); const signal: Signal = buildSignal(events, rule, reason); const mergedEvents = objectArrayIntersection(events.map((event) => event._source)); return { @@ -154,7 +154,7 @@ export const buildSignalFromEvent = ( ? buildRuleWithOverrides(ruleSO, mergedEvent._source ?? {}) : buildRuleWithoutOverrides(ruleSO); const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ mergedDoc: mergedEvent, rule, timestamp }); + const reason = buildReasonMessage({ mergedDoc: mergedEvent, rule }); const signal: Signal = { ...buildSignal([mergedEvent], rule, reason), ...additionalSignalFields(mergedEvent), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts index e7f4fb41c763b..1a383b51eb8d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts @@ -12,7 +12,6 @@ import { SignalSourceHit } from './types'; describe('reason_formatter', () => { let rule: RulesSchema; let mergedDoc: SignalSourceHit; - let timestamp: string; beforeAll(() => { rule = { name: 'What is in a name', @@ -28,18 +27,17 @@ describe('reason_formatter', () => { '@timestamp': '2021-08-11T02:28:59.101Z', }, }; - timestamp = '2021-08-11T02:28:59.401Z'; }); describe('buildCommonReasonMessage', () => { - describe('when rule, mergedDoc, and timestamp are provided', () => { + describe('when rule and mergedDoc are provided', () => { it('should return the full reason message', () => { - expect(buildCommonReasonMessage({ rule, mergedDoc, timestamp })).toEqual( - 'Alert What is in a name created at 2021-08-11T02:28:59.401Z with a medium severity and risk score of 9000 by ferris bueller on party host.' + expect(buildCommonReasonMessage({ rule, mergedDoc })).toEqual( + 'Alert What is in a name created with a medium severity and risk score of 9000 by ferris bueller on party host.' ); }); }); - describe('when rule, mergedDoc, and timestamp are provided and host.name is missing', () => { + describe('when rule and mergedDoc are provided, but host.name is missing', () => { it('should return the reason message without the host name', () => { const updatedMergedDoc = { ...mergedDoc, @@ -48,12 +46,12 @@ describe('reason_formatter', () => { 'host.name': ['-'], }, }; - expect(buildCommonReasonMessage({ rule, mergedDoc: updatedMergedDoc, timestamp })).toEqual( - 'Alert What is in a name created at 2021-08-11T02:28:59.401Z with a medium severity and risk score of 9000 by ferris bueller.' + expect(buildCommonReasonMessage({ rule, mergedDoc: updatedMergedDoc })).toEqual( + 'Alert What is in a name created with a medium severity and risk score of 9000 by ferris bueller.' ); }); }); - describe('when rule, mergedDoc, and timestamp are provided and user.name is missing', () => { + describe('when rule and mergedDoc are provided, but user.name is missing', () => { it('should return the reason message without the user name', () => { const updatedMergedDoc = { ...mergedDoc, @@ -62,15 +60,15 @@ describe('reason_formatter', () => { 'user.name': ['-'], }, }; - expect(buildCommonReasonMessage({ rule, mergedDoc: updatedMergedDoc, timestamp })).toEqual( - 'Alert What is in a name created at 2021-08-11T02:28:59.401Z with a medium severity and risk score of 9000 on party host.' + expect(buildCommonReasonMessage({ rule, mergedDoc: updatedMergedDoc })).toEqual( + 'Alert What is in a name created with a medium severity and risk score of 9000 on party host.' ); }); }); - describe('when only rule and timestamp are provided', () => { + describe('when only rule is provided', () => { it('should return the reason message without host name or user name', () => { - expect(buildCommonReasonMessage({ rule, timestamp })).toEqual( - 'Alert What is in a name created at 2021-08-11T02:28:59.401Z with a medium severity and risk score of 9000.' + expect(buildCommonReasonMessage({ rule })).toEqual( + 'Alert What is in a name created with a medium severity and risk score of 9000.' ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts index 0586462a2a581..4917cdbd29170 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts @@ -12,7 +12,6 @@ import { SignalSourceHit } from './types'; export interface BuildReasonMessageArgs { rule: RulesSchema; mergedDoc?: SignalSourceHit; - timestamp: string; } export type BuildReasonMessage = (args: BuildReasonMessageArgs) => string; @@ -23,11 +22,7 @@ export type BuildReasonMessage = (args: BuildReasonMessageArgs) => string; * to more easily allow for this in the future. * @export buildCommonReasonMessage - is only exported for testing purposes, and only used internally here. */ -export const buildCommonReasonMessage = ({ - rule, - mergedDoc, - timestamp, -}: BuildReasonMessageArgs) => { +export const buildCommonReasonMessage = ({ rule, mergedDoc }: BuildReasonMessageArgs) => { if (!rule) { // This should never happen, but in case, better to not show a malformed string return ''; @@ -44,13 +39,12 @@ export const buildCommonReasonMessage = ({ return i18n.translate('xpack.securitySolution.detectionEngine.signals.alertReasonDescription', { defaultMessage: - 'Alert {alertName} created at {timestamp} with a {alertSeverity} severity and risk score of {alertRiskScore}{userName, select, null {} other {{whitespace}by {userName}} }{hostName, select, null {} other {{whitespace}on {hostName}} }.', + 'Alert {alertName} created with a {alertSeverity} severity and risk score of {alertRiskScore}{userName, select, null {} other {{whitespace}by {userName}} }{hostName, select, null {} other {{whitespace}on {hostName}} }.', values: { alertName: rule.name, alertSeverity: rule.severity, alertRiskScore: rule.risk_score, hostName: isFieldEmpty(hostName) ? 'null' : hostName, - timestamp, userName: isFieldEmpty(userName) ? 'null' : userName, whitespace: ' ', // there isn't support for the unicode /u0020 for whitespace, and leading spaces are deleted, so to prevent double-whitespace explicitly passing the space in. }, diff --git a/x-pack/test/functional/apps/dashboard_mode/index.js b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts similarity index 50% rename from x-pack/test/functional/apps/dashboard_mode/index.js rename to x-pack/plugins/security_solution/server/lib/telemetry/constants.ts index c28b805dc6b23..3ef45a554e7a5 100644 --- a/x-pack/test/functional/apps/dashboard_mode/index.js +++ b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts @@ -5,11 +5,10 @@ * 2.0. */ -export default function ({ loadTestFile }) { - describe('dashboard mode', function () { - this.tags('ciGroup7'); - - loadTestFile(require.resolve('./dashboard_view_mode')); - loadTestFile(require.resolve('./dashboard_empty_screen')); - }); -} +export const TELEMETRY_CHANNEL_LISTS = 'security-lists'; + +export const TELEMETRY_CHANNEL_ENDPOINT_META = 'endpoint-metadata'; + +export const LIST_ENDPOINT_EXCEPTION = 'endpoint_exception'; + +export const LIST_ENDPOINT_EVENT_FILTER = 'endpoint_event_filter'; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/endpoint_task.ts b/x-pack/plugins/security_solution/server/lib/telemetry/endpoint_task.ts index 13b4ebf0b3efb..668696f0dce1d 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/endpoint_task.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/endpoint_task.ts @@ -25,6 +25,7 @@ import { EndpointPolicyResponseAggregation, EndpointPolicyResponseDocument, } from './types'; +import { TELEMETRY_CHANNEL_ENDPOINT_META } from './constants'; export const TelemetryEndpointTaskConstants = { TIMEOUT: '5m', @@ -326,7 +327,7 @@ export class TelemetryEndpointTask { * Send the documents in a batches of 100 */ batchTelemetryRecords(telemetryPayloads, 100).forEach((telemetryBatch) => - this.sender.sendOnDemand('endpoint-metadata', telemetryBatch) + this.sender.sendOnDemand(TELEMETRY_CHANNEL_ENDPOINT_META, telemetryBatch) ); return telemetryPayloads.length; } catch (err) { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts index bee673fc8725f..a4d11b71f2a8e 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts @@ -7,12 +7,17 @@ import moment from 'moment'; import { createMockPackagePolicy } from './mocks'; +import { TrustedApp } from '../../../common/endpoint/types'; +import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER } from './constants'; import { getPreviousDiagTaskTimestamp, getPreviousEpMetaTaskTimestamp, batchTelemetryRecords, isPackagePolicyList, + templateTrustedApps, + templateEndpointExceptions, } from './helpers'; +import { EndpointExceptionListItem } from './types'; describe('test diagnostic telemetry scheduled task timing helper', () => { test('test -5 mins is returned when there is no previous task run', async () => { @@ -125,3 +130,67 @@ describe('test package policy type guard', () => { expect(result).toEqual(true); }); }); + +describe('list telemetry schema', () => { + test('trusted apps document is correctly formed', () => { + const data = [{ id: 'test_1' }] as TrustedApp[]; + const templatedItems = templateTrustedApps(data); + + expect(templatedItems[0]?.trusted_application.length).toEqual(1); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + }); + + test('trusted apps document is correctly formed with multiple entries', () => { + const data = [{ id: 'test_2' }, { id: 'test_2' }] as TrustedApp[]; + const templatedItems = templateTrustedApps(data); + + expect(templatedItems[0]?.trusted_application.length).toEqual(1); + expect(templatedItems[1]?.trusted_application.length).toEqual(1); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + }); + + test('endpoint exception document is correctly formed', () => { + const data = [{ id: 'test_3' }] as EndpointExceptionListItem[]; + const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EXCEPTION); + + expect(templatedItems[0]?.trusted_application.length).toEqual(0); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + }); + + test('endpoint exception document is correctly formed with multiple entries', () => { + const data = [ + { id: 'test_4' }, + { id: 'test_4' }, + { id: 'test_4' }, + ] as EndpointExceptionListItem[]; + const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EXCEPTION); + + expect(templatedItems[0]?.trusted_application.length).toEqual(0); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); + expect(templatedItems[1]?.endpoint_exception.length).toEqual(1); + expect(templatedItems[2]?.endpoint_exception.length).toEqual(1); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + }); + + test('endpoint event filters document is correctly formed', () => { + const data = [{ id: 'test_5' }] as EndpointExceptionListItem[]; + const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EVENT_FILTER); + + expect(templatedItems[0]?.trusted_application.length).toEqual(0); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(1); + }); + + test('endpoint event filters document is correctly formed with multiple entries', () => { + const data = [{ id: 'test_6' }, { id: 'test_6' }] as EndpointExceptionListItem[]; + const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EVENT_FILTER); + + expect(templatedItems[0]?.trusted_application.length).toEqual(0); + expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); + expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(1); + expect(templatedItems[1]?.endpoint_event_filter.length).toEqual(1); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index 6af258a4cbe64..bb2cc4f42ca90 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -6,7 +6,11 @@ */ import moment from 'moment'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { TrustedApp } from '../../../common/endpoint/types'; import { PackagePolicy } from '../../../../fleet/common/types/models/package_policy'; +import { EndpointExceptionListItem, ListTemplate } from './types'; +import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER } from './constants'; /** * Determines the when the last run was in order to execute to. @@ -84,9 +88,82 @@ export function isPackagePolicyList( return (data as PackagePolicy[])[0].inputs !== undefined; } +/** + * Maps Exception list item to parsable object + * + * @param exceptionListItem + * @returns collection of endpoint exceptions + */ +export const exceptionListItemToEndpointEntry = (exceptionListItem: ExceptionListItemSchema) => { + return { + id: exceptionListItem.id, + version: exceptionListItem._version || '', + name: exceptionListItem.name, + description: exceptionListItem.description, + created_at: exceptionListItem.created_at, + created_by: exceptionListItem.created_by, + updated_at: exceptionListItem.updated_at, + updated_by: exceptionListItem.updated_by, + entries: exceptionListItem.entries, + os_types: exceptionListItem.os_types, + } as EndpointExceptionListItem; +}; + +/** + * Constructs the lists telemetry schema from a collection of Trusted Apps + * + * @param listData + * @returns lists telemetry schema + */ +export const templateTrustedApps = (listData: TrustedApp[]) => { + return listData.map((item) => { + const template: ListTemplate = { + trusted_application: [], + endpoint_exception: [], + endpoint_event_filter: [], + }; + + template.trusted_application.push(item); + return template; + }); +}; + +/** + * Consructs the list telemetry schema from a collection of endpoint exceptions + * + * @param listData + * @param listType + * @returns lists telemetry schema + */ +export const templateEndpointExceptions = ( + listData: EndpointExceptionListItem[], + listType: string +) => { + return listData.map((item) => { + const template: ListTemplate = { + trusted_application: [], + endpoint_exception: [], + endpoint_event_filter: [], + }; + + if (listType === LIST_ENDPOINT_EXCEPTION) { + template.endpoint_exception.push(item); + return template; + } + + if (listType === LIST_ENDPOINT_EVENT_FILTER) { + template.endpoint_event_filter.push(item); + return template; + } + + return null; + }); +}; + /** * Convert counter label list to kebab case - * @params label_list the list of labels to create standardized UsageCounter from + * + * @param label_list the list of labels to create standardized UsageCounter from * @returns a string label for usage in the UsageCounter */ export function createUsageCounterLabel(labelList: string[]): string { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts b/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts index 642be5fc737f7..a38042e214ceb 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts @@ -9,7 +9,7 @@ import { TelemetryEventsSender } from './sender'; import { TelemetryDiagTask } from './diagnostic_task'; import { TelemetryEndpointTask } from './endpoint_task'; -import { TelemetryTrustedAppsTask } from './trusted_apps_task'; +import { TelemetryExceptionListsTask } from './security_lists_task'; import { PackagePolicy } from '../../../../fleet/common/types/models/package_policy'; /** @@ -69,8 +69,8 @@ export class MockTelemetryEndpointTask extends TelemetryEndpointTask { } /** - * Creates a mocked Telemetry trusted app Task + * Creates a mocked Telemetry exception lists Task */ -export class MockTelemetryTrustedAppTask extends TelemetryTrustedAppsTask { +export class MockExceptionListsTask extends TelemetryExceptionListsTask { public runTask = jest.fn(); } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.test.ts similarity index 66% rename from x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.test.ts rename to x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.test.ts index 5cd67a9c9c215..20d89c9721b27 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.test.ts @@ -9,10 +9,13 @@ import { loggingSystemMock } from 'src/core/server/mocks'; import { TaskStatus } from '../../../../task_manager/server'; import { taskManagerMock } from '../../../../task_manager/server/mocks'; -import { TelemetryTrustedAppsTask, TelemetryTrustedAppsTaskConstants } from './trusted_apps_task'; -import { createMockTelemetryEventsSender, MockTelemetryTrustedAppTask } from './mocks'; +import { + TelemetryExceptionListsTask, + TelemetrySecuityListsTaskConstants, +} from './security_lists_task'; +import { createMockTelemetryEventsSender, MockExceptionListsTask } from './mocks'; -describe('test trusted apps telemetry task functionality', () => { +describe('test exception list telemetry task functionality', () => { let logger: ReturnType; beforeEach(() => { @@ -20,25 +23,25 @@ describe('test trusted apps telemetry task functionality', () => { }); test('the trusted apps task can register', () => { - const telemetryTrustedAppsTask = new TelemetryTrustedAppsTask( + const telemetryTrustedAppsTask = new TelemetryExceptionListsTask( logger, taskManagerMock.createSetup(), createMockTelemetryEventsSender(true) ); - expect(telemetryTrustedAppsTask).toBeInstanceOf(TelemetryTrustedAppsTask); + expect(telemetryTrustedAppsTask).toBeInstanceOf(TelemetryExceptionListsTask); }); - test('the trusted apps task should be registered', () => { + test('the exception list task should be registered', () => { const mockTaskManager = taskManagerMock.createSetup(); - new TelemetryTrustedAppsTask(logger, mockTaskManager, createMockTelemetryEventsSender(true)); + new TelemetryExceptionListsTask(logger, mockTaskManager, createMockTelemetryEventsSender(true)); expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalled(); }); - test('the trusted apps task should be scheduled', async () => { + test('the exception list task should be scheduled', async () => { const mockTaskManagerSetup = taskManagerMock.createSetup(); - const telemetryTrustedAppsTask = new TelemetryTrustedAppsTask( + const telemetryTrustedAppsTask = new TelemetryExceptionListsTask( logger, mockTaskManagerSetup, createMockTelemetryEventsSender(true) @@ -49,13 +52,13 @@ describe('test trusted apps telemetry task functionality', () => { expect(mockTaskManagerStart.ensureScheduled).toHaveBeenCalled(); }); - test('the trusted apps task should not query elastic if telemetry is not opted in', async () => { + test('the exception list task should not query elastic if telemetry is not opted in', async () => { const mockSender = createMockTelemetryEventsSender(false); const mockTaskManager = taskManagerMock.createSetup(); - new MockTelemetryTrustedAppTask(logger, mockTaskManager, mockSender); + new MockExceptionListsTask(logger, mockTaskManager, mockSender); const mockTaskInstance = { - id: TelemetryTrustedAppsTaskConstants.TYPE, + id: TelemetrySecuityListsTaskConstants.TYPE, runAt: new Date(), attempts: 0, ownerId: '', @@ -65,28 +68,28 @@ describe('test trusted apps telemetry task functionality', () => { retryAt: new Date(), params: {}, state: {}, - taskType: TelemetryTrustedAppsTaskConstants.TYPE, + taskType: TelemetrySecuityListsTaskConstants.TYPE, }; const createTaskRunner = mockTaskManager.registerTaskDefinitions.mock.calls[0][0][ - TelemetryTrustedAppsTaskConstants.TYPE + TelemetrySecuityListsTaskConstants.TYPE ].createTaskRunner; const taskRunner = createTaskRunner({ taskInstance: mockTaskInstance }); await taskRunner.run(); expect(mockSender.fetchTrustedApplications).not.toHaveBeenCalled(); }); - test('the trusted apps task should query elastic if telemetry opted in', async () => { + test('the exception list task should query elastic if telemetry opted in', async () => { const mockSender = createMockTelemetryEventsSender(true); const mockTaskManager = taskManagerMock.createSetup(); - const telemetryTrustedAppsTask = new MockTelemetryTrustedAppTask( + const telemetryTrustedAppsTask = new MockExceptionListsTask( logger, mockTaskManager, mockSender ); const mockTaskInstance = { - id: TelemetryTrustedAppsTaskConstants.TYPE, + id: TelemetrySecuityListsTaskConstants.TYPE, runAt: new Date(), attempts: 0, ownerId: '', @@ -96,11 +99,11 @@ describe('test trusted apps telemetry task functionality', () => { retryAt: new Date(), params: {}, state: {}, - taskType: TelemetryTrustedAppsTaskConstants.TYPE, + taskType: TelemetrySecuityListsTaskConstants.TYPE, }; const createTaskRunner = mockTaskManager.registerTaskDefinitions.mock.calls[0][0][ - TelemetryTrustedAppsTaskConstants.TYPE + TelemetrySecuityListsTaskConstants.TYPE ].createTaskRunner; const taskRunner = createTaskRunner({ taskInstance: mockTaskInstance }); await taskRunner.run(); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.ts b/x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.ts new file mode 100644 index 0000000000000..1c4dc28f1c5a5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/security_lists_task.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { Logger } from 'src/core/server'; +import { + ENDPOINT_LIST_ID, + ENDPOINT_EVENT_FILTERS_LIST_ID, +} from '@kbn/securitysolution-list-constants'; +import { + ConcreteTaskInstance, + TaskManagerSetupContract, + TaskManagerStartContract, +} from '../../../../task_manager/server'; +import { + LIST_ENDPOINT_EXCEPTION, + LIST_ENDPOINT_EVENT_FILTER, + TELEMETRY_CHANNEL_LISTS, +} from './constants'; +import { batchTelemetryRecords, templateEndpointExceptions, templateTrustedApps } from './helpers'; +import { TelemetryEventsSender } from './sender'; + +export const TelemetrySecuityListsTaskConstants = { + TIMEOUT: '3m', + TYPE: 'security:telemetry-lists', + INTERVAL: '24h', + VERSION: '1.0.0', +}; + +const MAX_TELEMETRY_BATCH = 1_000; + +export class TelemetryExceptionListsTask { + private readonly logger: Logger; + private readonly sender: TelemetryEventsSender; + + constructor( + logger: Logger, + taskManager: TaskManagerSetupContract, + sender: TelemetryEventsSender + ) { + this.logger = logger; + this.sender = sender; + + taskManager.registerTaskDefinitions({ + [TelemetrySecuityListsTaskConstants.TYPE]: { + title: 'Security Solution Lists Telemetry', + timeout: TelemetrySecuityListsTaskConstants.TIMEOUT, + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + const { state } = taskInstance; + + return { + run: async () => { + const taskExecutionTime = moment().utc().toISOString(); + const hits = await this.runTask(taskInstance.id); + + return { + state: { + lastExecutionTimestamp: taskExecutionTime, + runs: (state.runs || 0) + 1, + hits, + }, + }; + }, + cancel: async () => {}, + }; + }, + }, + }); + } + + public start = async (taskManager: TaskManagerStartContract) => { + try { + await taskManager.ensureScheduled({ + id: this.getTaskId(), + taskType: TelemetrySecuityListsTaskConstants.TYPE, + scope: ['securitySolution'], + schedule: { + interval: TelemetrySecuityListsTaskConstants.INTERVAL, + }, + state: { runs: 0 }, + params: { version: TelemetrySecuityListsTaskConstants.VERSION }, + }); + } catch (e) { + this.logger.error(`Error scheduling task, received ${e.message}`); + } + }; + + private getTaskId = (): string => { + return `${TelemetrySecuityListsTaskConstants.TYPE}:${TelemetrySecuityListsTaskConstants.VERSION}`; + }; + + public runTask = async (taskId: string) => { + if (taskId !== this.getTaskId()) { + return 0; + } + + const isOptedIn = await this.sender.isTelemetryOptedIn(); + if (!isOptedIn) { + return 0; + } + + // Lists Telemetry: Trusted Applications + + const trustedApps = await this.sender.fetchTrustedApplications(); + const trustedAppsJson = templateTrustedApps(trustedApps.data); + this.logger.debug(`Trusted Apps: ${trustedAppsJson}`); + + batchTelemetryRecords(trustedAppsJson, MAX_TELEMETRY_BATCH).forEach((batch) => + this.sender.sendOnDemand(TELEMETRY_CHANNEL_LISTS, batch) + ); + + // Lists Telemetry: Endpoint Exceptions + + const epExceptions = await this.sender.fetchEndpointList(ENDPOINT_LIST_ID); + const epExceptionsJson = templateEndpointExceptions(epExceptions.data, LIST_ENDPOINT_EXCEPTION); + this.logger.debug(`EP Exceptions: ${epExceptionsJson}`); + + batchTelemetryRecords(epExceptionsJson, MAX_TELEMETRY_BATCH).forEach((batch) => + this.sender.sendOnDemand(TELEMETRY_CHANNEL_LISTS, batch) + ); + + // Lists Telemetry: Endpoint Event Filters + + const epFilters = await this.sender.fetchEndpointList(ENDPOINT_EVENT_FILTERS_LIST_ID); + const epFiltersJson = templateEndpointExceptions(epFilters.data, LIST_ENDPOINT_EVENT_FILTER); + this.logger.debug(`EP Event Filters: ${epFiltersJson}`); + + batchTelemetryRecords(epFiltersJson, MAX_TELEMETRY_BATCH).forEach((batch) => + this.sender.sendOnDemand(TELEMETRY_CHANNEL_LISTS, batch) + ); + + return trustedAppsJson.length + epExceptionsJson.length + epFiltersJson.length; + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index 5724c61bfcee7..c7bb58dd2251b 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -18,14 +18,15 @@ import { TaskManagerSetupContract, TaskManagerStartContract, } from '../../../../task_manager/server'; -import { createUsageCounterLabel } from './helpers'; import { TelemetryDiagTask } from './diagnostic_task'; import { TelemetryEndpointTask } from './endpoint_task'; -import { TelemetryTrustedAppsTask } from './trusted_apps_task'; +import { TelemetryExceptionListsTask } from './security_lists_task'; import { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { AgentService, AgentPolicyServiceInterface } from '../../../../fleet/server'; -import { ExceptionListClient } from '../../../../lists/server'; import { getTrustedAppsList } from '../../endpoint/routes/trusted_apps/service'; +import { ExceptionListClient } from '../../../../lists/server'; +import { GetEndpointListResponse } from './types'; +import { createUsageCounterLabel, exceptionListItemToEndpointEntry } from './helpers'; type BaseSearchTypes = string | number | boolean | object; export type SearchTypes = BaseSearchTypes | BaseSearchTypes[] | undefined; @@ -63,7 +64,7 @@ export class TelemetryEventsSender { private isOptedIn?: boolean = true; // Assume true until the first check private diagTask?: TelemetryDiagTask; private epMetricsTask?: TelemetryEndpointTask; - private trustedAppsTask?: TelemetryTrustedAppsTask; + private exceptionListTask?: TelemetryExceptionListsTask; private agentService?: AgentService; private agentPolicyService?: AgentPolicyServiceInterface; private esClient?: ElasticsearchClient; @@ -86,7 +87,7 @@ export class TelemetryEventsSender { if (taskManager) { this.diagTask = new TelemetryDiagTask(this.logger, taskManager, this); this.epMetricsTask = new TelemetryEndpointTask(this.logger, taskManager, this); - this.trustedAppsTask = new TelemetryTrustedAppsTask(this.logger, taskManager, this); + this.exceptionListTask = new TelemetryExceptionListsTask(this.logger, taskManager, this); } } @@ -108,7 +109,7 @@ export class TelemetryEventsSender { this.logger.debug(`Starting diagnostic and endpoint telemetry tasks`); this.diagTask.start(taskManager); this.epMetricsTask.start(taskManager); - this.trustedAppsTask?.start(taskManager); + this.exceptionListTask?.start(taskManager); } this.logger.debug(`Starting local task`); @@ -279,6 +280,32 @@ export class TelemetryEventsSender { return getTrustedAppsList(this.exceptionListClient, { page: 1, per_page: 10_000 }); } + public async fetchEndpointList(listId: string): Promise { + if (this?.exceptionListClient === undefined || this?.exceptionListClient === null) { + throw Error('could not fetch trusted applications. exception list client not available.'); + } + + // Ensure list is created if it does not exist + await this.exceptionListClient.createTrustedAppsList(); + + const results = await this.exceptionListClient.findExceptionListItem({ + listId, + page: 1, + perPage: this.max_records, + filter: undefined, + namespaceType: 'agnostic', + sortField: 'name', + sortOrder: 'asc', + }); + + return { + data: results?.data.map(exceptionListItemToEndpointEntry) ?? [], + total: results?.total ?? 0, + page: results?.page ?? 1, + per_page: results?.per_page ?? this.max_records, + }; + } + public queueTelemetryEvents(events: TelemetryEvent[]) { const qlength = this.queue.length; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.ts b/x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.ts deleted file mode 100644 index f91f3e8428d04..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/telemetry/trusted_apps_task.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import moment from 'moment'; -import { Logger } from 'src/core/server'; -import { - ConcreteTaskInstance, - TaskManagerSetupContract, - TaskManagerStartContract, -} from '../../../../task_manager/server'; - -import { getPreviousEpMetaTaskTimestamp, batchTelemetryRecords } from './helpers'; -import { TelemetryEventsSender } from './sender'; - -export const TelemetryTrustedAppsTaskConstants = { - TIMEOUT: '1m', - TYPE: 'security:trusted-apps-telemetry', - INTERVAL: '24h', - VERSION: '1.0.0', -}; - -/** Telemetry Trusted Apps Task - * - * The Trusted Apps task is a daily batch job that collects and transmits non-sensitive - * trusted apps hashes + file paths for supported operating systems. This helps test - * efficacy of our protections. - */ -export class TelemetryTrustedAppsTask { - private readonly logger: Logger; - private readonly sender: TelemetryEventsSender; - - constructor( - logger: Logger, - taskManager: TaskManagerSetupContract, - sender: TelemetryEventsSender - ) { - this.logger = logger; - this.sender = sender; - - taskManager.registerTaskDefinitions({ - [TelemetryTrustedAppsTaskConstants.TYPE]: { - title: 'Security Solution Telemetry Endpoint Metrics and Info task', - timeout: TelemetryTrustedAppsTaskConstants.TIMEOUT, - createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { - const { state } = taskInstance; - - return { - run: async () => { - const taskExecutionTime = moment().utc().toISOString(); - const lastExecutionTimestamp = getPreviousEpMetaTaskTimestamp( - taskExecutionTime, - taskInstance.state?.lastExecutionTimestamp - ); - - const hits = await this.runTask( - taskInstance.id, - lastExecutionTimestamp, - taskExecutionTime - ); - - return { - state: { - lastExecutionTimestamp: taskExecutionTime, - runs: (state.runs || 0) + 1, - hits, - }, - }; - }, - cancel: async () => {}, - }; - }, - }, - }); - } - - public start = async (taskManager: TaskManagerStartContract) => { - try { - await taskManager.ensureScheduled({ - id: this.getTaskId(), - taskType: TelemetryTrustedAppsTaskConstants.TYPE, - scope: ['securitySolution'], - schedule: { - interval: TelemetryTrustedAppsTaskConstants.INTERVAL, - }, - state: { runs: 0 }, - params: { version: TelemetryTrustedAppsTaskConstants.VERSION }, - }); - } catch (e) { - this.logger.error(`Error scheduling task, received ${e.message}`); - } - }; - - private getTaskId = (): string => { - return `${TelemetryTrustedAppsTaskConstants.TYPE}:${TelemetryTrustedAppsTaskConstants.VERSION}`; - }; - - public runTask = async (taskId: string, executeFrom: string, executeTo: string) => { - if (taskId !== this.getTaskId()) { - this.logger.debug(`Outdated task running: ${taskId}`); - return 0; - } - - const isOptedIn = await this.sender.isTelemetryOptedIn(); - if (!isOptedIn) { - this.logger.debug(`Telemetry is not opted-in.`); - return 0; - } - - const response = await this.sender.fetchTrustedApplications(); - this.logger.debug(`Trusted Apps: ${response}`); - - batchTelemetryRecords(response.data, 1_000).forEach((telemetryBatch) => - this.sender.sendOnDemand('lists-trustedapps', telemetryBatch) - ); - - return response.data.length; - }; -} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts index 355393145fa0b..d1d7740071e1f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -5,6 +5,9 @@ * 2.0. */ +import { schema, TypeOf } from '@kbn/config-schema'; +import { TrustedApp } from '../../../common/endpoint/types'; + // EP Policy Response export interface EndpointPolicyResponseAggregation { @@ -138,3 +141,43 @@ interface EndpointMetricOS { platform: string; full: string; } + +// List HTTP Types + +export const GetTrustedAppsRequestSchema = { + query: schema.object({ + page: schema.maybe(schema.number({ defaultValue: 1, min: 1 })), + per_page: schema.maybe(schema.number({ defaultValue: 20, min: 1 })), + kuery: schema.maybe(schema.string()), + }), +}; + +export type GetEndpointListRequest = TypeOf; + +export interface GetEndpointListResponse { + per_page: number; + page: number; + total: number; + data: EndpointExceptionListItem[]; +} + +// Telemetry List types + +export interface EndpointExceptionListItem { + id: string; + version: string; + name: string; + description: string; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; + entries: object; + os_types: object; +} + +export interface ListTemplate { + trusted_application: TrustedApp[]; + endpoint_exception: EndpointExceptionListItem[]; + endpoint_event_filter: EndpointExceptionListItem[]; +} diff --git a/x-pack/plugins/spaces/common/index.ts b/x-pack/plugins/spaces/common/index.ts index 003a0c068a166..3af87c44f8e0f 100644 --- a/x-pack/plugins/spaces/common/index.ts +++ b/x-pack/plugins/spaces/common/index.ts @@ -9,6 +9,7 @@ export { isReservedSpace } from './is_reserved_space'; export { MAX_SPACE_INITIALS, SPACE_SEARCH_COUNT_THRESHOLD, ENTER_SPACE_PATH } from './constants'; export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser'; export type { + Space, GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, diff --git a/x-pack/plugins/spaces/common/is_reserved_space.test.ts b/x-pack/plugins/spaces/common/is_reserved_space.test.ts index 0128a7483f166..630d4a000f3e5 100644 --- a/x-pack/plugins/spaces/common/is_reserved_space.test.ts +++ b/x-pack/plugins/spaces/common/is_reserved_space.test.ts @@ -5,9 +5,8 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; - import { isReservedSpace } from './is_reserved_space'; +import type { Space } from './types'; test('it returns true for reserved spaces', () => { const space: Space = { diff --git a/x-pack/plugins/spaces/common/is_reserved_space.ts b/x-pack/plugins/spaces/common/is_reserved_space.ts index f78fe7bbdac1b..92b9a0a99ddbb 100644 --- a/x-pack/plugins/spaces/common/is_reserved_space.ts +++ b/x-pack/plugins/spaces/common/is_reserved_space.ts @@ -7,7 +7,7 @@ import { get } from 'lodash'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from './types'; /** * Returns whether the given Space is reserved or not. diff --git a/x-pack/plugins/spaces/common/types.ts b/x-pack/plugins/spaces/common/types.ts index 55bd1c137f8cf..39864447310b4 100644 --- a/x-pack/plugins/spaces/common/types.ts +++ b/x-pack/plugins/spaces/common/types.ts @@ -5,7 +5,60 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; +/** + * A Space. + */ +export interface Space { + /** + * The unique identifier for this space. + * The id becomes part of the "URL Identifier" of the space. + * + * Example: an id of `marketing` would result in the URL identifier of `/s/marketing`. + */ + id: string; + + /** + * Display name for this space. + */ + name: string; + + /** + * Optional description for this space. + */ + description?: string; + + /** + * Optional color (hex code) for this space. + * If neither `color` nor `imageUrl` is specified, then a color will be automatically generated. + */ + color?: string; + + /** + * Optional display initials for this space's avatar. Supports a maximum of 2 characters. + * If initials are not provided, then they will be derived from the space name automatically. + * + * Initials are not displayed if an `imageUrl` has been specified. + */ + initials?: string; + + /** + * Optional base-64 encoded data image url to show as this space's avatar. + * This setting takes precedence over any configured `color` or `initials`. + */ + imageUrl?: string; + + /** + * The set of feature ids that should be hidden within this space. + */ + disabledFeatures: string[]; + + /** + * Indicates that this space is reserved (system controlled). + * Reserved spaces cannot be created or deleted by end-users. + * @private + */ + _reserved?: boolean; +} /** * Controls how spaces are retrieved. diff --git a/x-pack/plugins/spaces/kibana.json b/x-pack/plugins/spaces/kibana.json index e01224d03bfc3..090e5d0894480 100644 --- a/x-pack/plugins/spaces/kibana.json +++ b/x-pack/plugins/spaces/kibana.json @@ -8,13 +8,12 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "spaces"], - "requiredPlugins": ["features", "licensing", "spacesOss"], + "requiredPlugins": ["features", "licensing"], "optionalPlugins": [ "advancedSettings", "home", "management", - "usageCollection", - "savedObjectsManagement" + "usageCollection" ], "server": true, "ui": true, @@ -22,7 +21,6 @@ "requiredBundles": [ "esUiShared", "kibanaReact", - "savedObjectsManagement", "home" ] } diff --git a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx index 5658f95b62854..28d64e41fcbe4 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx @@ -8,8 +8,8 @@ import React from 'react'; import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../common'; import { AdvancedSettingsSubtitle, AdvancedSettingsTitle } from './components'; interface SetupDeps { diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx index 75a27a3738e61..613cd9fdaebce 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx @@ -9,7 +9,8 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import React, { Fragment, useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { Space } from '../../../../common'; interface Props { getActiveSpace: () => Promise; diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx index 9bec9e32ca736..a5af84bd33948 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx @@ -9,8 +9,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic import React, { lazy, Suspense, useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../../common'; import { getSpaceAvatarComponent } from '../../../space_avatar'; // No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx index b04450ae4febd..8d9c2f17bdec6 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx @@ -11,14 +11,14 @@ import { EuiBadge, EuiIconTip, EuiLoadingSpinner } from '@elastic/eui'; import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { SpacesDataEntry } from '../../types'; import type { SummarizedCopyToSpaceResult } from '../lib'; import type { ImportRetry } from '../types'; import { ResolveAllConflicts } from './resolve_all_conflicts'; interface Props { - space: Space; + space: SpacesDataEntry; summarizedCopyResult: SummarizedCopyToSpaceResult; conflictResolutionInProgress: boolean; retries: ImportRetry[]; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx index 3fee2fdfa975d..f1472032fffa1 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal'; +import type { CopyToSpaceFlyoutProps } from '../types'; export const getCopyToSpaceFlyoutComponent = async (): Promise< React.FC diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx index c021d8bdf69a1..998b202a8d6b3 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx @@ -17,11 +17,8 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { - FailedImport, - ProcessedImportResponse, -} from 'src/plugins/saved_objects_management/public'; +import type { FailedImport, ProcessedImportResponse } from '../lib'; import type { ImportRetry } from '../types'; interface Props { @@ -62,8 +59,8 @@ export const CopyToSpaceFlyoutFooter = (props: Props) => { let pendingCount = 0; let skippedCount = 0; let errorCount = 0; - if (spaceResult.status === 'success') { - successCount = spaceResult.importCount; + if (spaceResult.success === true) { + successCount = spaceResult.successfulImports.length; } else { const uniqueResolvableErrors = spaceResult.failedImports .filter(isResolvableError) diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx index cb821061b9251..2bad5757613e0 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx @@ -12,11 +12,11 @@ import React from 'react'; import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest'; import { coreMock } from 'src/core/public/mocks'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import { getSpacesContextProviderWrapper } from '../../spaces_context'; import { spacesManagerMock } from '../../spaces_manager/mocks'; -import type { SavedObjectTarget } from '../types'; +import type { CopyToSpaceSavedObjectTarget } from '../types'; import { CopyModeControl } from './copy_mode_control'; import { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout'; import { CopyToSpaceForm } from './copy_to_space_form'; @@ -82,7 +82,7 @@ const setup = async (opts: SetupOpts = {}) => { namespaces: ['default'], icon: 'dashboard', title: 'foo', - } as SavedObjectTarget; + } as CopyToSpaceSavedObjectTarget; const SpacesContext = await getSpacesContextProviderWrapper({ getStartServices, diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx index 8f219b7154def..7697780c352c9 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx @@ -24,31 +24,26 @@ import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import { processImportResponse } from '../../../../../../src/plugins/saved_objects_management/public'; import { useSpaces } from '../../spaces_context'; -import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; +import type { SpacesDataEntry } from '../../types'; +import { processImportResponse } from '../lib'; +import type { ProcessedImportResponse } from '../lib'; +import type { CopyOptions, CopyToSpaceFlyoutProps, ImportRetry } from '../types'; import { CopyToSpaceFlyoutFooter } from './copy_to_space_flyout_footer'; import { CopyToSpaceForm } from './copy_to_space_form'; import { ProcessingCopyToSpace } from './processing_copy_to_space'; -export interface CopyToSpaceFlyoutProps { - onClose: () => void; - savedObjectTarget: SavedObjectTarget; -} - const INCLUDE_RELATED_DEFAULT = true; const CREATE_NEW_COPIES_DEFAULT = true; const OVERWRITE_ALL_DEFAULT = true; export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => { - const { spacesManager, services } = useSpaces(); + const { spacesManager, spacesDataPromise, services } = useSpaces(); const { notifications } = services; const toastNotifications = notifications!.toasts; - const { onClose, savedObjectTarget: object } = props; + const { onClose = () => null, savedObjectTarget: object } = props; const savedObjectTarget = useMemo( () => ({ type: object.type, @@ -66,22 +61,21 @@ export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => { selectedSpaceIds: [], }); - const [{ isLoading, spaces }, setSpacesState] = useState<{ isLoading: boolean; spaces: Space[] }>( - { - isLoading: true, - spaces: [], - } - ); + const [{ isLoading, spaces }, setSpacesState] = useState<{ + isLoading: boolean; + spaces: SpacesDataEntry[]; + }>({ + isLoading: true, + spaces: [], + }); useEffect(() => { - const getSpaces = spacesManager.getSpaces({ includeAuthorizedPurposes: true }); - const getActiveSpace = spacesManager.getActiveSpace(); - Promise.all([getSpaces, getActiveSpace]) - .then(([allSpaces, activeSpace]) => { + spacesDataPromise + .then(({ spacesMap }) => { setSpacesState({ isLoading: false, - spaces: allSpaces.filter( - ({ id, authorizedPurposes }) => - id !== activeSpace.id && authorizedPurposes?.copySavedObjectsIntoSpace !== false + spaces: [...spacesMap.values()].filter( + ({ isActiveSpace, isAuthorizedForPurpose }) => + isActiveSpace || isAuthorizedForPurpose('copySavedObjectsIntoSpace') ), }); }) @@ -92,7 +86,7 @@ export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => { }), }); }); - }, [spacesManager, toastNotifications]); + }, [spacesDataPromise, toastNotifications]); const [copyInProgress, setCopyInProgress] = useState(false); const [conflictResolutionInProgress, setConflictResolutionInProgress] = useState(false); diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx index e28e95ed42d25..1b92936816e52 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx @@ -9,16 +9,16 @@ import { EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import type { CopyOptions, SavedObjectTarget } from '../types'; +import type { SpacesDataEntry } from '../../types'; +import type { CopyOptions, CopyToSpaceSavedObjectTarget } from '../types'; import type { CopyMode } from './copy_mode_control'; import { CopyModeControl } from './copy_mode_control'; import { SelectableSpacesControl } from './selectable_spaces_control'; interface Props { - savedObjectTarget: Required; - spaces: Space[]; + savedObjectTarget: Required; + spaces: SpacesDataEntry[]; onUpdate: (copyOptions: CopyOptions) => void; copyOptions: CopyOptions; } diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts index f3173e14aa794..6001920f34c11 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts @@ -6,4 +6,3 @@ */ export { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout'; -export type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx index 1fba249e5410a..91ee2acf6bb42 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx @@ -15,21 +15,21 @@ import { import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { SpacesDataEntry } from '../../types'; +import type { ProcessedImportResponse } from '../lib'; import { summarizeCopyResult } from '../lib'; -import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; +import type { CopyOptions, CopyToSpaceSavedObjectTarget, ImportRetry } from '../types'; import { SpaceResult, SpaceResultProcessing } from './space_result'; interface Props { - savedObjectTarget: Required; + savedObjectTarget: Required; copyInProgress: boolean; conflictResolutionInProgress: boolean; copyResult: Record; retries: Record; onRetriesChange: (retries: Record) => void; - spaces: Space[]; + spaces: SpacesDataEntry[]; copyOptions: CopyOptions; } @@ -95,7 +95,7 @@ export const ProcessingCopyToSpace = (props: Props) => { {props.copyOptions.selectedSpaceIds.map((id) => { - const space = props.spaces.find((s) => s.id === id) as Space; + const space = props.spaces.find((s) => s.id === id)!; const spaceCopyResult = props.copyResult[space.id]; const summarizedSpaceCopyResult = summarizeCopyResult( props.savedObjectTarget, diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx index 2f96646844a35..57b55ac5a2618 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -12,10 +12,10 @@ import { EuiIconTip, EuiLoadingSpinner, EuiSelectable } from '@elastic/eui'; import React, { lazy, Suspense } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common'; import { getSpaceAvatarComponent } from '../../space_avatar'; +import type { SpacesDataEntry } from '../../types'; // No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. const LazySpaceAvatar = lazy(() => @@ -23,7 +23,7 @@ const LazySpaceAvatar = lazy(() => ); interface Props { - spaces: Space[]; + spaces: SpacesDataEntry[]; selectedSpaceIds: string[]; disabledSpaceIds: Set; onChange: (selectedSpaceIds: string[]) => void; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx index 6d14584ac21a9..932b8bc9254f6 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx @@ -17,9 +17,8 @@ import { } from '@elastic/eui'; import React, { lazy, Suspense, useState } from 'react'; -import type { Space } from 'src/plugins/spaces_oss/common'; - import { getSpaceAvatarComponent } from '../../space_avatar'; +import type { SpacesDataEntry } from '../../types'; import type { SummarizedCopyToSpaceResult } from '../lib'; import type { ImportRetry } from '../types'; import { CopyStatusSummaryIndicator } from './copy_status_summary_indicator'; @@ -31,7 +30,7 @@ const LazySpaceAvatar = lazy(() => ); interface Props { - space: Space; + space: SpacesDataEntry; summarizedCopyResult: SummarizedCopyToSpaceResult; retries: ImportRetry[]; onRetriesChange: (retries: ImportRetry[]) => void; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx index e2db8e7fb7480..d58490a86b7f5 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx @@ -25,15 +25,15 @@ import type { SavedObjectsImportAmbiguousConflictError, SavedObjectsImportConflictError, } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { SpacesDataEntry } from '../../types'; import type { SummarizedCopyToSpaceResult } from '../lib'; import type { ImportRetry } from '../types'; import { CopyStatusIndicator } from './copy_status_indicator'; interface Props { summarizedCopyResult: SummarizedCopyToSpaceResult; - space: Space; + space: SpacesDataEntry; retries: ImportRetry[]; onRetriesChange: (retries: ImportRetry[]) => void; destinationMap: Map; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx deleted file mode 100644 index 7818e648dd1cf..0000000000000 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { lazy } from 'react'; -import useAsync from 'react-use/lib/useAsync'; - -import { i18n } from '@kbn/i18n'; -import type { StartServicesAccessor } from 'src/core/public'; -import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; - -import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public'; -import type { PluginsStart } from '../plugin'; -import { SuspenseErrorBoundary } from '../suspense_error_boundary'; -import type { CopyToSpaceFlyoutProps } from './components'; -import { getCopyToSpaceFlyoutComponent } from './components'; - -const LazyCopyToSpaceFlyout = lazy(() => - getCopyToSpaceFlyoutComponent().then((component) => ({ default: component })) -); - -interface WrapperProps { - getStartServices: StartServicesAccessor; - props: CopyToSpaceFlyoutProps; -} - -const Wrapper = ({ getStartServices, props }: WrapperProps) => { - const { value: startServices = [{ notifications: undefined }] } = useAsync(getStartServices); - const [{ notifications }] = startServices; - - if (!notifications) { - return null; - } - - return ( - - - - ); -}; - -export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { - public id: string = 'copy_saved_objects_to_space'; - - public euiAction = { - name: i18n.translate('xpack.spaces.management.copyToSpace.actionTitle', { - defaultMessage: 'Copy to space', - }), - description: i18n.translate('xpack.spaces.management.copyToSpace.actionDescription', { - defaultMessage: 'Make a copy of this saved object in one or more spaces', - }), - icon: 'copy', - type: 'icon', - available: (object: SavedObjectsManagementRecord) => { - return object.meta.namespaceType !== 'agnostic' && !object.meta.hiddenType; - }, - onClick: (object: SavedObjectsManagementRecord) => { - this.start(object); - }, - }; - - constructor(private getStartServices: StartServicesAccessor) { - super(); - } - - public render = () => { - if (!this.record) { - throw new Error('No record available! `render()` was likely called before `start()`.'); - } - - const props: CopyToSpaceFlyoutProps = { - onClose: this.onClose, - savedObjectTarget: { - type: this.record.type, - id: this.record.id, - namespaces: this.record.namespaces ?? [], - title: this.record.meta.title, - icon: this.record.meta.icon, - }, - }; - - return ; - }; - - private onClose = () => { - this.finish(); - }; -} diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts deleted file mode 100644 index f55a7d8054608..0000000000000 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { coreMock } from 'src/core/public/mocks'; -import { savedObjectsManagementPluginMock } from 'src/plugins/saved_objects_management/public/mocks'; - -import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; -import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service'; - -describe('CopySavedObjectsToSpaceService', () => { - describe('#setup', () => { - it('registers the CopyToSpaceSavedObjectsManagementAction', () => { - const { getStartServices } = coreMock.createSetup(); - const deps = { - savedObjectsManagementSetup: savedObjectsManagementPluginMock.createSetupContract(), - getStartServices, - }; - - const service = new CopySavedObjectsToSpaceService(); - service.setup(deps); - - expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledTimes(1); - expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledWith( - expect.any(CopyToSpaceSavedObjectsManagementAction) - ); - }); - }); -}); diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts deleted file mode 100644 index 17bb26cbf7f11..0000000000000 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { StartServicesAccessor } from 'src/core/public'; -import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; - -import type { PluginsStart } from '../plugin'; -import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; - -interface SetupDeps { - savedObjectsManagementSetup: SavedObjectsManagementPluginSetup; - getStartServices: StartServicesAccessor; -} - -export class CopySavedObjectsToSpaceService { - public setup({ savedObjectsManagementSetup, getStartServices }: SetupDeps) { - const action = new CopyToSpaceSavedObjectsManagementAction(getStartServices); - savedObjectsManagementSetup.actions.register(action); - } -} diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts index abbbc8ba1368f..2443c8443c091 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts @@ -6,4 +6,4 @@ */ export { getCopyToSpaceFlyoutComponent } from './components'; -export { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service'; +export type { CopyToSpaceFlyoutProps, CopyToSpaceSavedObjectTarget } from './types'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts index 882ee234ca5dc..70a5cadd527bc 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts @@ -5,6 +5,9 @@ * 2.0. */ +export type { FailedImport, ProcessedImportResponse } from './process_import_response'; +export { processImportResponse } from './process_import_response'; + export type { SummarizedCopyToSpaceResult, SummarizedSavedObjectResult, diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/process_import_response.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/process_import_response.ts new file mode 100644 index 0000000000000..cf402b0dd4ec2 --- /dev/null +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/process_import_response.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 type { + SavedObjectsImportAmbiguousConflictError, + SavedObjectsImportConflictError, + SavedObjectsImportFailure, + SavedObjectsImportMissingReferencesError, + SavedObjectsImportResponse, + SavedObjectsImportSuccess, + SavedObjectsImportUnknownError, + SavedObjectsImportUnsupportedTypeError, +} from 'src/core/public'; + +export interface FailedImport { + obj: Omit; + error: + | SavedObjectsImportConflictError + | SavedObjectsImportAmbiguousConflictError + | SavedObjectsImportUnsupportedTypeError + | SavedObjectsImportMissingReferencesError + | SavedObjectsImportUnknownError; +} + +export interface ProcessedImportResponse { + success: boolean; + failedImports: FailedImport[]; + successfulImports: SavedObjectsImportSuccess[]; +} + +// This is derived from the function of the same name in the savedObjectsManagement plugin +export function processImportResponse( + response: SavedObjectsImportResponse +): ProcessedImportResponse { + const { success, errors = [], successResults = [] } = response; + const failedImports = errors.map(({ error, ...obj }) => ({ obj, error })); + return { + success, + failedImports, + successfulImports: successResults, + }; +} diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts index 6a3d82aaef59c..298b89050b637 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts @@ -5,13 +5,8 @@ * 2.0. */ -import type { - FailedImport, - ProcessedImportResponse, - SavedObjectsManagementRecord, -} from 'src/plugins/saved_objects_management/public'; - -import type { SavedObjectTarget } from '../types'; +import type { FailedImport, ProcessedImportResponse } from '../lib'; +import type { CopyToSpaceSavedObjectTarget } from '../types'; import { summarizeCopyResult } from './summarize_copy_result'; // Sample data references: @@ -29,7 +24,7 @@ const OBJECTS = { namespaces: [], icon: 'dashboardApp', title: 'my-dashboard-title', - } as Required, + } as Required, MY_DASHBOARD: { type: 'dashboard', id: 'foo', @@ -43,7 +38,7 @@ const OBJECTS = { { type: 'visualization', id: 'foo', name: 'Visualization foo' }, { type: 'visualization', id: 'bar', name: 'Visualization bar' }, ], - } as SavedObjectsManagementRecord, + }, VISUALIZATION_FOO: { type: 'visualization', id: 'bar', @@ -54,7 +49,7 @@ const OBJECTS = { hiddenType: false, }, references: [{ type: 'index-pattern', id: 'foo', name: 'Index pattern foo' }], - } as SavedObjectsManagementRecord, + }, VISUALIZATION_BAR: { type: 'visualization', id: 'baz', @@ -65,7 +60,7 @@ const OBJECTS = { hiddenType: false, }, references: [{ type: 'index-pattern', id: 'bar', name: 'Index pattern bar' }], - } as SavedObjectsManagementRecord, + }, INDEX_PATTERN_FOO: { type: 'index-pattern', id: 'foo', @@ -76,7 +71,7 @@ const OBJECTS = { hiddenType: false, }, references: [], - } as SavedObjectsManagementRecord, + }, INDEX_PATTERN_BAR: { type: 'index-pattern', id: 'bar', @@ -87,7 +82,7 @@ const OBJECTS = { hiddenType: false, }, references: [], - } as SavedObjectsManagementRecord, + }, }; interface ObjectProperties { diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts index 68baba12d8b98..206952774ff9a 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts @@ -9,12 +9,9 @@ import type { SavedObjectsImportAmbiguousConflictError, SavedObjectsImportConflictError, } from 'src/core/public'; -import type { - FailedImport, - ProcessedImportResponse, -} from 'src/plugins/saved_objects_management/public'; -import type { SavedObjectTarget } from '../types'; +import type { FailedImport, ProcessedImportResponse } from '../lib'; +import type { CopyToSpaceSavedObjectTarget } from '../types'; export interface SummarizedSavedObjectResult { type: string; @@ -68,7 +65,7 @@ export type SummarizedCopyToSpaceResult = | ProcessingResponse; export function summarizeCopyResult( - savedObjectTarget: Required, + savedObjectTarget: Required, copyResult: ProcessedImportResponse | undefined ): SummarizedCopyToSpaceResult { const conflicts = copyResult?.failedImports.filter(isAnyConflict) ?? []; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts index 60a4e176f40bb..732d779c6f616 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts @@ -20,7 +20,24 @@ export interface CopySavedObjectsToSpaceResponse { [spaceId: string]: SavedObjectsImportResponse; } -export interface SavedObjectTarget { +/** + * Properties for the CopyToSpaceFlyout. + */ +export interface CopyToSpaceFlyoutProps { + /** + * The object to render the flyout for. + */ + savedObjectTarget: CopyToSpaceSavedObjectTarget; + /** + * Optional callback when the flyout is closed. + */ + onClose?: () => void; +} + +/** + * Describes the target saved object during a copy operation. + */ +export interface CopyToSpaceSavedObjectTarget { /** * The object's type. */ diff --git a/x-pack/plugins/spaces/public/index.ts b/x-pack/plugins/spaces/public/index.ts index 08dda35a6eb10..d13f8f48e6719 100644 --- a/x-pack/plugins/spaces/public/index.ts +++ b/x-pack/plugins/spaces/public/index.ts @@ -11,10 +11,28 @@ export { getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_avata export { SpacesPluginSetup, SpacesPluginStart } from './plugin'; -export type { GetAllSpacesPurpose, GetSpaceResult } from '../common'; +export type { Space, GetAllSpacesPurpose, GetSpaceResult } from '../common'; -// re-export types from oss definition -export type { Space } from 'src/plugins/spaces_oss/common'; +export type { SpacesData, SpacesDataEntry, SpacesApi } from './types'; + +export type { + CopyToSpaceFlyoutProps, + CopyToSpaceSavedObjectTarget, +} from './copy_saved_objects_to_space'; + +export type { + LegacyUrlConflictProps, + ShareToSpaceFlyoutProps, + ShareToSpaceSavedObjectTarget, +} from './share_saved_objects_to_space'; + +export type { SpaceAvatarProps } from './space_avatar'; + +export type { SpaceListProps } from './space_list'; + +export type { SpacesContextProps, SpacesReactContextValue } from './spaces_context'; + +export type { LazyComponentFn, SpacesApiUi, SpacesApiUiComponent } from './ui_api'; export const plugin = () => { return new SpacesPlugin(); diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx index f3ed578a94962..4c808f9a47582 100644 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx +++ b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx @@ -13,9 +13,9 @@ import useAsyncFn from 'react-use/lib/useAsyncFn'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import type { Space } from '../../../../common'; import type { SpacesManager } from '../../../spaces_manager'; interface Props { diff --git a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx index 92b68426d172e..dc78441c3ba02 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx @@ -12,8 +12,8 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { NotificationsStart } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import type { SpacesManager } from '../../spaces_manager'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 7481676430307..9860d701ee4ab 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -11,10 +11,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import type { KibanaFeatureConfig } from '../../../../../features/public'; +import type { Space } from '../../../../common'; import { SectionPanel } from '../section_panel'; import { FeatureTable } from './feature_table'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx index 78ea73741a8ad..2371a97370b53 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx @@ -25,9 +25,9 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import type { AppCategory } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; import type { KibanaFeatureConfig } from '../../../../../features/public'; +import type { Space } from '../../../../common'; import { getEnabledFeatures } from '../../lib/feature_utils'; interface Props { diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx index 5a7ae701e97e0..bd0621e539ed0 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -23,10 +23,10 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { SectionLoading } from '../../../../../../src/plugins/es_ui_shared/public'; import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public'; +import type { Space } from '../../../common'; import { isReservedSpace } from '../../../common'; import { getSpacesFeatureDescription } from '../../constants'; import { getSpaceColor, getSpaceInitials } from '../../space_avatar'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx index da32ea79724ad..6415444a05620 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx @@ -9,8 +9,8 @@ import { EuiBadge, EuiToolTip } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import { isReservedSpace } from '../../../common'; interface Props { diff --git a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts index d332b3e34c0a6..7598e18d6680c 100644 --- a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts @@ -5,9 +5,8 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; - import type { KibanaFeatureConfig } from '../../../../features/common'; +import type { Space } from '../../../common'; export function getEnabledFeatures(features: KibanaFeatureConfig[], space: Partial) { return features.filter((feature) => !(space.disabledFeatures || []).includes(feature.id)); diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index 200b868a1b103..e40f92bd54486 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -26,10 +26,10 @@ import type { NotificationsStart, ScopedHistory, } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public'; import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public'; +import type { Space } from '../../../common'; import { isReservedSpace } from '../../../common'; import { DEFAULT_SPACE_ID } from '../../../common/constants'; import { getSpacesFeatureDescription } from '../../constants'; diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index 4bf8b46fecb75..a7a7591b94562 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -12,13 +12,13 @@ import { Route, Router, Switch, useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from 'src/core/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { APP_WRAPPER_CLASS } from '../../../../../src/core/public'; import { KibanaContextProvider, RedirectAppLinks, } from '../../../../../src/plugins/kibana_react/public'; +import type { Space } from '../../common'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; diff --git a/src/plugins/spaces_oss/public/api.mock.ts b/x-pack/plugins/spaces/public/mocks.ts similarity index 68% rename from src/plugins/spaces_oss/public/api.mock.ts rename to x-pack/plugins/spaces/public/mocks.ts index 9ad7599b5ae61..897f58e1d649c 100644 --- a/src/plugins/spaces_oss/public/api.mock.ts +++ b/x-pack/plugins/spaces/public/mocks.ts @@ -1,14 +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. + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. */ import { of } from 'rxjs'; -import type { SpacesApi, SpacesApiUi, SpacesApiUiComponent } from './api'; +import type { SpacesPluginStart } from './plugin'; +import type { SpacesApi } from './types'; +import type { SpacesApiUi, SpacesApiUiComponent } from './ui_api'; const createApiMock = (): jest.Mocked => ({ getActiveSpace$: jest.fn().mockReturnValue(of()), @@ -24,6 +25,7 @@ const createApiUiMock = () => { const mock: SpacesApiUiMock = { components: createApiUiComponentsMock(), redirectLegacyUrl: jest.fn(), + useSpaces: jest.fn(), }; return mock; @@ -35,6 +37,7 @@ const createApiUiComponentsMock = () => { const mock: SpacesApiUiComponentMock = { getSpacesContextProvider: jest.fn(), getShareToSpaceFlyout: jest.fn(), + getCopyToSpaceFlyout: jest.fn(), getSpaceList: jest.fn(), getLegacyUrlConflict: jest.fn(), getSpaceAvatar: jest.fn(), @@ -43,6 +46,8 @@ const createApiUiComponentsMock = () => { return mock; }; -export const spacesApiMock = { - create: createApiMock, +const createStartContract = (): jest.Mocked => createApiMock(); + +export const spacesPluginMock = { + createStartContract, }; diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index 392a95c445921..5fafe151dade9 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -21,8 +21,8 @@ import React, { Component, lazy, Suspense } from 'react'; import type { InjectedIntl } from '@kbn/i18n/react'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import type { ApplicationStart, Capabilities } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import { addSpaceIdToPath, ENTER_SPACE_PATH, SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common'; import { getSpaceAvatarComponent } from '../../space_avatar'; import { ManageSpacesButton } from './manage_spaces_button'; diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index 1653506c550f6..41a05a38fa305 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -11,8 +11,8 @@ import React, { Component, lazy, Suspense } from 'react'; import type { Subscription } from 'rxjs'; import type { ApplicationStart, Capabilities } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../common'; import { getSpaceAvatarComponent } from '../space_avatar'; import type { SpacesManager } from '../spaces_manager'; import { SpacesDescription } from './components/spaces_description'; diff --git a/x-pack/plugins/spaces/public/plugin.test.ts b/x-pack/plugins/spaces/public/plugin.test.ts index 39478ca2fd9be..43b701c48b2c2 100644 --- a/x-pack/plugins/spaces/public/plugin.test.ts +++ b/x-pack/plugins/spaces/public/plugin.test.ts @@ -12,7 +12,6 @@ import { createManagementSectionMock, managementPluginMock, } from 'src/plugins/management/public/mocks'; -import { spacesOssPluginMock } from 'src/plugins/spaces_oss/public/mocks'; import { SpacesPlugin } from './plugin'; @@ -20,12 +19,9 @@ describe('Spaces plugin', () => { describe('#setup', () => { it('should register the spaces API and the space selector app', () => { const coreSetup = coreMock.createSetup(); - const spacesOss = spacesOssPluginMock.createSetupContract(); const plugin = new SpacesPlugin(); - plugin.setup(coreSetup, { spacesOss }); - - expect(spacesOss.registerSpacesApi).toHaveBeenCalledTimes(1); + plugin.setup(coreSetup, {}); expect(coreSetup.application.register).toHaveBeenCalledWith( expect.objectContaining({ @@ -39,7 +35,6 @@ describe('Spaces plugin', () => { it('should register the management and feature catalogue sections when the management and home plugins are both available', () => { const coreSetup = coreMock.createSetup(); - const spacesOss = spacesOssPluginMock.createSetupContract(); const home = homePluginMock.createSetupContract(); const management = managementPluginMock.createSetupContract(); @@ -50,7 +45,6 @@ describe('Spaces plugin', () => { const plugin = new SpacesPlugin(); plugin.setup(coreSetup, { - spacesOss, management, home, }); @@ -71,11 +65,10 @@ describe('Spaces plugin', () => { it('should register the advanced settings components if the advanced_settings plugin is available', () => { const coreSetup = coreMock.createSetup(); - const spacesOss = spacesOssPluginMock.createSetupContract(); const advancedSettings = advancedSettingsMock.createSetupContract(); const plugin = new SpacesPlugin(); - plugin.setup(coreSetup, { spacesOss, advancedSettings }); + plugin.setup(coreSetup, { advancedSettings }); expect(advancedSettings.component.register.mock.calls).toMatchInlineSnapshot(` Array [ @@ -97,11 +90,10 @@ describe('Spaces plugin', () => { describe('#start', () => { it('should register the spaces nav control', () => { const coreSetup = coreMock.createSetup(); - const spacesOss = spacesOssPluginMock.createSetupContract(); const coreStart = coreMock.createStart(); const plugin = new SpacesPlugin(); - plugin.setup(coreSetup, { spacesOss }); + plugin.setup(coreSetup, {}); plugin.start(coreStart); diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index 1ba1cd1a1f3d4..782cb3ba708a3 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -9,26 +9,21 @@ import type { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; import type { HomePublicPluginSetup } from 'src/plugins/home/public'; import type { ManagementSetup, ManagementStart } from 'src/plugins/management/public'; -import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; -import type { SpacesApi, SpacesOssPluginSetup } from 'src/plugins/spaces_oss/public'; import type { FeaturesPluginStart } from '../../features/public'; import { AdvancedSettingsService } from './advanced_settings'; -import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; import { ManagementService } from './management'; import { initSpacesNavControl } from './nav_control'; -import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space'; import { spaceSelectorApp } from './space_selector'; import { SpacesManager } from './spaces_manager'; +import type { SpacesApi } from './types'; import { getUiApi } from './ui_api'; export interface PluginsSetup { - spacesOss: SpacesOssPluginSetup; advancedSettings?: AdvancedSettingsSetup; home?: HomePublicPluginSetup; management?: ManagementSetup; - savedObjectsManagement?: SavedObjectsManagementPluginSetup; } export interface PluginsStart { @@ -84,27 +79,12 @@ export class SpacesPlugin implements Plugin ); interface Props { - spaces: ShareToSpaceTarget[]; + spaces: SpacesDataEntry[]; aliasesToDisable: InternalLegacyUrlAliasTarget[]; } @@ -38,10 +38,7 @@ export const AliasTable: FunctionComponent = ({ spaces, aliasesToDisable const spacesMap = useMemo( () => - spaces.reduce( - (acc, space) => acc.set(space.id, space), - new Map() - ), + spaces.reduce((acc, space) => acc.set(space.id, space), new Map()), [spaces] ); const filteredAliasesToDisable = useMemo( diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx index f053c081ab589..8aaf455204c9b 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx @@ -7,8 +7,7 @@ import React from 'react'; -import type { LegacyUrlConflictProps } from 'src/plugins/spaces_oss/public'; - +import type { LegacyUrlConflictProps } from '../types'; import type { InternalProps } from './legacy_url_conflict_internal'; export const getLegacyUrlConflict = async ( diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx index 1ebde52a734c6..95bf7b404db34 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx @@ -18,9 +18,9 @@ import { first } from 'rxjs/operators'; import { FormattedMessage } from '@kbn/i18n/react'; import type { ApplicationStart, StartServicesAccessor } from 'src/core/public'; -import type { LegacyUrlConflictProps } from 'src/plugins/spaces_oss/public'; import type { PluginsStart } from '../../plugin'; +import type { LegacyUrlConflictProps } from '../types'; import { DEFAULT_OBJECT_NOUN } from './constants'; export interface InternalProps { diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/relatives_footer.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/relatives_footer.tsx index ea3f29724e0d5..97318ea5312be 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/relatives_footer.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/relatives_footer.tsx @@ -10,7 +10,8 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import type { SavedObjectReferenceWithContext } from 'src/core/public'; -import type { ShareToSpaceSavedObjectTarget } from 'src/plugins/spaces_oss/public'; + +import type { ShareToSpaceSavedObjectTarget } from '../types'; interface Props { savedObjectTarget: ShareToSpaceSavedObjectTarget; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx index fad819d35e18a..4e45487a20562 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -29,7 +29,7 @@ import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; import { DocumentationLinksService } from '../../lib'; import { getSpaceAvatarComponent } from '../../space_avatar'; import { useSpaces } from '../../spaces_context'; -import type { ShareToSpaceTarget } from '../../types'; +import type { SpacesDataEntry } from '../../types'; import type { ShareOptions } from '../types'; import { NoSpacesAvailable } from './no_spaces_available'; @@ -39,7 +39,7 @@ const LazySpaceAvatar = lazy(() => ); interface Props { - spaces: ShareToSpaceTarget[]; + spaces: SpacesDataEntry[]; shareOptions: ShareOptions; onChange: (selectedSpaceIds: string[]) => void; enableCreateNewSpaceLink: boolean; @@ -248,7 +248,7 @@ export const SelectableSpacesControl = (props: Props) => { * Gets additional props for the selection option. */ function getAdditionalProps( - space: ShareToSpaceTarget, + space: SpacesDataEntry, activeSpaceId: string | false, checked: boolean ) { @@ -259,7 +259,7 @@ function getAdditionalProps( checked: 'on' as 'on', }; } - if (space.cannotShareToSpace) { + if (!space.isAuthorizedForPurpose('shareSavedObjectsIntoSpace')) { return { append: ( <> @@ -280,11 +280,11 @@ function getAdditionalProps( } /** - * Given the active space, create a comparator to sort a ShareToSpaceTarget array so that the active space is at the beginning, and space(s) for + * Given the active space, create a comparator to sort a SpacesDataEntry array so that the active space is at the beginning, and space(s) for * which the current feature is disabled are all at the end. */ function createSpacesComparator(activeSpaceId: string | false) { - return (a: ShareToSpaceTarget, b: ShareToSpaceTarget) => { + return (a: SpacesDataEntry, b: SpacesDataEntry) => { if (a.id === activeSpaceId) { return -1; } diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx index 7151f72583d6a..07439542bf839 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx @@ -24,12 +24,12 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { ALL_SPACES_ID } from '../../../common/constants'; import { DocumentationLinksService } from '../../lib'; import { useSpaces } from '../../spaces_context'; -import type { ShareToSpaceTarget } from '../../types'; +import type { SpacesDataEntry } from '../../types'; import type { ShareOptions } from '../types'; import { SelectableSpacesControl } from './selectable_spaces_control'; interface Props { - spaces: ShareToSpaceTarget[]; + spaces: SpacesDataEntry[]; objectNoun: string; canShareToAllSpaces: boolean; shareOptions: ShareOptions; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx index dc2a358a653ab..e0f25277c50fe 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import type { ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public'; +import type { ShareToSpaceFlyoutProps } from '../types'; export const getShareToSpaceFlyoutComponent = async (): Promise< React.FC diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx index f02cae7674058..8b435865c760f 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx @@ -14,8 +14,8 @@ import React from 'react'; import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest'; import type { SavedObjectReferenceWithContext } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import { ALL_SPACES_ID } from '../../../common/constants'; import { CopyToSpaceFlyoutInternal } from '../../copy_saved_objects_to_space/components/copy_to_space_flyout_internal'; import { getSpacesContextProviderWrapper } from '../../spaces_context'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx index 712adeb26bccb..076825e7c70aa 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx @@ -26,17 +26,17 @@ import React, { lazy, Suspense, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { SavedObjectReferenceWithContext, ToastsStart } from 'src/core/public'; -import type { - ShareToSpaceFlyoutProps, - ShareToSpaceSavedObjectTarget, -} from 'src/plugins/spaces_oss/public'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; import { getCopyToSpaceFlyoutComponent } from '../../copy_saved_objects_to_space'; import { useSpaces } from '../../spaces_context'; import type { SpacesManager } from '../../spaces_manager'; -import type { ShareToSpaceTarget } from '../../types'; -import type { ShareOptions } from '../types'; +import type { SpacesDataEntry } from '../../types'; +import type { + ShareOptions, + ShareToSpaceFlyoutProps, + ShareToSpaceSavedObjectTarget, +} from '../types'; import { AliasTable } from './alias_table'; import { DEFAULT_OBJECT_NOUN } from './constants'; import { RelativesFooter } from './relatives_footer'; @@ -124,7 +124,7 @@ function createDefaultChangeSpacesHandler( } export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { - const { spacesManager, shareToSpacesDataPromise, services } = useSpaces(); + const { spacesManager, spacesDataPromise, services } = useSpaces(); const { notifications } = services; const toastNotifications = notifications!.toasts; @@ -168,7 +168,7 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { const [{ isLoading, spaces, referenceGraph, aliasTargets }, setSpacesState] = useState<{ isLoading: boolean; - spaces: ShareToSpaceTarget[]; + spaces: SpacesDataEntry[]; referenceGraph: SavedObjectReferenceWithContext[]; aliasTargets: InternalLegacyUrlAliasTarget[]; }>({ isLoading: true, spaces: [], referenceGraph: [], aliasTargets: [] }); @@ -176,9 +176,9 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { const { type, id } = savedObjectTarget; const getShareableReferences = spacesManager.getShareableReferences([{ type, id }]); const getPermissions = spacesManager.getShareSavedObjectPermissions(type); - Promise.all([shareToSpacesDataPromise, getShareableReferences, getPermissions]) - .then(([shareToSpacesData, shareableReferences, permissions]) => { - const activeSpaceId = !enableSpaceAgnosticBehavior && shareToSpacesData.activeSpaceId; + Promise.all([spacesDataPromise, getShareableReferences, getPermissions]) + .then(([spacesData, shareableReferences, permissions]) => { + const activeSpaceId = !enableSpaceAgnosticBehavior && spacesData.activeSpaceId; const selectedSpaceIds = savedObjectTarget.namespaces.filter( (spaceId) => spaceId !== activeSpaceId ); @@ -189,13 +189,13 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { setCanShareToAllSpaces(permissions.shareToAllSpaces); setSpacesState({ isLoading: false, - spaces: [...shareToSpacesData.spacesMap].map(([, spaceTarget]) => spaceTarget), + spaces: [...spacesData.spacesMap].map(([, spaceTarget]) => spaceTarget), referenceGraph: shareableReferences.objects, aliasTargets: shareableReferences.objects.reduce( (acc, x) => { for (const space of x.spacesWithMatchingAliases ?? []) { if (space !== '?') { - const spaceExists = shareToSpacesData.spacesMap.has(space); + const spaceExists = spacesData.spacesMap.has(space); // If the user does not have privileges to view all spaces, they will be redacted; we cannot attempt to disable aliases for redacted spaces. acc.push({ targetSpace: space, targetType: x.type, sourceId: x.id, spaceExists }); } @@ -216,7 +216,7 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { }, [ savedObjectTarget, spacesManager, - shareToSpacesDataPromise, + spacesDataPromise, toastNotifications, enableSpaceAgnosticBehavior, ]); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx index 7f8c659805c45..1841d634c6482 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx @@ -12,12 +12,12 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { ShareToSpaceTarget } from '../../types'; +import type { SpacesDataEntry } from '../../types'; import type { ShareOptions } from '../types'; import { ShareModeControl } from './share_mode_control'; interface Props { - spaces: ShareToSpaceTarget[]; + spaces: SpacesDataEntry[]; objectNoun: string; onUpdate: (shareOptions: ShareOptions) => void; shareOptions: ShareOptions; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts index beed0fd9d592a..fe90ee8d6a8a9 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/index.ts @@ -5,6 +5,10 @@ * 2.0. */ -export { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space_service'; export { getShareToSpaceFlyoutComponent, getLegacyUrlConflict } from './components'; export { createRedirectLegacyUrl } from './utils'; +export type { + LegacyUrlConflictProps, + ShareToSpaceFlyoutProps, + ShareToSpaceSavedObjectTarget, +} from './types'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts deleted file mode 100644 index eb973a48ef879..0000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { savedObjectsManagementPluginMock } from 'src/plugins/saved_objects_management/public/mocks'; - -import { uiApiMock } from '../ui_api/mocks'; -import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; -// import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; -import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space_service'; - -describe('ShareSavedObjectsToSpaceService', () => { - describe('#setup', () => { - it('registers the ShareToSpaceSavedObjectsManagement Action and Column', () => { - const deps = { - savedObjectsManagementSetup: savedObjectsManagementPluginMock.createSetupContract(), - spacesApiUi: uiApiMock.create(), - }; - - const service = new ShareSavedObjectsToSpaceService(); - service.setup(deps); - - expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledTimes(1); - expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledWith( - expect.any(ShareToSpaceSavedObjectsManagementAction) - ); - - // expect(deps.savedObjectsManagementSetup.columns.register).toHaveBeenCalledTimes(1); - // expect(deps.savedObjectsManagementSetup.columns.register).toHaveBeenCalledWith( - // expect.any(ShareToSpaceSavedObjectsManagementColumn) - // ); - expect(deps.savedObjectsManagementSetup.columns.register).not.toHaveBeenCalled(); // ensure this test fails after column code is uncommented - }); - }); -}); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts deleted file mode 100644 index bc70347760465..0000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; - -import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; -// import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; - -interface SetupDeps { - savedObjectsManagementSetup: SavedObjectsManagementPluginSetup; - spacesApiUi: SpacesApiUi; -} - -export class ShareSavedObjectsToSpaceService { - public setup({ savedObjectsManagementSetup, spacesApiUi }: SetupDeps) { - const action = new ShareToSpaceSavedObjectsManagementAction(spacesApiUi); - savedObjectsManagementSetup.actions.register(action); - // Note: this column is hidden for now because no saved objects are shareable. It should be uncommented when at least one saved object type is multi-namespace. - // const column = new ShareToSpaceSavedObjectsManagementColumn(spacesApiUi); - // savedObjectsManagementSetup.columns.register(column); - } -} diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts index be8165e822736..1beccaa546282 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts @@ -17,3 +17,126 @@ export type ImportRetry = Omit; export interface ShareSavedObjectsToSpaceResponse { [spaceId: string]: SavedObjectsImportResponse; } + +/** + * Properties for the LegacyUrlConflict component. + */ +export interface LegacyUrlConflictProps { + /** + * The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different + * **object**_. + * + * Default value is 'object'. + */ + objectNoun?: string; + /** + * The ID of the object that is currently shown on the page. + */ + currentObjectId: string; + /** + * The ID of the other object that the legacy URL alias points to. + */ + otherObjectId: string; + /** + * The path to use for the new URL, optionally including `search` and/or `hash` URL components. + */ + otherObjectPath: string; +} + +/** + * Properties for the ShareToSpaceFlyout. + */ +export interface ShareToSpaceFlyoutProps { + /** + * The object to render the flyout for. + */ + savedObjectTarget: ShareToSpaceSavedObjectTarget; + /** + * The EUI icon that is rendered in the flyout's title. + * + * Default is 'share'. + */ + flyoutIcon?: string; + /** + * The string that is rendered in the flyout's title. + * + * Default is 'Edit spaces for object'. + */ + flyoutTitle?: string; + /** + * When enabled, if the object is not yet shared to multiple spaces, a callout will be displayed that suggests the user might want to + * create a copy instead. + * + * Default value is false. + */ + enableCreateCopyCallout?: boolean; + /** + * When enabled, if no other spaces exist _and_ the user has the appropriate privileges, a sentence will be displayed that suggests the + * user might want to create a space. + * + * Default value is false. + */ + enableCreateNewSpaceLink?: boolean; + /** + * When set to 'within-space' (default), the flyout behaves like it is running on a page within the active space, and it will prevent the + * user from removing the object from the active space. + * + * Conversely, when set to 'outside-space', the flyout behaves like it is running on a page outside of any space, so it will allow the + * user to remove the object from the active space. + */ + behaviorContext?: 'within-space' | 'outside-space'; + /** + * Optional handler that is called when the user has saved changes and there are spaces to be added to and/or removed from the object and + * its relatives. If this is not defined, a default handler will be used that calls `/api/spaces/_update_objects_spaces` and displays a + * toast indicating what occurred. + */ + changeSpacesHandler?: ( + objects: Array<{ type: string; id: string }>, + spacesToAdd: string[], + spacesToRemove: string[] + ) => Promise; + /** + * Optional callback when the target object and its relatives are updated. + */ + onUpdate?: (updatedObjects: Array<{ type: string; id: string }>) => void; + /** + * Optional callback when the flyout is closed. + */ + onClose?: () => void; +} + +/** + * Describes the target saved object during a share operation. + */ +export interface ShareToSpaceSavedObjectTarget { + /** + * The object's type. + */ + type: string; + /** + * The object's ID. + */ + id: string; + /** + * The namespaces that the object currently exists in. + */ + namespaces: string[]; + /** + * The EUI icon that is rendered in the flyout's subtitle. + * + * Default is 'empty'. + */ + icon?: string; + /** + * The string that is rendered in the flyout's subtitle. + * + * Default is `${type} [id=${id}]`. + */ + title?: string; + /** + * The string that is used to describe the object in several places, e.g., _Make **object** available in selected spaces only_. + * + * Default value is 'object'. + */ + noun?: string; +} diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts index 338b2e8c94e0d..d427b1bc05242 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts @@ -9,9 +9,9 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from 'src/core/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import type { PluginsStart } from '../../plugin'; +import type { SpacesApiUi } from '../../ui_api'; import { DEFAULT_OBJECT_NOUN } from '../components/constants'; export function createRedirectLegacyUrl( diff --git a/x-pack/plugins/spaces/public/space_avatar/index.ts b/x-pack/plugins/spaces/public/space_avatar/index.ts index 86d94738f2c79..a8d67b3964665 100644 --- a/x-pack/plugins/spaces/public/space_avatar/index.ts +++ b/x-pack/plugins/spaces/public/space_avatar/index.ts @@ -7,3 +7,4 @@ export { getSpaceAvatarComponent } from './space_avatar'; export * from './space_attributes'; +export type { SpaceAvatarProps } from './types'; diff --git a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts index 682a61c6f23a5..047e544f94056 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts +++ b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts @@ -7,8 +7,7 @@ import { VISUALIZATION_COLORS } from '@elastic/eui'; -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../common'; import { MAX_SPACE_INITIALS } from '../../common'; // code point for lowercase "a" diff --git a/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx index a2d58a12c5eaa..2bd86effcc656 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx +++ b/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import type { SpaceAvatarProps } from 'src/plugins/spaces_oss/public'; +import type { SpaceAvatarProps } from './types'; export const getSpaceAvatarComponent = async (): Promise> => { const { SpaceAvatarInternal } = await import('./space_avatar_internal'); diff --git a/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx index 91b4dbf8a964e..b8fd5ed77f488 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx +++ b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx @@ -10,25 +10,11 @@ import { EuiAvatar, isValidHex } from '@elastic/eui'; import type { FC } from 'react'; import React from 'react'; -import type { Space } from 'src/plugins/spaces_oss/common'; - import { MAX_SPACE_INITIALS } from '../../common'; import { getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_attributes'; +import type { SpaceAvatarProps } from './types'; -interface Props { - space: Partial; - size?: 's' | 'm' | 'l' | 'xl'; - className?: string; - announceSpaceName?: boolean; - /** - * This property is passed to the underlying `EuiAvatar` component. If enabled, the SpaceAvatar will have a grayed out appearance. For - * example, this can be useful when rendering a list of spaces for a specific feature, if the feature is disabled in one of those spaces. - * Default: false. - */ - isDisabled?: boolean; -} - -export const SpaceAvatarInternal: FC = (props: Props) => { +export const SpaceAvatarInternal: FC = (props: SpaceAvatarProps) => { const { space, size, announceSpaceName, ...rest } = props; const spaceName = space.name ? space.name.trim() : ''; diff --git a/x-pack/plugins/spaces/public/space_avatar/types.ts b/x-pack/plugins/spaces/public/space_avatar/types.ts new file mode 100644 index 0000000000000..365c71eeeea73 --- /dev/null +++ b/x-pack/plugins/spaces/public/space_avatar/types.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Space } from '../../common'; + +/** + * Properties for the SpaceAvatar component. + */ +export interface SpaceAvatarProps { + /** The space to represent with an avatar. */ + space: Partial; + + /** The size of the avatar. */ + size?: 's' | 'm' | 'l' | 'xl'; + + /** Optional CSS class(es) to apply. */ + className?: string; + + /** + * When enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers. + * + * Default value is true. + */ + announceSpaceName?: boolean; + + /** + * Whether or not to render the avatar in a disabled state. + * + * Default value is false. + */ + isDisabled?: boolean; +} diff --git a/x-pack/plugins/spaces/public/space_list/index.ts b/x-pack/plugins/spaces/public/space_list/index.ts index 1570ad123b9ab..9d367f3739c70 100644 --- a/x-pack/plugins/spaces/public/space_list/index.ts +++ b/x-pack/plugins/spaces/public/space_list/index.ts @@ -6,3 +6,4 @@ */ export { getSpaceListComponent } from './space_list'; +export type { SpaceListProps } from './types'; diff --git a/x-pack/plugins/spaces/public/space_list/space_list.tsx b/x-pack/plugins/spaces/public/space_list/space_list.tsx index efd8b367bcd45..86e6432d1e210 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; +import type { SpaceListProps } from './types'; export const getSpaceListComponent = async (): Promise> => { const { SpaceListInternal } = await import('./space_list_internal'); diff --git a/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx b/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx index 8109444fc1271..39ae339fb7e18 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx @@ -11,12 +11,12 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { coreMock } from 'src/core/public/mocks'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; +import type { Space } from '../../common'; import { getSpacesContextProviderWrapper } from '../spaces_context'; import { spacesManagerMock } from '../spaces_manager/mocks'; import { SpaceListInternal } from './space_list_internal'; +import type { SpaceListProps } from './types'; const ACTIVE_SPACE: Space = { id: 'default', diff --git a/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx b/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx index ac7e6446f2ccd..bfe4486fafa76 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx @@ -18,12 +18,12 @@ import React, { lazy, Suspense, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; import { getSpaceAvatarComponent } from '../space_avatar'; import { useSpaces } from '../spaces_context'; -import type { ShareToSpacesData, ShareToSpaceTarget } from '../types'; +import type { SpacesData, SpacesDataEntry } from '../types'; +import type { SpaceListProps } from './types'; // No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. const LazySpaceAvatar = lazy(() => @@ -31,6 +31,7 @@ const LazySpaceAvatar = lazy(() => ); const DEFAULT_DISPLAY_LIMIT = 5; +type SpaceTarget = Omit; /** * Displays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for any @@ -43,16 +44,16 @@ export const SpaceListInternal = ({ displayLimit = DEFAULT_DISPLAY_LIMIT, behaviorContext, }: SpaceListProps) => { - const { shareToSpacesDataPromise } = useSpaces(); + const { spacesDataPromise } = useSpaces(); const [isExpanded, setIsExpanded] = useState(false); - const [shareToSpacesData, setShareToSpacesData] = useState(); + const [shareToSpacesData, setShareToSpacesData] = useState(); useEffect(() => { - shareToSpacesDataPromise.then((x) => { + spacesDataPromise.then((x) => { setShareToSpacesData(x); }); - }, [shareToSpacesDataPromise]); + }, [spacesDataPromise]); if (!shareToSpacesData) { return null; @@ -61,7 +62,7 @@ export const SpaceListInternal = ({ const isSharedToAllSpaces = namespaces.includes(ALL_SPACES_ID); const unauthorizedSpacesCount = namespaces.filter((namespace) => namespace === UNKNOWN_SPACE) .length; - let displayedSpaces: ShareToSpaceTarget[]; + let displayedSpaces: SpaceTarget[]; let button: ReactNode = null; if (isSharedToAllSpaces) { @@ -77,8 +78,8 @@ export const SpaceListInternal = ({ ]; } else { const authorized = namespaces.filter((namespace) => namespace !== UNKNOWN_SPACE); - const enabledSpaceTargets: ShareToSpaceTarget[] = []; - const disabledSpaceTargets: ShareToSpaceTarget[] = []; + const enabledSpaceTargets: SpaceTarget[] = []; + const disabledSpaceTargets: SpaceTarget[] = []; authorized.forEach((namespace) => { const spaceTarget = shareToSpacesData.spacesMap.get(namespace); if (spaceTarget === undefined) { diff --git a/x-pack/plugins/spaces/public/space_list/types.ts b/x-pack/plugins/spaces/public/space_list/types.ts new file mode 100644 index 0000000000000..2e7e813a48a2f --- /dev/null +++ b/x-pack/plugins/spaces/public/space_list/types.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Properties for the SpaceList component. + */ +export interface SpaceListProps { + /** + * The namespaces of a saved object to render into a corresponding list of spaces. + */ + namespaces: string[]; + /** + * Optional limit to the number of spaces that can be displayed in the list. If the number of spaces exceeds this limit, they will be + * hidden behind a "show more" button. Set to 0 to disable. + * + * Default value is 5. + */ + displayLimit?: number; + /** + * When set to 'within-space' (default), the space list behaves like it is running on a page within the active space, and it will omit the + * active space (e.g., it displays a list of all the _other_ spaces that an object is shared to). + * + * Conversely, when set to 'outside-space', the space list behaves like it is running on a page outside of any space, so it will not omit + * the active space. + */ + behaviorContext?: 'within-space' | 'outside-space'; +} diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx index 0628f79990af6..214659169e72d 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx @@ -10,8 +10,7 @@ import './space_card.scss'; import { EuiCard, EuiLoadingSpinner } from '@elastic/eui'; import React, { lazy, Suspense } from 'react'; -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../../common'; import { addSpaceIdToPath, ENTER_SPACE_PATH } from '../../../common'; import { getSpaceAvatarComponent } from '../../space_avatar'; diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx index e7bef5f646036..c13a3a53fbe8f 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx @@ -10,8 +10,7 @@ import './space_cards.scss'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { Component } from 'react'; -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../../common'; import { SpaceCard } from './space_card'; interface Props { diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx index b4417d98bcace..e17c3e0078d42 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { shallowWithIntl } from '@kbn/test/jest'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../common'; import { spacesManagerMock } from '../spaces_manager/mocks'; import { SpaceSelector } from './space_selector'; diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx index cee304408495d..00ad39bf0027f 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -28,8 +28,8 @@ import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import type { CoreStart } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../common'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; import type { SpacesManager } from '../spaces_manager'; import { SpaceCards } from './components'; diff --git a/x-pack/plugins/spaces/public/spaces_context/context.tsx b/x-pack/plugins/spaces/public/spaces_context/context.tsx index e38a2f17151a9..64df17bed5768 100644 --- a/x-pack/plugins/spaces/public/spaces_context/context.tsx +++ b/x-pack/plugins/spaces/public/spaces_context/context.tsx @@ -7,29 +7,29 @@ import * as React from 'react'; +import type { CoreStart } from 'src/core/public'; + import type { SpacesManager } from '../spaces_manager'; -import type { ShareToSpacesData } from '../types'; -import type { KibanaServices, SpacesReactContext, SpacesReactContextValue } from './types'; +import type { SpacesData } from '../types'; +import type { SpacesReactContext, SpacesReactContextValue } from './types'; const { useContext, createElement, createContext } = React; -const context = createContext>>({}); +const context = createContext>>>({}); -export const useSpaces = (): SpacesReactContextValue< - KibanaServices & Extra -> => - useContext( - (context as unknown) as React.Context> - ); +export const useSpaces = < + Services extends Partial +>(): SpacesReactContextValue => + useContext((context as unknown) as React.Context>); -export const createSpacesReactContext = ( +export const createSpacesReactContext = >( services: Services, spacesManager: SpacesManager, - shareToSpacesDataPromise: Promise + spacesDataPromise: Promise ): SpacesReactContext => { const value: SpacesReactContextValue = { spacesManager, - shareToSpacesDataPromise, + spacesDataPromise, services, }; const Provider: React.FC = ({ children }) => diff --git a/x-pack/plugins/spaces/public/spaces_context/index.ts b/x-pack/plugins/spaces/public/spaces_context/index.ts index 0187131b02b93..5b5ff829b3800 100644 --- a/x-pack/plugins/spaces/public/spaces_context/index.ts +++ b/x-pack/plugins/spaces/public/spaces_context/index.ts @@ -6,4 +6,5 @@ */ export { useSpaces } from './context'; +export type { SpacesContextProps, SpacesReactContextValue } from './types'; export { getSpacesContextProviderWrapper } from './wrapper'; diff --git a/x-pack/plugins/spaces/public/spaces_context/types.ts b/x-pack/plugins/spaces/public/spaces_context/types.ts index e73da7cb26b68..d3a6859875b9c 100644 --- a/x-pack/plugins/spaces/public/spaces_context/types.ts +++ b/x-pack/plugins/spaces/public/spaces_context/types.ts @@ -11,23 +11,31 @@ import type { CoreStart, StartServicesAccessor } from 'src/core/public'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; -import type { ShareToSpacesData } from '../types'; +import type { SpacesData } from '../types'; -export type KibanaServices = Partial; - -export interface SpacesReactContextValue { +export interface SpacesReactContextValue> { readonly spacesManager: SpacesManager; - readonly shareToSpacesDataPromise: Promise; + readonly spacesDataPromise: Promise; readonly services: Services; } -export interface SpacesReactContext { - value: SpacesReactContextValue; +export interface SpacesReactContext> { + value: SpacesReactContextValue; Provider: React.FC; - Consumer: React.Consumer>; + Consumer: React.Consumer>; } export interface InternalProps { spacesManager: SpacesManager; getStartServices: StartServicesAccessor; } + +/** + * Properties for the SpacesContext. + */ +export interface SpacesContextProps { + /** + * If a feature is specified, all Spaces components will treat it appropriately if the feature is disabled in a given Space. + */ + feature?: string; +} diff --git a/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx b/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx index 6de14290abb74..8fae6e78d1bb2 100644 --- a/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx +++ b/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx @@ -8,9 +8,7 @@ import type { PropsWithChildren } from 'react'; import React from 'react'; -import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; - -import type { InternalProps } from './types'; +import type { InternalProps, SpacesContextProps } from './types'; export const getSpacesContextProviderWrapper = async ( internalProps: InternalProps diff --git a/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx b/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx index dd6408e9550ee..83ad69b1017d5 100644 --- a/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx +++ b/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx @@ -9,12 +9,12 @@ import type { PropsWithChildren } from 'react'; import React, { useEffect, useMemo, useState } from 'react'; import type { ApplicationStart, DocLinksStart, NotificationsStart } from 'src/core/public'; -import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; +import type { GetAllSpacesPurpose } from '../../common'; import type { SpacesManager } from '../spaces_manager'; -import type { ShareToSpacesData, ShareToSpaceTarget } from '../types'; +import type { SpacesData, SpacesDataEntry } from '../types'; import { createSpacesReactContext } from './context'; -import type { InternalProps, SpacesReactContext } from './types'; +import type { InternalProps, SpacesContextProps, SpacesReactContext } from './types'; interface Services { application: ApplicationStart; @@ -22,25 +22,25 @@ interface Services { notifications: NotificationsStart; } -async function getShareToSpacesData( - spacesManager: SpacesManager, - feature?: string -): Promise { +async function getSpacesData(spacesManager: SpacesManager, feature?: string): Promise { const spaces = await spacesManager.getSpaces({ includeAuthorizedPurposes: true }); const activeSpace = await spacesManager.getActiveSpace(); const spacesMap = spaces - .map(({ authorizedPurposes, disabledFeatures, ...space }) => { + .map(({ authorizedPurposes, disabledFeatures, ...space }) => { const isActiveSpace = space.id === activeSpace.id; - const cannotShareToSpace = authorizedPurposes?.shareSavedObjectsIntoSpace === false; const isFeatureDisabled = feature !== undefined && disabledFeatures.includes(feature); return { ...space, ...(isActiveSpace && { isActiveSpace }), - ...(cannotShareToSpace && { cannotShareToSpace }), ...(isFeatureDisabled && { isFeatureDisabled }), + isAuthorizedForPurpose: (purpose: GetAllSpacesPurpose) => + // If authorizedPurposes is not present, then Security is disabled; normally in a situation like this we would "fail-secure", but + // in this case we are dealing with an abstraction over the client-side UI capabilities. There is no chance for privilege + // escalation here, and the correct behavior is that if Security is disabled, the user is implicitly authorized to do everything. + authorizedPurposes ? authorizedPurposes[purpose] === true : true, }; }) - .reduce((acc, cur) => acc.set(cur.id, cur), new Map()); + .reduce((acc, cur) => acc.set(cur.id, cur), new Map()); return { spacesMap, @@ -54,7 +54,7 @@ export const SpacesContextWrapperInternal = ( const { spacesManager, getStartServices, feature, children } = props; const [context, setContext] = useState | undefined>(); - const shareToSpacesDataPromise = useMemo(() => getShareToSpacesData(spacesManager, feature), [ + const spacesDataPromise = useMemo(() => getSpacesData(spacesManager, feature), [ spacesManager, feature, ]); @@ -63,9 +63,9 @@ export const SpacesContextWrapperInternal = ( getStartServices().then(([coreStart]) => { const { application, docLinks, notifications } = coreStart; const services = { application, docLinks, notifications }; - setContext(createSpacesReactContext(services, spacesManager, shareToSpacesDataPromise)); + setContext(createSpacesReactContext(services, spacesManager, spacesDataPromise)); }); - }, [getStartServices, shareToSpacesDataPromise, spacesManager]); + }, [getStartServices, spacesDataPromise, spacesManager]); if (!context) { return null; diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts index 5282163f93b15..3f3cc3f4d7801 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts @@ -8,8 +8,7 @@ import type { Observable } from 'rxjs'; import { of } from 'rxjs'; -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../common'; import type { SpacesManager } from './spaces_manager'; function createSpacesManagerMock() { diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index 845373bf22299..d0406d744b72a 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -13,9 +13,13 @@ import type { HttpSetup, SavedObjectsCollectMultiNamespaceReferencesResponse, } from 'src/core/public'; -import type { Space } from 'src/plugins/spaces_oss/common'; -import type { GetAllSpacesOptions, GetSpaceResult, LegacyUrlAliasTarget } from '../../common'; +import type { + GetAllSpacesOptions, + GetSpaceResult, + LegacyUrlAliasTarget, + Space, +} from '../../common'; import type { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; interface SavedObjectTarget { diff --git a/x-pack/plugins/spaces/public/types.ts b/x-pack/plugins/spaces/public/types.ts index e999e332b9884..fd926621b72da 100644 --- a/x-pack/plugins/spaces/public/types.ts +++ b/x-pack/plugins/spaces/public/types.ts @@ -5,14 +5,17 @@ * 2.0. */ -import type { GetSpaceResult } from '../common'; +import type { Observable } from 'rxjs'; + +import type { GetAllSpacesPurpose, GetSpaceResult, Space } from '../common'; +import type { SpacesApiUi } from './ui_api'; /** * The structure for all of the space data that must be loaded for share-to-space components to function. */ -export interface ShareToSpacesData { - /** A map of each existing space's ID and its associated {@link ShareToSpaceTarget}. */ - readonly spacesMap: Map; +export interface SpacesData { + /** A map of each existing space's ID and its associated {@link SpacesDataEntry}. */ + readonly spacesMap: Map; /** The ID of the active space. */ readonly activeSpaceId: string; } @@ -21,11 +24,33 @@ export interface ShareToSpacesData { * The data that was fetched for a specific space. Includes optional additional fields that are needed to handle edge cases in the * share-to-space components that consume it. */ -export interface ShareToSpaceTarget extends Omit { +export interface SpacesDataEntry + extends Omit { /** True if this space is the active space. */ isActiveSpace?: true; - /** True if the user has read access to this space, but is not authorized to share objects into this space. */ - cannotShareToSpace?: true; /** True if the current feature (specified in the `SpacesContext`) is disabled in this space. */ isFeatureDisabled?: true; + /** Returns true if the user is authorized for the given purpose. */ + isAuthorizedForPurpose(purpose: GetAllSpacesPurpose): boolean; +} + +/** + * Client-side Spaces API. + */ +export interface SpacesApi { + /** + * Observable representing the currently active space. + * The details of the space can change without a full page reload (such as display name, color, etc.) + */ + getActiveSpace$(): Observable; + + /** + * Retrieve the currently active space. + */ + getActiveSpace(): Promise; + + /** + * UI components and services to add spaces capabilities to an application. + */ + ui: SpacesApiUi; } diff --git a/x-pack/plugins/spaces/public/ui_api/components.tsx b/x-pack/plugins/spaces/public/ui_api/components.tsx index a277e3a1dd119..a33480712ffae 100644 --- a/x-pack/plugins/spaces/public/ui_api/components.tsx +++ b/x-pack/plugins/spaces/public/ui_api/components.tsx @@ -9,8 +9,8 @@ import type { FC, PropsWithChildren, PropsWithRef } from 'react'; import React from 'react'; import type { StartServicesAccessor } from 'src/core/public'; -import type { SpacesApiUiComponent } from 'src/plugins/spaces_oss/public'; +import { getCopyToSpaceFlyoutComponent } from '../copy_saved_objects_to_space'; import type { PluginsStart } from '../plugin'; import { getLegacyUrlConflict, @@ -21,6 +21,7 @@ import { getSpaceListComponent } from '../space_list'; import { getSpacesContextProviderWrapper } from '../spaces_context'; import type { SpacesManager } from '../spaces_manager'; import { LazyWrapper } from './lazy_wrapper'; +import type { SpacesApiUiComponent } from './types'; export interface GetComponentsOptions { spacesManager: SpacesManager; @@ -51,6 +52,7 @@ export const getComponents = ({ getSpacesContextProviderWrapper({ spacesManager, getStartServices }) ), getShareToSpaceFlyout: wrapLazy(getShareToSpaceFlyoutComponent, { showLoadingSpinner: false }), + getCopyToSpaceFlyout: wrapLazy(getCopyToSpaceFlyoutComponent, { showLoadingSpinner: false }), getSpaceList: wrapLazy(getSpaceListComponent), getLegacyUrlConflict: wrapLazy(() => getLegacyUrlConflict({ getStartServices })), getSpaceAvatar: wrapLazy(getSpaceAvatarComponent), diff --git a/x-pack/plugins/spaces/public/ui_api/index.ts b/x-pack/plugins/spaces/public/ui_api/index.ts index 4bfb482b41407..e0749b04de139 100644 --- a/x-pack/plugins/spaces/public/ui_api/index.ts +++ b/x-pack/plugins/spaces/public/ui_api/index.ts @@ -6,23 +6,27 @@ */ import type { StartServicesAccessor } from 'src/core/public'; -import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import type { PluginsStart } from '../plugin'; import { createRedirectLegacyUrl } from '../share_saved_objects_to_space'; +import { useSpaces } from '../spaces_context'; import type { SpacesManager } from '../spaces_manager'; import { getComponents } from './components'; +import type { LazyComponentFn, SpacesApiUi, SpacesApiUiComponent } from './types'; interface GetUiApiOptions { spacesManager: SpacesManager; getStartServices: StartServicesAccessor; } +export type { LazyComponentFn, SpacesApiUi, SpacesApiUiComponent }; + export const getUiApi = ({ spacesManager, getStartServices }: GetUiApiOptions): SpacesApiUi => { const components = getComponents({ spacesManager, getStartServices }); return { components, redirectLegacyUrl: createRedirectLegacyUrl(getStartServices), + useSpaces, }; }; diff --git a/x-pack/plugins/spaces/public/ui_api/mocks.ts b/x-pack/plugins/spaces/public/ui_api/mocks.ts deleted file mode 100644 index fe3826a58fccc..0000000000000 --- a/x-pack/plugins/spaces/public/ui_api/mocks.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SpacesApiUi, SpacesApiUiComponent } from 'src/plugins/spaces_oss/public'; - -function createComponentsMock(): jest.Mocked { - return { - getSpacesContextProvider: jest.fn(), - getShareToSpaceFlyout: jest.fn(), - getSpaceList: jest.fn(), - getLegacyUrlConflict: jest.fn(), - getSpaceAvatar: jest.fn(), - }; -} - -function createUiApiMock(): jest.Mocked { - return { - components: createComponentsMock(), - redirectLegacyUrl: jest.fn(), - }; -} - -export const uiApiMock = { - create: createUiApiMock, -}; diff --git a/x-pack/plugins/spaces/public/ui_api/types.ts b/x-pack/plugins/spaces/public/ui_api/types.ts new file mode 100644 index 0000000000000..5048e5a9b9652 --- /dev/null +++ b/x-pack/plugins/spaces/public/ui_api/types.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ReactElement } from 'react'; + +import type { CoreStart } from 'src/core/public'; + +import type { CopyToSpaceFlyoutProps } from '../copy_saved_objects_to_space'; +import type { + LegacyUrlConflictProps, + ShareToSpaceFlyoutProps, +} from '../share_saved_objects_to_space'; +import type { SpaceAvatarProps } from '../space_avatar'; +import type { SpaceListProps } from '../space_list'; +import type { SpacesContextProps, SpacesReactContextValue } from '../spaces_context'; + +/** + * Function that returns a promise for a lazy-loadable component. + */ +export type LazyComponentFn = (props: T) => ReactElement; + +/** + * UI components and services to add spaces capabilities to an application. + */ +export interface SpacesApiUi { + /** + * Lazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature. + */ + components: SpacesApiUiComponent; + /** + * Redirect the user from a legacy URL to a new URL. This needs to be used if a call to `SavedObjectsClient.resolve()` results in an + * `"aliasMatch"` outcome, which indicates that the user has loaded the page using a legacy URL. Calling this function will trigger a + * client-side redirect to the new URL, and it will display a toast to the user. + * + * Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call + * `SavedObjectsClient.resolve()` (old ID) and the object ID in the result (new ID). For example... + * + * The old object ID is `workpad-123` and the new object ID is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`. + * + * Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1` + * + * New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1` + * + * The protocol, hostname, port, base path, and app path are automatically included. + * + * @param path The path to use for the new URL, optionally including `search` and/or `hash` URL components. + * @param objectNoun The string that is used to describe the object in the toast, e.g., _The **object** you're looking for has a new + * location_. Default value is 'object'. + */ + redirectLegacyUrl: (path: string, objectNoun?: string) => Promise; + /** + * Helper function to easily access the Spaces React Context provider. + */ + useSpaces>(): SpacesReactContextValue; +} + +/** + * React UI components to be used to display the Spaces feature in any application. + */ +export interface SpacesApiUiComponent { + /** + * Provides a context that is required to render some Spaces components. + */ + getSpacesContextProvider: LazyComponentFn; + /** + * Displays a flyout to edit the spaces that an object is shared to. + * + * Note: must be rendered inside of a SpacesContext. + */ + getShareToSpaceFlyout: LazyComponentFn; + /** + * Displays a flyout to copy an object to other spaces. + * + * Note: must be rendered inside of a SpacesContext. + */ + getCopyToSpaceFlyout: LazyComponentFn; + /** + * Displays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for + * any number of spaces that the user is not authorized to see) by default. If more than five named spaces would be displayed, the extras + * (along with the unauthorized spaces indicator, if present) are hidden behind a button. If '*' (aka "All spaces") is present, it + * supersedes all of the above and just displays a single badge without a button. + * + * Note: must be rendered inside of a SpacesContext. + */ + getSpaceList: LazyComponentFn; + /** + * Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which + * indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a + * different object (B). + * + * In this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining + * that there is a conflict, and it includes a button that will redirect the user to object B when clicked. + * + * Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call + * `SavedObjectsClient.resolve()` (A) and the `alias_target_id` value in the response (B). For example... + * + * A is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`. + * + * Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1` + * + * New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1` + */ + getLegacyUrlConflict: LazyComponentFn; + /** + * Displays an avatar for the given space. + */ + getSpaceAvatar: LazyComponentFn; +} diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts index c3393da770ded..13be8398da480 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts @@ -7,10 +7,10 @@ import type { Capabilities, CoreSetup } from 'src/core/server'; import { coreMock, httpServerMock, loggingSystemMock } from 'src/core/server/mocks'; -import type { Space } from 'src/plugins/spaces_oss/common'; import type { KibanaFeature } from '../../../features/server'; import { featuresPluginMock } from '../../../features/server/mocks'; +import type { Space } from '../../common'; import type { PluginsStart } from '../plugin'; import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; import { setupCapabilitiesSwitcher } from './capabilities_switcher'; diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts index 1f6249d9b3220..ac2ff735de797 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts @@ -8,9 +8,9 @@ import _ from 'lodash'; import type { Capabilities, CapabilitiesSwitcher, CoreSetup, Logger } from 'src/core/server'; -import type { Space } from 'src/plugins/spaces_oss/common'; import type { KibanaFeature } from '../../../features/server'; +import type { Space } from '../../common'; import type { PluginsStart } from '../plugin'; import type { SpacesServiceStart } from '../spaces_service'; diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index 4765b06f5a02a..31714df958d49 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -23,16 +23,14 @@ export { SpacesPluginSetup, SpacesPluginStart } from './plugin'; export { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; export { ISpacesClient, SpacesClientRepositoryFactory, SpacesClientWrapper } from './spaces_client'; -export { +export type { + Space, GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, LegacyUrlAliasTarget, } from '../common'; -// re-export types from oss definition -export type { Space } from 'src/plugins/spaces_oss/common'; - export const config: PluginConfigDescriptor = { schema: ConfigSchema, deprecations: spacesConfigDeprecationProvider, diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts index ff81960185b62..f5b41786d2478 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts @@ -6,8 +6,8 @@ */ import type { CoreSetup, Logger } from 'src/core/server'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; import { addSpaceIdToPath } from '../../../common'; import { DEFAULT_SPACE_ID, ENTER_SPACE_PATH } from '../../../common/constants'; import type { PluginsSetup } from '../../plugin'; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.ts index 7f0bd3231c6dd..09a4ac5a843a6 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.ts @@ -6,8 +6,8 @@ */ import { schema } from '@kbn/config-schema'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../../common'; import { wrapError } from '../../../lib/errors'; import { createLicensedRouteHandler } from '../../lib'; import type { ExternalRouteDeps } from './'; diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.ts b/x-pack/plugins/spaces/server/routes/api/external/put.ts index b58601c8c7e77..196f7c11932eb 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.ts @@ -6,9 +6,9 @@ */ import { schema } from '@kbn/config-schema'; -import type { Space } from 'src/plugins/spaces_oss/common'; import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server'; +import type { Space } from '../../../../common'; import { wrapError } from '../../../lib/errors'; import { spaceSchema } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; diff --git a/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts b/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts index 6a90d367fc4a7..53969d3984bda 100644 --- a/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts +++ b/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../../common'; export function convertSavedObjectToSpace(savedObject: any): Space { return { diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts index 62a3d98662939..e07050fe97d73 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts @@ -5,8 +5,7 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../../common'; import { migrateTo660 } from './space_migrations'; describe('migrateTo660', () => { diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts index d88db2b0181dd..c26983e84de57 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts @@ -6,7 +6,8 @@ */ import type { SavedObjectUnsanitizedDoc } from 'src/core/server'; -import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { Space } from '../../../common'; export const migrateTo660 = (doc: SavedObjectUnsanitizedDoc) => { if (!doc.attributes.hasOwnProperty('disabledFeatures')) { diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts index 56bfe71b581ed..b94113436d7ad 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts @@ -533,27 +533,94 @@ const ERROR_NAMESPACE_SPECIFIED = 'Spaces currently determines the namespaces'; }); describe('#openPointInTimeForType', () => { - test(`throws error if options.namespace is specified`, async () => { - const { client } = createSpacesSavedObjectsClient(); + test(`throws error if if user is unauthorized in this space`, async () => { + const { client, baseClient, spacesService } = createSpacesSavedObjectsClient(); + const spacesClient = spacesClientMock.create(); + spacesClient.getAll.mockResolvedValue([]); + spacesService.createSpacesClient.mockReturnValue(spacesClient); - await expect(client.openPointInTimeForType('foo', { namespace: 'bar' })).rejects.toThrow( - ERROR_NAMESPACE_SPECIFIED + await expect( + client.openPointInTimeForType('foo', { namespaces: ['bar'] }) + ).rejects.toThrowError('Bad Request'); + + expect(baseClient.openPointInTimeForType).not.toHaveBeenCalled(); + }); + + test(`throws error if if user is unauthorized in any space`, async () => { + const { client, baseClient, spacesService } = createSpacesSavedObjectsClient(); + const spacesClient = spacesClientMock.create(); + spacesClient.getAll.mockRejectedValue(Boom.unauthorized()); + spacesService.createSpacesClient.mockReturnValue(spacesClient); + + await expect( + client.openPointInTimeForType('foo', { namespaces: ['bar'] }) + ).rejects.toThrowError('Bad Request'); + + expect(baseClient.openPointInTimeForType).not.toHaveBeenCalled(); + }); + + test(`filters options.namespaces based on authorization`, async () => { + const { client, baseClient, spacesService } = createSpacesSavedObjectsClient(); + const expectedReturnValue = { id: 'abc123' }; + baseClient.openPointInTimeForType.mockReturnValue(Promise.resolve(expectedReturnValue)); + + const spacesClient = spacesService.createSpacesClient( + null as any + ) as jest.Mocked; + spacesClient.getAll.mockImplementation(() => + Promise.resolve([ + { id: 'ns-1', name: '', disabledFeatures: [] }, + { id: 'ns-2', name: '', disabledFeatures: [] }, + ]) ); + + const options = Object.freeze({ namespaces: ['ns-1', 'ns-3'] }); + const actualReturnValue = await client.openPointInTimeForType(['foo', 'bar'], options); + + expect(actualReturnValue).toBe(expectedReturnValue); + expect(baseClient.openPointInTimeForType).toHaveBeenCalledWith(['foo', 'bar'], { + namespaces: ['ns-1'], + }); + expect(spacesClient.getAll).toHaveBeenCalledWith({ purpose: 'findSavedObjects' }); }); - test(`supplements options with the current namespace`, async () => { + test(`translates options.namespaces: ['*']`, async () => { + const { client, baseClient, spacesService } = createSpacesSavedObjectsClient(); + const expectedReturnValue = { id: 'abc123' }; + baseClient.openPointInTimeForType.mockReturnValue(Promise.resolve(expectedReturnValue)); + + const spacesClient = spacesService.createSpacesClient( + null as any + ) as jest.Mocked; + spacesClient.getAll.mockImplementation(() => + Promise.resolve([ + { id: 'ns-1', name: '', disabledFeatures: [] }, + { id: 'ns-2', name: '', disabledFeatures: [] }, + ]) + ); + + const options = Object.freeze({ namespaces: ['*'] }); + const actualReturnValue = await client.openPointInTimeForType(['foo', 'bar'], options); + + expect(actualReturnValue).toBe(expectedReturnValue); + expect(baseClient.openPointInTimeForType).toHaveBeenCalledWith(['foo', 'bar'], { + namespaces: ['ns-1', 'ns-2'], + }); + expect(spacesClient.getAll).toHaveBeenCalledWith({ purpose: 'findSavedObjects' }); + }); + + test(`supplements options with the current namespace if unspecified`, async () => { const { client, baseClient } = createSpacesSavedObjectsClient(); const expectedReturnValue = { id: 'abc123' }; baseClient.openPointInTimeForType.mockReturnValue(Promise.resolve(expectedReturnValue)); - const options = Object.freeze({ foo: 'bar' }); - // @ts-expect-error + const options = Object.freeze({ keepAlive: '2m' }); const actualReturnValue = await client.openPointInTimeForType('foo', options); expect(actualReturnValue).toBe(expectedReturnValue); expect(baseClient.openPointInTimeForType).toHaveBeenCalledWith('foo', { - foo: 'bar', - namespace: currentSpace.expectedNamespace, + keepAlive: '2m', + namespaces: [currentSpace.expectedNamespace ?? DEFAULT_SPACE_ID], }); }); }); diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts index e344aa8cecf07..9c51f22e280d8 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts @@ -30,7 +30,7 @@ import type { SavedObjectsUpdateOptions, } from 'src/core/server'; -import { SavedObjectsUtils } from '../../../../../src/core/server'; +import { SavedObjectsErrorHelpers, SavedObjectsUtils } from '../../../../../src/core/server'; import { ALL_SPACES_ID } from '../../common/constants'; import { spaceIdToNamespace } from '../lib/utils/namespace'; import type { ISpacesClient } from '../spaces_client'; @@ -175,32 +175,19 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ public async find(options: SavedObjectsFindOptions) { - throwErrorIfNamespaceSpecified(options); - - let namespaces = options.namespaces; - if (namespaces) { - try { - const availableSpaces = await this.spacesClient.getAll({ purpose: 'findSavedObjects' }); - if (namespaces.includes(ALL_SPACES_ID)) { - namespaces = availableSpaces.map((space) => space.id); - } else { - namespaces = namespaces.filter((namespace) => - availableSpaces.some((space) => space.id === namespace) - ); - } - if (namespaces.length === 0) { - // return empty response, since the user is unauthorized in this space (or these spaces), but we don't return forbidden errors for `find` operations - return SavedObjectsUtils.createEmptyFindResponse(options); - } - } catch (err) { - if (Boom.isBoom(err) && err.output.payload.statusCode === 403) { - // return empty response, since the user is unauthorized in any space, but we don't return forbidden errors for `find` operations - return SavedObjectsUtils.createEmptyFindResponse(options); - } - throw err; + let namespaces: string[]; + try { + namespaces = await this.getSearchableSpaces(options.namespaces); + } catch (err) { + if (Boom.isBoom(err) && err.output.payload.statusCode === 403) { + // return empty response, since the user is unauthorized in any space, but we don't return forbidden errors for `find` operations + return SavedObjectsUtils.createEmptyFindResponse(options); } - } else { - namespaces = [this.spaceId]; + throw err; + } + if (namespaces.length === 0) { + // return empty response, since the user is unauthorized in this space (or these spaces), but we don't return forbidden errors for `find` operations + return SavedObjectsUtils.createEmptyFindResponse(options); } return await this.client.find({ @@ -396,10 +383,15 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { type: string | string[], options: SavedObjectsOpenPointInTimeOptions = {} ) { - throwErrorIfNamespaceSpecified(options); + const namespaces = await this.getSearchableSpaces(options.namespaces); + if (namespaces.length === 0) { + // throw bad request if no valid spaces were found. + throw SavedObjectsErrorHelpers.createBadRequestError(); + } + return await this.client.openPointInTimeForType(type, { ...options, - namespace: spaceIdToNamespace(this.spaceId), + namespaces, }); } @@ -446,4 +438,19 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { ...dependencies, }); } + + private async getSearchableSpaces(namespaces?: string[]): Promise { + if (namespaces) { + const availableSpaces = await this.spacesClient.getAll({ purpose: 'findSavedObjects' }); + if (namespaces.includes(ALL_SPACES_ID)) { + return availableSpaces.map((space) => space.id); + } else { + return namespaces.filter((namespace) => + availableSpaces.some((space) => space.id === namespace) + ); + } + } else { + return [this.spaceId]; + } + } } diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts index d893d2b089f89..a4e209ff11d32 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts @@ -5,8 +5,7 @@ * 2.0. */ -import type { Space } from 'src/plugins/spaces_oss/common'; - +import type { Space } from '../../common'; import { DEFAULT_SPACE_ID } from '../../common/constants'; import type { SpacesClient } from './spaces_client'; diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index 824d6e28b9923..0a91c7aff1a08 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -9,13 +9,13 @@ import Boom from '@hapi/boom'; import { omit } from 'lodash'; import type { ISavedObjectsRepository, SavedObject } from 'src/core/server'; -import type { Space } from 'src/plugins/spaces_oss/common'; import type { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, LegacyUrlAliasTarget, + Space, } from '../../common'; import { isReservedSpace } from '../../common'; import type { ConfigType } from '../config'; diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts index e951ed38072d7..2ffe77a4c9a89 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts @@ -6,8 +6,8 @@ */ import type { IBasePath, KibanaRequest } from 'src/core/server'; -import type { Space } from 'src/plugins/spaces_oss/common'; +import type { Space } from '../../common'; import { getSpaceIdFromPath } from '../../common'; import { DEFAULT_SPACE_ID } from '../../common/constants'; import { namespaceToSpaceId, spaceIdToNamespace } from '../lib/utils/namespace'; diff --git a/x-pack/plugins/spaces/tsconfig.json b/x-pack/plugins/spaces/tsconfig.json index 4cc95504a158e..bf2c6e7fc8694 100644 --- a/x-pack/plugins/spaces/tsconfig.json +++ b/x-pack/plugins/spaces/tsconfig.json @@ -15,8 +15,6 @@ { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/management/tsconfig.json" }, - { "path": "../../../src/plugins/saved_objects_management/tsconfig.json" }, - { "path": "../../../src/plugins/spaces_oss/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" } ] } 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 bbe0ad8014ae7..54a5f22839bfd 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -4044,6 +4044,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4054,6 +4057,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4064,6 +4070,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4075,6 +4084,9 @@ "total": { "type": "long" }, + "deprecated": { + "type": "long" + }, "app": { "properties": { "canvas workpad": { @@ -4105,9 +4117,6 @@ }, "status": { "properties": { - "cancelled": { - "type": "long" - }, "completed": { "type": "long" }, @@ -4127,62 +4136,6 @@ }, "statuses": { "properties": { - "cancelled": { - "properties": { - "csv": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "csv_searchsource": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "PNG": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "printable_pdf": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - } - } - }, "completed": { "properties": { "csv": { @@ -4483,6 +4436,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4493,6 +4449,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4503,6 +4462,9 @@ }, "total": { "type": "long" + }, + "deprecated": { + "type": "long" } } }, @@ -4514,6 +4476,9 @@ "total": { "type": "long" }, + "deprecated": { + "type": "long" + }, "app": { "properties": { "canvas workpad": { @@ -4544,9 +4509,6 @@ }, "status": { "properties": { - "cancelled": { - "type": "long" - }, "completed": { "type": "long" }, @@ -4566,62 +4528,6 @@ }, "statuses": { "properties": { - "cancelled": { - "properties": { - "csv": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "csv_searchsource": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "PNG": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - }, - "printable_pdf": { - "properties": { - "canvas workpad": { - "type": "long" - }, - "dashboard": { - "type": "long" - }, - "visualization": { - "type": "long" - } - } - } - } - }, "completed": { "properties": { "csv": { @@ -5921,16 +5827,10 @@ }, "ui_open": { "properties": { - "cluster": { - "type": "long", - "_meta": { - "description": "Number of times a user viewed the list of Elasticsearch cluster deprecations." - } - }, - "indices": { + "elasticsearch": { "type": "long", "_meta": { - "description": "Number of times a user viewed the list of Elasticsearch index deprecations." + "description": "Number of times a user viewed the list of Elasticsearch deprecations." } }, "overview": { diff --git a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts index 269fc6598beaa..c173e347fbdb5 100644 --- a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts +++ b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { AlertConsumers } from '@kbn/rule-data-utils'; import { IEsSearchRequest } from '../../../../../../src/plugins/data/common'; import { ESQuery } from '../../typed_json'; @@ -44,7 +43,6 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest { docValueFields?: DocValueFields[]; factoryQueryType?: TimelineFactoryQueryTypes; entityType?: EntityType; - alertConsumers?: AlertConsumers[]; } export interface TimelineRequestSortField extends SortField { diff --git a/x-pack/plugins/timelines/common/types/timeline/actions/index.ts b/x-pack/plugins/timelines/common/types/timeline/actions/index.ts index 4d426272d8621..bd864b9d97487 100644 --- a/x-pack/plugins/timelines/common/types/timeline/actions/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/actions/index.ts @@ -118,10 +118,12 @@ export interface BulkActionsObjectProp { } export type BulkActionsProp = boolean | BulkActionsObjectProp; +export type AlertWorkflowStatus = 'open' | 'closed' | 'acknowledged'; + /** * @deprecated * TODO: remove when `acknowledged` migrations are finished */ export type InProgressStatus = 'in-progress'; -export type AlertStatus = 'open' | 'closed' | 'acknowledged' | InProgressStatus; +export type AlertStatus = AlertWorkflowStatus | InProgressStatus; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx index 1a3c8267f946c..af19a6b7cdb74 100644 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx +++ b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx @@ -33,8 +33,9 @@ const AddToCaseActionComponent: React.FC = ({ {i18n.ACTION_ADD_EXISTING_CASE} diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx index 7585382b14820..7a8e53bd10f83 100644 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx +++ b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx @@ -34,8 +34,9 @@ const AddToCaseActionComponent: React.FC = ({ {i18n.ACTION_ADD_NEW_CASE} diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx index 8dbc7f34b530e..236ff6b4f8e6f 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx @@ -105,7 +105,7 @@ interface OwnProps { hasAlertsCrud?: boolean; } -const basicUnit = (n: number) => i18n.UNIT(n); +const defaultUnit = (n: number) => i18n.ALERTS_UNIT(n); const NUM_OF_ICON_IN_TIMELINE_ROW = 2; export const hasAdditionalActions = (id: TimelineId): boolean => @@ -273,7 +273,7 @@ export const BodyComponent = React.memo( totalItems, totalPages, trailingControlColumns = EMPTY_CONTROL_COLUMNS, - unit = basicUnit, + unit = defaultUnit, hasAlertsCrud, }) => { const dispatch = useDispatch(); diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/translations.ts b/x-pack/plugins/timelines/public/components/t_grid/body/translations.ts index 4eb47afdc1923..b8989bca65330 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/translations.ts +++ b/x-pack/plugins/timelines/public/components/t_grid/body/translations.ts @@ -217,8 +217,8 @@ export const INVESTIGATE_IN_RESOLVER_DISABLED = i18n.translate( } ); -export const UNIT = (totalCount: number) => - i18n.translate('xpack.timelines.timeline.body.unit', { +export const ALERTS_UNIT = (totalCount: number) => + i18n.translate('xpack.timelines.timeline.alertsUnit', { values: { totalCount }, - defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, + defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`, }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index b84eff4d2142c..b2e6b592c0f45 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -50,7 +50,6 @@ import { useTimelineEvents } from '../../../container'; import { StatefulBody } from '../body'; import { Footer, footerHeight } from '../footer'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexGroup, UpdatedFlexItem } from '../styles'; -import * as i18n from '../translations'; import { Sort } from '../body/sort'; import { InspectButton, InspectButtonContainer } from '../../inspect'; import { SummaryViewSelector, ViewSelection } from '../event_rendered_view/selector'; @@ -138,6 +137,7 @@ export interface TGridIntegratedProps { data?: DataPublicPluginStart; tGridEventRenderedViewEnabled: boolean; hasAlertsCrud: boolean; + unit?: (n: number) => string; } const TGridIntegratedComponent: React.FC = ({ @@ -175,6 +175,7 @@ const TGridIntegratedComponent: React.FC = ({ tGridEventRenderedViewEnabled, data, hasAlertsCrud, + unit, }) => { const dispatch = useDispatch(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; @@ -183,7 +184,6 @@ const TGridIntegratedComponent: React.FC = ({ const [tableView, setTableView] = useState('gridView'); const getManageTimeline = useMemo(() => tGridSelectors.getManageTimelineById(), []); - const unit = useMemo(() => (n: number) => i18n.ALERTS_UNIT(n), []); const { queryFields, title } = useDeepEqualSelector((state) => getManageTimeline(state, id ?? '') ); diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx index 9867cd834802e..4f78b090a2f9a 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { AlertConsumers } from '@kbn/rule-data-utils'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useEffect, useMemo, useState } from 'react'; @@ -40,7 +39,6 @@ import { StatefulBody } from '../body'; import { Footer, footerHeight } from '../footer'; import { LastUpdatedAt } from '../..'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexItem, UpdatedFlexGroup } from '../styles'; -import * as i18n from '../translations'; import { InspectButton, InspectButtonContainer } from '../../inspect'; import { useFetchIndex } from '../../../container/source'; import { AddToCaseAction } from '../../actions/timeline/cases/add_to_case_action'; @@ -81,7 +79,6 @@ const ScrollableFlexItem = styled(EuiFlexItem)` `; export interface TGridStandaloneProps { - alertConsumers: AlertConsumers[]; appId: string; casePermissions: { crud: boolean; @@ -113,13 +110,11 @@ export interface TGridStandaloneProps { trailingControlColumns: ControlColumnProps[]; bulkActions?: BulkActionsProp; data?: DataPublicPluginStart; - unit: (total: number) => React.ReactNode; + unit?: (total: number) => React.ReactNode; } -const basicUnit = (n: number) => i18n.UNIT(n); const TGridStandaloneComponent: React.FC = ({ afterCaseSelection, - alertConsumers, appId, casePermissions, columns, @@ -145,7 +140,7 @@ const TGridStandaloneComponent: React.FC = ({ leadingControlColumns, trailingControlColumns, data, - unit = basicUnit, + unit, }) => { const dispatch = useDispatch(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; @@ -212,7 +207,6 @@ const TGridStandaloneComponent: React.FC = ({ loading, { events, updatedAt, loadPage, pageInfo, refetch, totalCount = 0, inspect }, ] = useTimelineEvents({ - alertConsumers, docValueFields: [], entityType, excludeEcsData: true, diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/bulk_actions/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/bulk_actions/index.tsx index bda2f24bc7dd2..680da584b382e 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/bulk_actions/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/bulk_actions/index.tsx @@ -96,7 +96,7 @@ const BulkActionsComponent: React.FC = ({ - i18n.translate('xpack.timelines.timeline.unit', { - values: { totalCount }, - defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, - }); - -export const ALERTS_UNIT = (totalCount: number) => - i18n.translate('xpack.timelines.timeline.alertsUnit', { - values: { totalCount }, - defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`, - }); - export const BULK_ACTION_OPEN_SELECTED = i18n.translate( 'xpack.timelines.timeline.openSelectedTitle', { @@ -41,7 +29,7 @@ export const BULK_ACTION_OPEN_SELECTED = i18n.translate( export const BULK_ACTION_CLOSE_SELECTED = i18n.translate( 'xpack.timelines.timeline.closeSelectedTitle', { - defaultMessage: 'Close selected', + defaultMessage: 'Mark as closed', } ); diff --git a/x-pack/plugins/timelines/public/hooks/use_status_bulk_action_items.tsx b/x-pack/plugins/timelines/public/hooks/use_status_bulk_action_items.tsx index bab9f641d9724..8fd637767a387 100644 --- a/x-pack/plugins/timelines/public/hooks/use_status_bulk_action_items.tsx +++ b/x-pack/plugins/timelines/public/hooks/use_status_bulk_action_items.tsx @@ -127,7 +127,6 @@ export const useStatusBulkActionItems = ({ key="open" data-test-subj="open-alert-status" onClick={() => onClickUpdate(FILTER_OPEN)} - size="s" > {i18n.BULK_ACTION_OPEN_SELECTED} @@ -139,7 +138,6 @@ export const useStatusBulkActionItems = ({ key="acknowledge" data-test-subj="acknowledged-alert-status" onClick={() => onClickUpdate(FILTER_ACKNOWLEDGED)} - size="s" > {i18n.BULK_ACTION_ACKNOWLEDGED_SELECTED} @@ -151,7 +149,6 @@ export const useStatusBulkActionItems = ({ key="close" data-test-subj="close-alert-status" onClick={() => onClickUpdate(FILTER_CLOSED)} - size="s" > {i18n.BULK_ACTION_CLOSE_SELECTED} diff --git a/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx b/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx index 0c492ad8f8a59..121e5bda78ed8 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx +++ b/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx @@ -13,7 +13,7 @@ import { mockGlobalState } from '../../mock/global_state'; import { TGridModelSettings } from '.'; const id = 'foo'; -const timelineById = { +const defaultTimelineById = { ...mockGlobalState.timelineById, }; @@ -28,16 +28,32 @@ describe('setInitializeTgridSettings', () => { sort, // <-- override }; - expect(setInitializeTgridSettings({ id, timelineById, tGridSettingsProps })[id].sort).toEqual( - sort - ); + expect( + setInitializeTgridSettings({ id, timelineById: defaultTimelineById, tGridSettingsProps })[id] + .sort + ).toEqual(sort); }); test('it returns the default sort when tGridSettingsProps does NOT contain an override', () => { const tGridSettingsProps = { footerText: 'test' }; // <-- no `sort` override - expect(setInitializeTgridSettings({ id, timelineById, tGridSettingsProps })[id].sort).toEqual( - tGridDefaults.sort - ); + expect( + setInitializeTgridSettings({ id, timelineById: defaultTimelineById, tGridSettingsProps })[id] + .sort + ).toEqual(tGridDefaults.sort); + }); + + test('it doesn`t overwrite the timeline if it is initialized', () => { + const tGridSettingsProps = { title: 'testTitle' }; + + const timelineById = { + [id]: { + ...defaultTimelineById.test, + initialized: true, + }, + }; + + const result = setInitializeTgridSettings({ id, timelineById, tGridSettingsProps }); + expect(result).toBe(timelineById); }); }); diff --git a/x-pack/plugins/timelines/public/store/t_grid/helpers.ts b/x-pack/plugins/timelines/public/store/t_grid/helpers.ts index c7c015a283b75..730d127d16d98 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/helpers.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/helpers.ts @@ -160,20 +160,24 @@ export const setInitializeTgridSettings = ({ }: InitializeTgridParams): TimelineById => { const timeline = timelineById[id]; - return { - ...timelineById, - [id]: { - ...tGridDefaults, - ...timeline, - ...getTGridManageDefaults(id), - ...tGridSettingsProps, - ...(!timeline || (isEmpty(timeline.columns) && !isEmpty(tGridSettingsProps.defaultColumns)) - ? { columns: tGridSettingsProps.defaultColumns } - : {}), - sort: tGridSettingsProps.sort ?? tGridDefaults.sort, - loadingEventIds: tGridDefaults.loadingEventIds, - }, - }; + return !timeline?.initialized + ? { + ...timelineById, + [id]: { + ...tGridDefaults, + ...getTGridManageDefaults(id), + ...timeline, + ...tGridSettingsProps, + ...(!timeline || + (isEmpty(timeline.columns) && !isEmpty(tGridSettingsProps.defaultColumns)) + ? { columns: tGridSettingsProps.defaultColumns } + : {}), + sort: tGridSettingsProps.sort ?? tGridDefaults.sort, + loadingEventIds: tGridDefaults.loadingEventIds, + initialized: true, + }, + } + : timelineById; }; interface ApplyDeltaToTimelineColumnWidth { diff --git a/x-pack/plugins/timelines/public/store/t_grid/model.ts b/x-pack/plugins/timelines/public/store/t_grid/model.ts index 2c39e3739b691..0972189b38b30 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/model.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/model.ts @@ -82,6 +82,7 @@ export interface TGridModel extends TGridModelSettings { selectedEventIds: Record; savedObjectId: string | null; version: string | null; + initialized?: boolean; } export type TGridModelForTimeline = Pick< diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts index bd70d989d97dd..b2e073d3ecf59 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts @@ -5,26 +5,10 @@ * 2.0. */ -import { - AlertConsumers as CONSUMERS, - ALERT_RULE_CONSUMER, - ALERT_RULE_TYPE_ID, - SPACE_IDS, -} from '@kbn/rule-data-utils'; +import { ALERT_RULE_CONSUMER, ALERT_RULE_TYPE_ID, SPACE_IDS } from '@kbn/rule-data-utils'; import { map, mergeMap, catchError } from 'rxjs/operators'; import { from } from 'rxjs'; -import type { - AlertConsumers, - mapConsumerToIndexName as mapConsumerToIndexNameTyped, - isValidFeatureId as isValidFeatureIdTyped, -} from '@kbn/rule-data-utils'; -import { - mapConsumerToIndexName as mapConsumerToIndexNameNonTyped, - isValidFeatureId as isValidFeatureIdNonTyped, - // @ts-expect-error -} from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac'; - import { AlertingAuthorizationEntity, AlertingAuthorizationFilterType, @@ -49,9 +33,6 @@ import { ISearchOptions, } from '../../../../../../src/plugins/data/common'; -const mapConsumerToIndexName: typeof mapConsumerToIndexNameTyped = mapConsumerToIndexNameNonTyped; -const isValidFeatureId: typeof isValidFeatureIdTyped = isValidFeatureIdNonTyped; - export const timelineSearchStrategyProvider = ( data: PluginStart, alerting: AlertingPluginStartContract @@ -63,7 +44,6 @@ export const timelineSearchStrategyProvider = { const factoryQueryType = request.factoryQueryType; const entityType = request.entityType; - const alertConsumers = request.alertConsumers; if (factoryQueryType == null) { throw new Error('factoryQueryType is required'); @@ -71,13 +51,7 @@ export const timelineSearchStrategyProvider = = timelineFactory[factoryQueryType]; - if (alertConsumers != null && entityType != null && entityType === EntityType.ALERTS) { - const allFeatureIdsValid = alertConsumers.every((id) => isValidFeatureId(id)); - - if (!allFeatureIdsValid) { - throw new Error('An invalid alerts consumer feature id was provided'); - } - + if (entityType != null && entityType === EntityType.ALERTS) { return timelineAlertsSearchStrategy({ es: esAsInternal, request, @@ -85,7 +59,6 @@ export const timelineSearchStrategyProvider = ({ deps, queryFactory, alerting, - alertConsumers, }: { es: ISearchStrategy; request: TimelineStrategyRequestType; @@ -139,17 +111,10 @@ const timelineAlertsSearchStrategy = ({ deps: SearchStrategyDependencies; alerting: AlertingPluginStartContract; queryFactory: TimelineFactory; - alertConsumers: AlertConsumers[]; }) => { // Based on what solution alerts you want to see, figures out what corresponding // index to query (ex: siem --> .alerts-security.alerts) - const indices = alertConsumers.flatMap((consumer) => { - if (consumer === CONSUMERS.SIEM) { - return request.defaultIndex ?? request.indexType; - } - - return `${mapConsumerToIndexName[consumer]}`; - }); + const indices = request.defaultIndex ?? request.indexType; const requestWithAlertsIndices = { ...request, defaultIndex: indices, indexName: indices }; // Note: Alerts RBAC are built off of the alerting's authorization class, which diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1d660f5ab680a..dda5b79ebecee 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -296,11 +296,8 @@ "core.euiColumnSorting.emptySorting": "現在並び替えられているフィールドはありません", "core.euiColumnSorting.pickFields": "並び替え基準でフィールドの選択", "core.euiColumnSorting.sortFieldAriaLabel": "並べ替え基準:", - "core.euiColumnSortingDraggable.activeSortLabel": "このデータグリッドを並び替え中", "core.euiColumnSortingDraggable.defaultSortAsc": "A-Z", "core.euiColumnSortingDraggable.defaultSortDesc": "Z-A", - "core.euiColumnSortingDraggable.removeSortLabel": "データグリッドの並び替えから削除:", - "core.euiColumnSortingDraggable.toggleLegend": "フィールドの並び替え方法を選択:", "core.euiComboBoxOptionsList.allOptionsSelected": "利用可能なオプションをすべて選択しました", "core.euiComboBoxOptionsList.alreadyAdded": "{label} はすでに追加されています", "core.euiComboBoxOptionsList.createCustomOption": "{searchValue}をカスタムオプションとして追加", @@ -311,14 +308,8 @@ "core.euiComboBoxPill.removeSelection": "グループの選択項目から {children} を削除してください", "core.euiCommonlyUsedTimeRanges.legend": "頻繁に使用", "core.euiDataGrid.ariaLabel": "{label}; {page}/{pageCount}ページ。", - "core.euiDataGrid.ariaLabelGridPagination": "前のグリッドのページネーション:{label}", "core.euiDataGrid.ariaLabelledBy": "{page}/{pageCount}ページ。", - "core.euiDataGrid.ariaLabelledByGridPagination": "前のグリッドのページネーション", - "core.euiDataGrid.fullScreenButton": "全画面", - "core.euiDataGrid.fullScreenButtonActive": "全画面を終了", "core.euiDataGrid.screenReaderNotice": "セルにはインタラクティブコンテンツが含まれます。", - "core.euiDataGridCell.column": "列", - "core.euiDataGridCell.row": "行", "core.euiDataGridCellButtons.expandButtonTitle": "クリックするか enter を押すと、セルのコンテンツとインタラクトできます。", "core.euiDataGridHeaderCell.headerActions": "ヘッダーアクション", "core.euiDataGridSchema.booleanSortTextAsc": "False-True", @@ -696,9 +687,6 @@ "dashboard.urlWasRemovedInSixZeroWarningMessage": "URL「dashboard/create」は6.0で廃止されました。ブックマークを更新してください。", "data.advancedSettings.autocompleteIgnoreTimerange": "時間範囲を使用", "data.advancedSettings.autocompleteIgnoreTimerangeText": "このプロパティを無効にすると、現在の時間範囲からではなく、データセットからオートコンプリートの候補を取得します。 {learnMoreLink}", - "data.advancedSettings.courier.batchSearchesText": "Kibana は新しい非同期検索とインフラストラクチャを使用します。\n レガシー同期動作にフォールバックする場合は、このオプションを有効にします", - "data.advancedSettings.courier.batchSearchesTextDeprecation": "この設定はサポートが終了し、Kibana 8.0 では削除されます。", - "data.advancedSettings.courier.batchSearchesTitle": "同期検索を使用", "data.advancedSettings.courier.customRequestPreference.requestPreferenceLinkText": "リクエスト設定", "data.advancedSettings.courier.customRequestPreferenceText": "{setRequestReferenceSetting} が {customSettingValue} に設定されている時に使用される {requestPreferenceLink} です。", "data.advancedSettings.courier.customRequestPreferenceTitle": "カスタムリクエスト設定", @@ -4148,14 +4136,14 @@ "visTypeTable.vis.controls.formattedCSVButtonLabel": "フォーマット済み", "visTypeTable.vis.controls.rawCSVButtonLabel": "未加工", "visTypeTable.vis.noResultsFoundTitle": "結果が見つかりませんでした", - "visTypeTagCloud.feedbackMessage.tooSmallContainerDescription": "コンテナーが小さすぎてクラウド全体を表示できません。タグが切り取られたか省略されている可能性があります。", - "visTypeTagCloud.feedbackMessage.truncatedTagsDescription": "描写時間が長くなるのを防ぐため、タグの数が切り捨てられています。", - "visTypeTagCloud.function.bucket.help": "バケットディメンションの構成です。", - "visTypeTagCloud.function.help": "タグクラウドのビジュアライゼーションです。", - "visTypeTagCloud.function.metric.help": "メトリックディメンションの構成です。", - "visTypeTagCloud.function.orientation.help": "タグクラウド内の単語の方向です。", - "visTypeTagCloud.function.paletteHelpText": "グラフパレット名を定義します", - "visTypeTagCloud.function.scale.help": "単語のフォントサイズを決定するスケールです", + "expressionTagcloud.feedbackMessage.tooSmallContainerDescription": "コンテナーが小さすぎてクラウド全体を表示できません。タグが切り取られたか省略されている可能性があります。", + "expressionTagcloud.feedbackMessage.truncatedTagsDescription": "描写時間が長くなるのを防ぐため、タグの数が切り捨てられています。", + "expressionTagcloud.functions.tagcloud.args.bucketHelpText": "バケットディメンションの構成です。", + "expressionTagcloud.functions.tagcloudHelpText": "タグクラウドのビジュアライゼーションです。", + "expressionTagcloud.functions.tagcloud.args.metricHelpText": "メトリックディメンションの構成です。", + "expressionTagcloud.functions.tagcloud.args.orientationHelpText": "タグクラウド内の単語の方向です。", + "expressionTagcloud.functions.tagcloud.args.paletteHelpText": "グラフパレット名を定義します", + "expressionTagcloud.functions.tagcloud.args.scaleHelpText": "単語のフォントサイズを決定するスケールです", "visTypeTagCloud.orientations.multipleText": "複数", "visTypeTagCloud.orientations.rightAngledText": "直角", "visTypeTagCloud.orientations.singleText": "単一", @@ -5438,7 +5426,6 @@ "xpack.apm.correlations.customize.fieldPlaceholder": "オプションを選択または作成", "xpack.apm.correlations.customize.thresholdLabel": "しきい値", "xpack.apm.correlations.customize.thresholdPercentile": "{percentile}パーセンタイル", - "xpack.apm.correlations.latencyCorrelations.cancelButtonTitle": "キャンセル", "xpack.apm.correlations.latencyCorrelations.correlationsTable.actionsLabel": "フィルター", "xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription": "サービスの遅延に対するフィールドの影響。0~1の範囲。", "xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationLabel": "相関関係", @@ -5449,9 +5436,6 @@ "xpack.apm.correlations.latencyCorrelations.correlationsTable.filterDescription": "値でフィルタリング", "xpack.apm.correlations.latencyCorrelations.correlationsTable.filterLabel": "フィルター", "xpack.apm.correlations.latencyCorrelations.errorTitle": "相関関係の取得中にエラーが発生しました", - "xpack.apm.correlations.latencyCorrelations.progressAriaLabel": "進捗", - "xpack.apm.correlations.latencyCorrelations.progressTitle": "進捗状況: {progress}%", - "xpack.apm.correlations.latencyCorrelations.refreshButtonTitle": "更新", "xpack.apm.csm.breakdownFilter.browser": "ブラウザー", "xpack.apm.csm.breakdownFilter.device": "デバイス", "xpack.apm.csm.breakdownFilter.location": "場所", @@ -7185,21 +7169,6 @@ "xpack.canvas.workpadTemplates.table.descriptionColumnTitle": "説明", "xpack.canvas.workpadTemplates.table.nameColumnTitle": "テンプレート名", "xpack.canvas.workpadTemplates.table.tagsColumnTitle": "タグ", - "expressionShape.functions.progress.args.barColorHelpText": "背景バーの色です。", - "expressionShape.functions.progress.args.barWeightHelpText": "背景バーの太さです。", - "expressionShape.functions.progress.args.fontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。", - "expressionShape.functions.progress.args.labelHelpText": "ラベルの表示・非表示を切り替えるには、{BOOLEAN_TRUE}または{BOOLEAN_FALSE}を使用します。また、ラベルとして表示する文字列を入力することもできます。", - "expressionShape.functions.progress.args.maxHelpText": "進捗エレメントの最高値です。", - "expressionShape.functions.progress.args.shapeHelpText": "{list} または {end} を選択します。", - "expressionShape.functions.progress.args.valueColorHelpText": "進捗バーの色です。", - "expressionShape.functions.progress.args.valueWeightHelpText": "進捗バーの太さです。", - "expressionShape.functions.progress.invalidMaxValueErrorMessage": "無効な {arg} 値:「{max, number}」。「{arg}」は 0 より大きい必要があります", - "expressionShape.functions.progress.invalidValueErrorMessage": "無効な値:「{value, number}」。値は 0 と {max, number} の間でなければなりません", - "expressionShape.functions.progressHelpText": "進捗エレメントを構成します。", - "expressionShape.renderer.progress.displayName": "進捗インジケーター", - "expressionShape.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします", - "expressionShape.renderer.shape.displayName": "形状", - "expressionShape.renderer.shape.helpDescription": "基本的な図形をレンダリングします", "expressionRepeatImage.error.repeatImage.missingMaxArgument": "{emptyImageArgument} を指定する場合は、{maxArgument} を設定する必要があります", "expressionRepeatImage.functions.repeatImage.args.emptyImageHelpText": "この画像のエレメントについて、{CONTEXT}および{maxArg}パラメーターの差異を解消します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", "expressionRepeatImage.functions.repeatImage.args.imageHelpText": "繰り返す画像です。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", @@ -7221,6 +7190,8 @@ "expressionMetric.functions.metricHelpText": "ラベルの上に数字を表示します。", "expressionMetric.renderer.metric.displayName": "メトリック", "expressionMetric.renderer.metric.helpDescription": "ラベルの上に数字をレンダリングします", + "expressionShape.renderer.shape.displayName": "形状", + "expressionShape.renderer.shape.helpDescription": "基本的な図形をレンダリングします", "expressionError.errorComponent.description": "表現が失敗し次のメッセージが返されました:", "expressionError.errorComponent.title": "おっと!表現が失敗しました", "expressionError.renderer.debug.displayName": "デバッグ", @@ -7622,9 +7593,6 @@ "xpack.dashboard.drilldown.goToDashboard": "ダッシュボードに移動", "xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "ドリルダウンを作成", "xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "ドリルダウンを管理", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation": "この設定はサポートが終了し、Kibana 8.0 では削除されます。", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription": "ダッシュボード表示専用モードのロールです", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "ダッシュボード専用ロール", "xpack.data.mgmt.searchSessions.actionDelete": "削除", "xpack.data.mgmt.searchSessions.actionExtend": "延長", "xpack.data.mgmt.searchSessions.actionRename": "名前を編集", @@ -18138,10 +18106,6 @@ "xpack.monitoring.updateLicenseTitle": "ライセンスの更新", "xpack.monitoring.useAvailableLicenseDescription": "すでに新しいライセンスがある場合は、今すぐアップロードしてください。", "xpack.observability.alerts.searchBarPlaceholder": "\"domain\": \"ecommerce\" AND (\"service.name\":\"ProductCatalogService\" …)", - "xpack.observability.alerts.statusFilter.allButtonLabel": "すべて", - "xpack.observability.alerts.statusFilter.closedButtonLabel": "終了", - "xpack.observability.alerts.statusFilter.openButtonLabel": "開く", - "xpack.observability.alerts.statusFilterAriaLabel": "未解決および終了ステータスでアラートをフィルター", "xpack.observability.alertsDisclaimerLinkText": "アラートとアクション", "xpack.observability.alertsDisclaimerText": "このページには実験アラートビューが表示されます。ここに表示されるデータは、アラートを正確に表していない可能性があります。アラートの非実験リストは、スタック管理のアラートとアクション設定にあります。", "xpack.observability.alertsDisclaimerTitle": "実験的", @@ -23262,8 +23226,6 @@ "xpack.spaces.management.confirmDeleteModal.errorMessage": "スペース'{name}'を削除できませんでした", "xpack.spaces.management.confirmDeleteModal.successMessage": "スペース'{name}'が削除されました", "xpack.spaces.management.confirmDeleteModal.title": "スペース'{name}'を削除しますか?", - "xpack.spaces.management.copyToSpace.actionDescription": "1つ以上のスペースでこの保存されたオブジェクトのコピーを作成します", - "xpack.spaces.management.copyToSpace.actionTitle": "スペースにコピー", "xpack.spaces.management.copyToSpace.cancelButton": "キャンセル", "xpack.spaces.management.copyToSpace.copyDetail.overwriteSwitch": "上書きしますか?", "xpack.spaces.management.copyToSpace.copyDetail.selectControlLabel": "オブジェクトID", @@ -23372,13 +23334,9 @@ "xpack.spaces.navControl.spacesMenu.changeCurrentSpaceTitle": "現在のスペースの変更", "xpack.spaces.navControl.spacesMenu.findSpacePlaceholder": "スペースを検索", "xpack.spaces.navControl.spacesMenu.noSpacesFoundTitle": " スペースが見つかりません ", - "xpack.spaces.shareToSpace.actionDescription": "この保存されたオブジェクトを1つ以上のスペースと共有します。", - "xpack.spaces.shareToSpace.actionTitle": "スペースと共有", "xpack.spaces.shareToSpace.aliasTableCalloutTitle": "レガシーURL競合", "xpack.spaces.shareToSpace.allSpacesTarget": "すべてのスペース", "xpack.spaces.shareToSpace.cancelButton": "キャンセル", - "xpack.spaces.shareToSpace.columnDescription": "このオブジェクトが現在共有されている他のスペース", - "xpack.spaces.shareToSpace.columnTitle": "共有されているスペース", "xpack.spaces.shareToSpace.continueButton": "続行", "xpack.spaces.shareToSpace.currentSpaceBadge": "現在", "xpack.spaces.shareToSpace.featureIsDisabledTooltip": "この機能はこのスペースでは無効です。", @@ -24640,24 +24598,13 @@ "xpack.upgradeAssistant.breadcrumb.kibanaDeprecationsLabel": "Kibanaの廃止予定", "xpack.upgradeAssistant.breadcrumb.overviewLabel": "アップグレードアシスタント", "xpack.upgradeAssistant.checkupTab.changeFiltersShowMoreLabel": "より多く表示させるにはフィルターを変更します。", - "xpack.upgradeAssistant.checkupTab.confirmationModal.removeButtonLabel": "削除", "xpack.upgradeAssistant.checkupTab.controls.filterBar.criticalButtonLabel": "重大", "xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIndexLabel": "インデックス別", "xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIssueLabel": "問題別", "xpack.upgradeAssistant.checkupTab.deprecations.criticalActionTooltip": "アップグレード前にこの問題を解決してください。", "xpack.upgradeAssistant.checkupTab.deprecations.criticalLabel": "重大", - "xpack.upgradeAssistant.checkupTab.deprecations.documentationButtonLabel": "ドキュメント", - "xpack.upgradeAssistant.checkupTab.deprecations.indexTable.detailsColumnLabel": "詳細", - "xpack.upgradeAssistant.checkupTab.deprecations.indexTable.indexColumnLabel": "インデックス", "xpack.upgradeAssistant.checkupTab.deprecations.warningActionTooltip": "アップグレード前にこの問題を解決することをお勧めしますが、必須ではありません。", "xpack.upgradeAssistant.checkupTab.deprecations.warningLabel": "警告", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.cancelButtonLabel": "キャンセル", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.description": "次の廃止予定のインデックス設定が検出されました。これらは削除される予定です。", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.errorNotificationText": "インデックス設定の削除エラー", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.successNotificationText": "インデックス設定が削除されました", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.title": "廃止予定の設定を'{indexName}'から削除しますか?", - "xpack.upgradeAssistant.checkupTab.indexSettings.doneButtonLabel": "完了", - "xpack.upgradeAssistant.checkupTab.indexSettings.fixButtonLabel": "修正", "xpack.upgradeAssistant.checkupTab.noDeprecationsLabel": "説明がありません", "xpack.upgradeAssistant.checkupTab.numDeprecationsShownLabel": "{total} 件中 {numShown} 件を表示中", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.cancelButtonLabel": "キャンセル", @@ -24685,7 +24632,6 @@ "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.resumeWatcherStepTitle": "Watcher を再開中", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.stopWatcherStepTitle": "Watcher を停止中", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklistTitle": "プロセスを再インデックス中", - "xpack.upgradeAssistant.checkupTab.reindexing.flyout.flyoutHeader": "{indexName} を再インデックス", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails": "このインデックスは現在閉じています。アップグレードアシスタントが開き、再インデックスを実行してからインデックスを閉じます。 {reindexingMayTakeLongerEmph}。詳細については {docs} をご覧ください。", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails.reindexingTakesLongerEmphasis": "再インデックスには通常よりも時間がかかることがあります", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutTitle": "インデックスが閉じました", @@ -24697,13 +24643,6 @@ "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail": "続行する前に、インデックスをバックアップしてください。再インデックスを続行するには、各変更を承諾してください。", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle": "このインデックスには元に戻すことのできない破壊的な変更が含まれています", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.documentationLinkLabel": "ドキュメント", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.cancelledLabel": "キャンセル済み", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.doneLabel": "完了", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.failedLabel": "失敗", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.indexClosedToolTipDetails": "「{indexName}」は再インデックスが必要ですが現在閉じています。アップグレードアシスタントが開き、再インデックスを実行してからインデックスを閉じます。再インデックスには通常よりも時間がかかることがあります。", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.loadingLabel": "読み込み中…", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.pausedLabel": "一時停止中", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.reindexLabel": "再インデックス", "xpack.upgradeAssistant.deprecationGroupItem.docLinkText": "ドキュメンテーションを表示", "xpack.upgradeAssistant.deprecationGroupItem.fixButtonLabel": "修正する手順を表示", "xpack.upgradeAssistant.deprecationGroupItem.resolveButtonLabel": "クイック解決", @@ -24721,13 +24660,6 @@ "xpack.upgradeAssistant.esDeprecationErrors.partiallyUpgradedWarningMessage": "Kibanaをご使用のElasticsearchクラスターと同じバージョンにアップグレードしてください。クラスターの1つ以上のノードがKibanaとは異なるバージョンを実行しています。", "xpack.upgradeAssistant.esDeprecationErrors.permissionsErrorMessage": "Elasticsearchの廃止予定を表示する権限がありません。", "xpack.upgradeAssistant.esDeprecationErrors.upgradedWarningMessage": "構成は最新です。KibanaおよびすべてのElasticsearchノードは同じバージョンを実行しています。", - "xpack.upgradeAssistant.esDeprecations.backupDataButtonLabel": "データをバックアップ", - "xpack.upgradeAssistant.esDeprecations.backupDataTooltipText": "変更を行う前にスナップショットを作成します。", - "xpack.upgradeAssistant.esDeprecations.clusterLabel": "クラスター", - "xpack.upgradeAssistant.esDeprecations.clusterTabLabel": "クラスター", - "xpack.upgradeAssistant.esDeprecations.docLinkText": "ドキュメント", - "xpack.upgradeAssistant.esDeprecations.indexLabel": "インデックス", - "xpack.upgradeAssistant.esDeprecations.indicesTabLabel": "インデックス", "xpack.upgradeAssistant.esDeprecations.loadingText": "廃止予定を読み込んでいます...", "xpack.upgradeAssistant.esDeprecations.pageDescription": "廃止予定のクラスターとインデックス設定をレビューします。アップグレード前に重要な問題を解決する必要があります。", "xpack.upgradeAssistant.esDeprecations.pageTitle": "Elasticsearch", @@ -24735,7 +24667,6 @@ "xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle": "重大", "xpack.upgradeAssistant.esDeprecationStats.loadingText": "Elasticsearchの廃止統計情報を読み込んでいます...", "xpack.upgradeAssistant.esDeprecationStats.statsTitle": "Elasticsearch", - "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip": "このクラスターは{clusterCount}個の廃止予定のクラスター設定と{indexCount}個の廃止予定のインデックス設定を使用しています。", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorDescription": "エラーについては、Kibanaサーバーログを確認してください。", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorTitle": "Kibana廃止予定を取得できませんでした", "xpack.upgradeAssistant.kibanaDeprecationErrors.pluginErrorDescription": "エラーについては、Kibanaサーバーログを確認してください。", @@ -24759,10 +24690,8 @@ "xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage": "Kibana廃止予定の取得中にエラーが発生しました。", "xpack.upgradeAssistant.kibanaDeprecationStats.loadingText": "Kibana廃止予定統計情報を読み込んでいます…", "xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle": "Kibana", - "xpack.upgradeAssistant.noDeprecationsPrompt.description": "構成は最新です。", "xpack.upgradeAssistant.noDeprecationsPrompt.nextStepsDescription": "他のスタック廃止予定については、{overviewButton}を確認してください。", "xpack.upgradeAssistant.noDeprecationsPrompt.overviewLinkText": "概要ページ", - "xpack.upgradeAssistant.noDeprecationsPrompt.title": "アップグレードする準備ができました。", "xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage": "廃止予定のアクションをログに出力しません。", "xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage": "廃止予定のアクションをログに出力します。", "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage": "ログ情報を取得できませんでした。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f7f4822f7ecc4..bfee81a99d092 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -299,11 +299,8 @@ "core.euiColumnSorting.emptySorting": "当前未排序任何字段", "core.euiColumnSorting.pickFields": "选取排序依据的字段", "core.euiColumnSorting.sortFieldAriaLabel": "排序依据:", - "core.euiColumnSortingDraggable.activeSortLabel": "正在排序此数据网格", "core.euiColumnSortingDraggable.defaultSortAsc": "A-Z", "core.euiColumnSortingDraggable.defaultSortDesc": "Z-A", - "core.euiColumnSortingDraggable.removeSortLabel": "从数据网格排序中移除:", - "core.euiColumnSortingDraggable.toggleLegend": "为字段选择排序方法:", "core.euiComboBoxOptionsList.allOptionsSelected": "您已选择所有可用选项", "core.euiComboBoxOptionsList.alreadyAdded": "{label} 已添加", "core.euiComboBoxOptionsList.createCustomOption": "将 {searchValue} 添加为自定义选项", @@ -314,14 +311,8 @@ "core.euiComboBoxPill.removeSelection": "将 {children} 从此组中的选择移除", "core.euiCommonlyUsedTimeRanges.legend": "常用", "core.euiDataGrid.ariaLabel": "{label};第 {page} 页,共 {pageCount} 页。", - "core.euiDataGrid.ariaLabelGridPagination": "前面网格的分页:{label}", "core.euiDataGrid.ariaLabelledBy": "第 {page} 页,共 {pageCount} 页。", - "core.euiDataGrid.ariaLabelledByGridPagination": "前面网格的分页", - "core.euiDataGrid.fullScreenButton": "全屏", - "core.euiDataGrid.fullScreenButtonActive": "退出全屏", "core.euiDataGrid.screenReaderNotice": "单元格包含交互内容。", - "core.euiDataGridCell.column": "列", - "core.euiDataGridCell.row": "行", "core.euiDataGridCellButtons.expandButtonTitle": "单击或按 Enter 键以便与单元格内容进行交互", "core.euiDataGridHeaderCell.headerActions": "标题操作", "core.euiDataGridSchema.booleanSortTextAsc": "False-True", @@ -699,9 +690,6 @@ "dashboard.urlWasRemovedInSixZeroWarningMessage": "6.0 中已移除 url“dashboard/create”。请更新您的书签。", "data.advancedSettings.autocompleteIgnoreTimerange": "使用时间范围", "data.advancedSettings.autocompleteIgnoreTimerangeText": "禁用此属性可从您的完全数据集中获取自动完成建议,而非从当前时间范围。 {learnMoreLink}", - "data.advancedSettings.courier.batchSearchesText": "Kibana 使用新的异步搜索和基础架构。\n 如果想回退到旧式同步操作,请启用此选项", - "data.advancedSettings.courier.batchSearchesTextDeprecation": "此设置已过时,将在 Kibana 8.0 中移除。", - "data.advancedSettings.courier.batchSearchesTitle": "使用同步搜索", "data.advancedSettings.courier.customRequestPreference.requestPreferenceLinkText": "请求首选项", "data.advancedSettings.courier.customRequestPreferenceText": "将“{setRequestReferenceSetting}”设置为“{customSettingValue}”时,将使用“{requestPreferenceLink}”。", "data.advancedSettings.courier.customRequestPreferenceTitle": "定制请求首选项", @@ -4167,14 +4155,14 @@ "visTypeTable.vis.controls.formattedCSVButtonLabel": "格式化", "visTypeTable.vis.controls.rawCSVButtonLabel": "原始", "visTypeTable.vis.noResultsFoundTitle": "找不到结果", - "visTypeTagCloud.feedbackMessage.tooSmallContainerDescription": "容器太小,无法显示整个云。标签可能被裁剪或省略。", - "visTypeTagCloud.feedbackMessage.truncatedTagsDescription": "标签数量已截断,以避免绘制时间过长。", - "visTypeTagCloud.function.bucket.help": "存储桶维度配置", - "visTypeTagCloud.function.help": "标签云图可视化", - "visTypeTagCloud.function.metric.help": "指标维度配置", - "visTypeTagCloud.function.orientation.help": "标签云图内的字方向", - "visTypeTagCloud.function.paletteHelpText": "定义图表调色板名称", - "visTypeTagCloud.function.scale.help": "缩放以确定字体大小", + "expressionTagcloud.feedbackMessage.tooSmallContainerDescription": "容器太小,无法显示整个云。标签可能被裁剪或省略。", + "expressionTagcloud.feedbackMessage.truncatedTagsDescription": "标签数量已截断,以避免绘制时间过长。", + "expressionTagcloud.functions.tagcloud.args.bucketHelpText": "存储桶维度配置", + "expressionTagcloud.functions.tagcloudHelpText": "标签云图可视化", + "expressionTagcloud.functions.tagcloud.args.metricHelpText": "指标维度配置", + "expressionTagcloud.functions.tagcloud.args.orientationHelpText": "标签云图内的字方向", + "expressionTagcloud.functions.tagcloud.args.paletteHelpText": "定义图表调色板名称", + "expressionTagcloud.functions.tagcloud.args.scaleHelpText": "缩放以确定字体大小", "visTypeTagCloud.orientations.multipleText": "多个", "visTypeTagCloud.orientations.rightAngledText": "直角", "visTypeTagCloud.orientations.singleText": "单个", @@ -5462,7 +5450,6 @@ "xpack.apm.correlations.customize.fieldPlaceholder": "选择或创建选项", "xpack.apm.correlations.customize.thresholdLabel": "阈值", "xpack.apm.correlations.customize.thresholdPercentile": "第 {percentile} 个百分位数", - "xpack.apm.correlations.latencyCorrelations.cancelButtonTitle": "取消", "xpack.apm.correlations.latencyCorrelations.correlationsTable.actionsLabel": "筛选", "xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription": "字段对服务延迟的影响,范围从 0 到 1。", "xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationLabel": "相关性", @@ -5473,9 +5460,6 @@ "xpack.apm.correlations.latencyCorrelations.correlationsTable.filterDescription": "按值筛选", "xpack.apm.correlations.latencyCorrelations.correlationsTable.filterLabel": "筛选", "xpack.apm.correlations.latencyCorrelations.errorTitle": "提取关联性时发生错误", - "xpack.apm.correlations.latencyCorrelations.progressAriaLabel": "进度", - "xpack.apm.correlations.latencyCorrelations.progressTitle": "进度:{progress}%", - "xpack.apm.correlations.latencyCorrelations.refreshButtonTitle": "刷新", "xpack.apm.csm.breakdownFilter.browser": "浏览器", "xpack.apm.csm.breakdownFilter.device": "设备", "xpack.apm.csm.breakdownFilter.location": "位置", @@ -7851,9 +7835,6 @@ "xpack.dashboard.drilldown.goToDashboard": "前往仪表板", "xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "创建向下钻取", "xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "管理向下钻取", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation": "此设置已过时,将在 Kibana 8.0 中移除。", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription": "属于“仅查看仪表板”模式的角色", - "xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "仅限仪表板的角色", "xpack.data.mgmt.searchSessions.actionDelete": "删除", "xpack.data.mgmt.searchSessions.actionExtend": "延长", "xpack.data.mgmt.searchSessions.actionRename": "编辑名称", @@ -18547,10 +18528,6 @@ "xpack.monitoring.updateLicenseTitle": "更新您的许可证", "xpack.monitoring.useAvailableLicenseDescription": "如果您已经持有新的许可证,请立即上传。", "xpack.observability.alerts.searchBarPlaceholder": "\"domain\": \"ecommerce\" AND (\"service.name\":\"ProductCatalogService\" …)", - "xpack.observability.alerts.statusFilter.allButtonLabel": "全部", - "xpack.observability.alerts.statusFilter.closedButtonLabel": "已关闭", - "xpack.observability.alerts.statusFilter.openButtonLabel": "打开", - "xpack.observability.alerts.statusFilterAriaLabel": "按未结和关闭状态筛选告警", "xpack.observability.alertsDisclaimerLinkText": "告警和操作", "xpack.observability.alertsDisclaimerText": "此页面显示实验性告警视图。此处显示的数据可能无法准确表示告警。在“堆栈管理”的“告警和操作”中提供了告警的非实验性列表。", "xpack.observability.alertsDisclaimerTitle": "实验性", @@ -23791,8 +23768,6 @@ "xpack.spaces.management.confirmDeleteModal.errorMessage": "无法删除工作区“{name}”", "xpack.spaces.management.confirmDeleteModal.successMessage": "已删除工作区“{name}”", "xpack.spaces.management.confirmDeleteModal.title": "删除工作区“{name}”?", - "xpack.spaces.management.copyToSpace.actionDescription": "在一个或多个工作区中创建此已保存对象的副本", - "xpack.spaces.management.copyToSpace.actionTitle": "复制到工作区", "xpack.spaces.management.copyToSpace.cancelButton": "取消", "xpack.spaces.management.copyToSpace.copyDetail.overwriteSwitch": "覆盖?", "xpack.spaces.management.copyToSpace.copyDetail.selectControlLabel": "对象 ID", @@ -23902,14 +23877,10 @@ "xpack.spaces.navControl.spacesMenu.changeCurrentSpaceTitle": "更改当前空间", "xpack.spaces.navControl.spacesMenu.findSpacePlaceholder": "查找工作区", "xpack.spaces.navControl.spacesMenu.noSpacesFoundTitle": " 未找到工作区 ", - "xpack.spaces.shareToSpace.actionDescription": "将此已保存对象共享到一个或多个工作区", - "xpack.spaces.shareToSpace.actionTitle": "共享到工作区", "xpack.spaces.shareToSpace.aliasTableCalloutBody": "将禁用 {aliasesToDisableCount, plural, other {# 个旧版 URL}}。", "xpack.spaces.shareToSpace.aliasTableCalloutTitle": "旧版 URL 冲突", "xpack.spaces.shareToSpace.allSpacesTarget": "所有工作区", "xpack.spaces.shareToSpace.cancelButton": "取消", - "xpack.spaces.shareToSpace.columnDescription": "目前将此对象共享到的其他工作区", - "xpack.spaces.shareToSpace.columnTitle": "共享工作区", "xpack.spaces.shareToSpace.continueButton": "继续", "xpack.spaces.shareToSpace.currentSpaceBadge": "当前", "xpack.spaces.shareToSpace.featureIsDisabledTooltip": "此功能在此工作区中已禁用。", @@ -25190,25 +25161,13 @@ "xpack.upgradeAssistant.breadcrumb.kibanaDeprecationsLabel": "Kibana 弃用", "xpack.upgradeAssistant.breadcrumb.overviewLabel": "升级助手", "xpack.upgradeAssistant.checkupTab.changeFiltersShowMoreLabel": "更改筛选以显示更多内容。", - "xpack.upgradeAssistant.checkupTab.confirmationModal.removeButtonLabel": "移除", "xpack.upgradeAssistant.checkupTab.controls.filterBar.criticalButtonLabel": "紧急", "xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIndexLabel": "按索引", "xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIssueLabel": "按问题", "xpack.upgradeAssistant.checkupTab.deprecations.criticalActionTooltip": "请解决此问题后再升级。", "xpack.upgradeAssistant.checkupTab.deprecations.criticalLabel": "紧急", - "xpack.upgradeAssistant.checkupTab.deprecations.documentationButtonLabel": "文档", - "xpack.upgradeAssistant.checkupTab.deprecations.indexTable.detailsColumnLabel": "详情", - "xpack.upgradeAssistant.checkupTab.deprecations.indexTable.indexColumnLabel": "索引", "xpack.upgradeAssistant.checkupTab.deprecations.warningActionTooltip": "建议在升级之前先解决此问题,但这不是必需的。", "xpack.upgradeAssistant.checkupTab.deprecations.warningLabel": "警告", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.cancelButtonLabel": "取消", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.description": "检测到并将移除以下弃用的索引设置:", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.errorNotificationText": "移除索引设置时出错", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.successNotificationText": "索引设置已移除", - "xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.title": "从“{indexName}”移除已弃用的设置?", - "xpack.upgradeAssistant.checkupTab.indexSettings.doneButtonLabel": "完成", - "xpack.upgradeAssistant.checkupTab.indexSettings.fixButtonLabel": "修复", - "xpack.upgradeAssistant.checkupTab.indicesBadgeLabel": "{numIndices, plural, other { 个索引}}", "xpack.upgradeAssistant.checkupTab.noDeprecationsLabel": "无弃用内容", "xpack.upgradeAssistant.checkupTab.numDeprecationsShownLabel": "显示 {numShown} 个,共 {total} 个", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.cancelButtonLabel": "取消", @@ -25236,7 +25195,6 @@ "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.resumeWatcherStepTitle": "正在恢复 Watcher", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.stopWatcherStepTitle": "正在停止 Watcher", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklistTitle": "重新索引过程", - "xpack.upgradeAssistant.checkupTab.reindexing.flyout.flyoutHeader": "重新索引 {indexName}", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails": "此索引当前已关闭。升级助手将打开索引,重新索引,然后关闭索引。{reindexingMayTakeLongerEmph}。请参阅文档{docs}以了解更多信息。", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails.reindexingTakesLongerEmphasis": "重新索引可能比通常花费更多的时间", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutTitle": "索引已关闭", @@ -25248,13 +25206,6 @@ "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail": "继续前备份索引。要继续重新索引,请接受每个更改。", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle": "此索引需要无法恢复的破坏性更改", "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.documentationLinkLabel": "文档", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.cancelledLabel": "已取消", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.doneLabel": "完成", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.failedLabel": "失败", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.indexClosedToolTipDetails": "“{indexName}”需要重新索引,但当前已关闭。升级助手将打开索引,重新索引,然后关闭索引。重新索引可能比通常花费更多的时间。", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.loadingLabel": "正在加载……", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.pausedLabel": "已暂停", - "xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.reindexLabel": "重新索引", "xpack.upgradeAssistant.deprecationGroupItem.docLinkText": "查看文档", "xpack.upgradeAssistant.deprecationGroupItem.fixButtonLabel": "显示修复步骤", "xpack.upgradeAssistant.deprecationGroupItem.resolveButtonLabel": "快速解决", @@ -25272,13 +25223,6 @@ "xpack.upgradeAssistant.esDeprecationErrors.partiallyUpgradedWarningMessage": "将 Kibana 升级到与您的 Elasticsearch 集群相同的版本。集群中的一个或多个节点正在运行与 Kibana 不同的版本。", "xpack.upgradeAssistant.esDeprecationErrors.permissionsErrorMessage": "您无权查看 Elasticsearch 弃用。", "xpack.upgradeAssistant.esDeprecationErrors.upgradedWarningMessage": "您的配置是最新的。Kibana 和索引 Elasticsearch 节点正在运行相同的版本。", - "xpack.upgradeAssistant.esDeprecations.backupDataButtonLabel": "备份您的数据", - "xpack.upgradeAssistant.esDeprecations.backupDataTooltipText": "在进行任何更改之前拍取快照。", - "xpack.upgradeAssistant.esDeprecations.clusterLabel": "集群", - "xpack.upgradeAssistant.esDeprecations.clusterTabLabel": "集群", - "xpack.upgradeAssistant.esDeprecations.docLinkText": "文档", - "xpack.upgradeAssistant.esDeprecations.indexLabel": "索引", - "xpack.upgradeAssistant.esDeprecations.indicesTabLabel": "索引", "xpack.upgradeAssistant.esDeprecations.loadingText": "正在加载弃用……", "xpack.upgradeAssistant.esDeprecations.pageDescription": "查看已弃用的群集和索引设置。在升级之前必须解决任何紧急问题。", "xpack.upgradeAssistant.esDeprecations.pageTitle": "Elasticsearch", @@ -25286,7 +25230,6 @@ "xpack.upgradeAssistant.esDeprecationStats.criticalDeprecationsTitle": "紧急", "xpack.upgradeAssistant.esDeprecationStats.loadingText": "正在加载 Elasticsearch 弃用统计……", "xpack.upgradeAssistant.esDeprecationStats.statsTitle": "Elasticsearch", - "xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip": "此集群正在使用 {clusterCount} 个已弃用集群设置和 {indexCount} 个已弃用的索引设置", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorDescription": "请在 Kibana 服务器日志中查看错误。", "xpack.upgradeAssistant.kibanaDeprecationErrors.loadingErrorTitle": "无法检索 Kibana 弃用", "xpack.upgradeAssistant.kibanaDeprecationErrors.pluginErrorDescription": "请在 Kibana 服务器日志中查看错误。", @@ -25310,10 +25253,8 @@ "xpack.upgradeAssistant.kibanaDeprecationStats.loadingErrorMessage": "检索 Kibana 弃用时发生错误。", "xpack.upgradeAssistant.kibanaDeprecationStats.loadingText": "正在加载 Kibana 弃用统计……", "xpack.upgradeAssistant.kibanaDeprecationStats.statsTitle": "Kibana", - "xpack.upgradeAssistant.noDeprecationsPrompt.description": "您的配置是最新的。", "xpack.upgradeAssistant.noDeprecationsPrompt.nextStepsDescription": "查看{overviewButton}以了解其他 Stack 弃用。", "xpack.upgradeAssistant.noDeprecationsPrompt.overviewLinkText": "“概览”页面", - "xpack.upgradeAssistant.noDeprecationsPrompt.title": "准备好升级!", "xpack.upgradeAssistant.overview.deprecationLogs.disabledToastMessage": "不记录弃用的操作。", "xpack.upgradeAssistant.overview.deprecationLogs.enabledToastMessage": "记录弃用的操作。", "xpack.upgradeAssistant.overview.deprecationLogs.fetchErrorMessage": "无法检索日志记录信息。", diff --git a/x-pack/plugins/triggers_actions_ui/kibana.json b/x-pack/plugins/triggers_actions_ui/kibana.json index 9e9af347af2a2..4033889d9811e 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.json +++ b/x-pack/plugins/triggers_actions_ui/kibana.json @@ -8,7 +8,7 @@ "server": true, "ui": true, "optionalPlugins": ["alerting", "features", "home", "spaces"], - "requiredPlugins": ["management", "charts", "data", "kibanaReact", "kibanaUtils", "savedObjects", "spacesOss"], + "requiredPlugins": ["management", "charts", "data", "kibanaReact", "kibanaUtils", "savedObjects"], "configPath": ["xpack", "trigger_actions_ui"], "extraPublicDirs": ["public/common", "public/common/constants"], "requiredBundles": ["home", "alerting", "esUiShared", "kibanaReact", "kibanaUtils"] diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index cc34f1beaf6b9..9786f5dcb949d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -18,7 +18,6 @@ import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; import type { SpacesPluginStart } from '../../../spaces/public'; -import type { SpacesOssPluginStart } from '../../../../../src/plugins/spaces_oss/public'; import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; @@ -37,7 +36,6 @@ export interface TriggersAndActionsUiServices extends CoreStart { charts: ChartsPluginStart; alerting?: AlertingStart; spaces?: SpacesPluginStart; - spacesOss: SpacesOssPluginStart; storage?: Storage; setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; actionTypeRegistry: ActionTypeRegistryContract; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx index 441daad1a50bd..9ecaa3d915551 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx @@ -15,7 +15,7 @@ import { ToastsApi } from 'kibana/public'; import { AlertDetailsRoute, getRuleData } from './alert_details_route'; import { Alert } from '../../../../types'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; -import { spacesOssPluginMock } from 'src/plugins/spaces_oss/public/mocks'; +import { spacesPluginMock } from '../../../../../../spaces/public/mocks'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -38,11 +38,11 @@ describe('alert_details_route', () => { jest.clearAllMocks(); }); - const spacesOssMock = spacesOssPluginMock.createStartContract(); + const spacesMock = spacesPluginMock.createStartContract(); async function setup() { const useKibanaMock = useKibana as jest.Mocked; // eslint-disable-next-line react-hooks/rules-of-hooks - useKibanaMock().services.spacesOss = spacesOssMock; + useKibanaMock().services.spaces = spacesMock; } it('render a loader while fetching data', () => { @@ -82,7 +82,7 @@ describe('alert_details_route', () => { expect(loadAlert).toHaveBeenCalledWith(rule.id); expect(resolveRule).toHaveBeenCalledWith(rule.id); - expect((spacesOssMock as any).ui.redirectLegacyUrl).toHaveBeenCalledWith( + expect((spacesMock as any).ui.redirectLegacyUrl).toHaveBeenCalledWith( `insightsAndAlerting/triggersActions/rule/new_id`, `rule` ); @@ -122,7 +122,7 @@ describe('alert_details_route', () => { expect(loadAlert).toHaveBeenCalledWith(rule.id); expect(resolveRule).toHaveBeenCalledWith(rule.id); - expect((spacesOssMock as any).ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({ + expect((spacesMock as any).ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({ currentObjectId: 'new_id', objectNoun: 'rule', otherObjectId: rule.id, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx index b6279d7fca100..123d60bb9fea3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx @@ -42,7 +42,7 @@ export const AlertDetailsRoute: React.FunctionComponent const { http, notifications: { toasts }, - spacesOss, + spaces: spacesApi, } = useKibana().services; const { basePath } = http; @@ -68,10 +68,10 @@ export const AlertDetailsRoute: React.FunctionComponent useEffect(() => { if (alert) { const outcome = (alert as ResolvedRule).outcome; - if (outcome === 'aliasMatch' && spacesOss.isSpacesAvailable) { + if (spacesApi && outcome === 'aliasMatch') { // This rule has been resolved from a legacy URL - redirect the user to the new URL and display a toast. const path = basePath.prepend(`insightsAndAlerting/triggersActions/rule/${alert.id}`); - spacesOss.ui.redirectLegacyUrl( + spacesApi.ui.redirectLegacyUrl( path, i18n.translate('xpack.triggersActionsUI.sections.alertDetails.redirectObjectNoun', { defaultMessage: 'rule', @@ -84,8 +84,8 @@ export const AlertDetailsRoute: React.FunctionComponent const getLegacyUrlConflictCallout = () => { const outcome = (alert as ResolvedRule).outcome; - const aliasTargetId = (alert as ResolvedRule).alias_target_id; - if (outcome === 'conflict' && aliasTargetId && spacesOss.isSpacesAvailable) { + if (spacesApi && outcome === 'conflict') { + const aliasTargetId = (alert as ResolvedRule).alias_target_id!; // This is always defined if outcome === 'conflict' // We have resolved to one rule, but there is another one with a legacy URL associated with this page. Display a // callout with a warning for the user, and provide a way for them to navigate to the other rule. const otherRulePath = basePath.prepend( @@ -94,7 +94,7 @@ export const AlertDetailsRoute: React.FunctionComponent return ( <> - {spacesOss.ui.components.getLegacyUrlConflict({ + {spacesApi.ui.components.getLegacyUrlConflict({ objectNoun: i18n.translate( 'xpack.triggersActionsUI.sections.alertDetails.redirectObjectNoun', { diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts index f8aa483711c30..2985a5306ed51 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts @@ -8,7 +8,6 @@ import React from 'react'; import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; -import { spacesOssPluginMock } from '../../../../../../../src/plugins/spaces_oss/public/mocks'; import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { TriggersAndActionsUiServices } from '../../../application/app'; @@ -45,7 +44,6 @@ export const createStartServicesMock = (): TriggersAndActionsUiServices => { element: ({ style: { cursor: 'pointer' }, } as unknown) as HTMLElement, - spacesOss: spacesOssPluginMock.createStartContract(), } as TriggersAndActionsUiServices; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 36d6964ce7753..7661eefba7f65 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -26,7 +26,6 @@ import { PluginStartContract as AlertingStart } from '../../alerting/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; import type { SpacesPluginStart } from '../../spaces/public'; -import type { SpacesOssPluginStart } from '../../../../src/plugins/spaces_oss/public'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; @@ -74,7 +73,6 @@ interface PluginsStart { charts: ChartsPluginStart; alerting?: AlertingStart; spaces?: SpacesPluginStart; - spacesOss: SpacesOssPluginStart; navigateToApp: CoreStart['application']['navigateToApp']; features: FeaturesPluginStart; } @@ -150,7 +148,6 @@ export class Plugin charts: pluginsStart.charts, alerting: pluginsStart.alerting, spaces: pluginsStart.spaces, - spacesOss: pluginsStart.spacesOss, element: params.element, storage: new Storage(window.localStorage), setBreadcrumbs: params.setBreadcrumbs, diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index e3c8b77d2c1d4..ac36780f10c01 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -24,6 +24,6 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/management/tsconfig.json" }, - { "path": "../../../src/plugins/spaces_oss/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" }, ] } diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts deleted file mode 100644 index 533a74842216a..0000000000000 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/cluster.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 'react-dom/test-utils'; -import { MlAction, ESUpgradeStatus } from '../../common/types'; - -import { ClusterTestBed, setupClusterPage, setupEnvironment } from './helpers'; - -describe('Cluster tab', () => { - let testBed: ClusterTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); - - describe('with deprecations', () => { - const snapshotId = '1'; - const jobId = 'deprecation_check_job'; - const esDeprecationsMockResponse: ESUpgradeStatus = { - totalCriticalDeprecations: 1, - cluster: [ - { - level: 'critical', - message: - 'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded', - details: - 'model snapshot [%s] for job [%s] supports minimum version [%s] and needs to be at least [%s]', - url: 'doc_url', - correctiveAction: { - type: 'mlSnapshot', - snapshotId, - jobId, - }, - }, - ], - indices: [], - }; - - beforeEach(async () => { - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ - isDeprecationLogIndexingEnabled: true, - isDeprecationLoggingEnabled: true, - }); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { actions, component } = testBed; - - component.update(); - - // Navigate to the cluster tab - await act(async () => { - actions.clickTab('cluster'); - }); - - component.update(); - }); - - test('renders deprecations', () => { - const { exists } = testBed; - expect(exists('clusterTabContent')).toBe(true); - expect(exists('deprecationsContainer')).toBe(true); - }); - - describe('fix ml snapshots button', () => { - let flyout: Element | null; - - beforeEach(async () => { - const { component, actions, exists, find } = testBed; - - expect(exists('deprecationsContainer')).toBe(true); - - // Open all deprecations - actions.clickExpandAll(); - - // The data-test-subj is derived from the deprecation message - const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message - .split(' ') - .join('_')}`; - - await act(async () => { - find(`${accordionTestSubj}.fixMlSnapshotsButton`).simulate('click'); - }); - - component.update(); - - // We need to read the document "body" as the flyout is added there and not inside - // the component DOM tree. - flyout = document.body.querySelector('[data-test-subj="fixSnapshotsFlyout"]'); - - expect(flyout).not.toBe(null); - expect(flyout!.textContent).toContain('Upgrade or delete model snapshot'); - }); - - test('upgrades snapshots', async () => { - const { component } = testBed; - - const upgradeButton: HTMLButtonElement | null = flyout!.querySelector( - '[data-test-subj="upgradeSnapshotButton"]' - ); - - httpRequestsMockHelpers.setUpgradeMlSnapshotResponse({ - nodeId: 'my_node', - snapshotId, - jobId, - status: 'in_progress', - }); - - await act(async () => { - upgradeButton!.click(); - }); - - component.update(); - - // First, we expect a POST request to upgrade the snapshot - const upgradeRequest = server.requests[server.requests.length - 2]; - expect(upgradeRequest.method).toBe('POST'); - expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); - - // Next, we expect a GET request to check the status of the upgrade - const statusRequest = server.requests[server.requests.length - 1]; - expect(statusRequest.method).toBe('GET'); - expect(statusRequest.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${jobId}/${snapshotId}` - ); - }); - - test('handles upgrade failure', async () => { - const { component, find } = testBed; - - const upgradeButton: HTMLButtonElement | null = flyout!.querySelector( - '[data-test-subj="upgradeSnapshotButton"]' - ); - - const error = { - statusCode: 500, - error: 'Upgrade snapshot error', - message: 'Upgrade snapshot error', - }; - - httpRequestsMockHelpers.setUpgradeMlSnapshotResponse(undefined, error); - - await act(async () => { - upgradeButton!.click(); - }); - - component.update(); - - const upgradeRequest = server.requests[server.requests.length - 1]; - expect(upgradeRequest.method).toBe('POST'); - expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); - - const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message - .split(' ') - .join('_')}`; - - expect(find(`${accordionTestSubj}.fixMlSnapshotsButton`).text()).toEqual('Failed'); - }); - - test('deletes snapshots', async () => { - const { component } = testBed; - - const deleteButton: HTMLButtonElement | null = flyout!.querySelector( - '[data-test-subj="deleteSnapshotButton"]' - ); - - httpRequestsMockHelpers.setDeleteMlSnapshotResponse({ - acknowledged: true, - }); - - await act(async () => { - deleteButton!.click(); - }); - - component.update(); - - const request = server.requests[server.requests.length - 1]; - const mlDeprecation = esDeprecationsMockResponse.cluster[0]; - - expect(request.method).toBe('DELETE'); - expect(request.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${ - (mlDeprecation.correctiveAction! as MlAction).jobId - }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` - ); - }); - - test('handles delete failure', async () => { - const { component, find } = testBed; - - const deleteButton: HTMLButtonElement | null = flyout!.querySelector( - '[data-test-subj="deleteSnapshotButton"]' - ); - - const error = { - statusCode: 500, - error: 'Upgrade snapshot error', - message: 'Upgrade snapshot error', - }; - - httpRequestsMockHelpers.setDeleteMlSnapshotResponse(undefined, error); - - await act(async () => { - deleteButton!.click(); - }); - - component.update(); - - const request = server.requests[server.requests.length - 1]; - const mlDeprecation = esDeprecationsMockResponse.cluster[0]; - - expect(request.method).toBe('DELETE'); - expect(request.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${ - (mlDeprecation.correctiveAction! as MlAction).jobId - }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` - ); - - const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message - .split(' ') - .join('_')}`; - - expect(find(`${accordionTestSubj}.fixMlSnapshotsButton`).text()).toEqual('Failed'); - }); - }); - }); - - describe('no deprecations', () => { - beforeEach(async () => { - const noDeprecationsResponse = { - totalCriticalDeprecations: 0, - cluster: [], - indices: [], - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(noDeprecationsResponse); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { component } = testBed; - - component.update(); - }); - - test('renders prompt', () => { - const { exists, find } = testBed; - expect(exists('noDeprecationsPrompt')).toBe(true); - expect(find('noDeprecationsPrompt').text()).toContain('Ready to upgrade!'); - }); - }); - - describe('error handling', () => { - test('handles 403', async () => { - const error = { - statusCode: 403, - error: 'Forbidden', - message: 'Forbidden', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('permissionsError')).toBe(true); - expect(find('permissionsError').text()).toContain( - 'You are not authorized to view Elasticsearch deprecations.' - ); - }); - - test('shows upgraded message when all nodes have been upgraded', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - // This is marked true in the scenario where none of the nodes have the same major version of Kibana, - // and therefore we assume all have been upgraded - allNodesUpgraded: true, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('upgradedCallout')).toBe(true); - expect(find('upgradedCallout').text()).toContain( - 'Your configuration is up to date. Kibana and all Elasticsearch nodes are running the same version.' - ); - }); - - test('shows partially upgrade error when nodes are running different versions', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: false, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('partiallyUpgradedWarning')).toBe(true); - expect(find('partiallyUpgradedWarning').text()).toContain( - 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' - ); - }); - - test('handles generic error', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupClusterPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('requestError')).toBe(true); - expect(find('requestError').text()).toContain( - 'Could not retrieve Elasticsearch deprecations.' - ); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts new file mode 100644 index 0000000000000..917fac8ef666a --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; + +import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; + +describe('Default deprecation flyout', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + afterAll(() => { + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + }); + + it('renders a flyout with deprecation details', async () => { + const multiFieldsDeprecation = esDeprecationsMockResponse.deprecations[2]; + const { actions, find, exists } = testBed; + + await actions.clickDefaultDeprecationAt(0); + + expect(exists('defaultDeprecationDetails')).toBe(true); + expect(find('defaultDeprecationDetails.flyoutTitle').text()).toContain( + multiFieldsDeprecation.message + ); + expect(find('defaultDeprecationDetails.flyoutDescription').text()).toContain( + multiFieldsDeprecation.index + ); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts new file mode 100644 index 0000000000000..ceebc528f0bc4 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts @@ -0,0 +1,267 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 'react-dom/test-utils'; + +import { API_BASE_PATH } from '../../../common/constants'; +import type { MlAction } from '../../../common/types'; +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; +import { + esDeprecationsMockResponse, + MOCK_SNAPSHOT_ID, + MOCK_JOB_ID, + createEsDeprecationsMockResponse, +} from './mocked_responses'; + +describe('Deprecations table', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + afterAll(() => { + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + }); + + it('renders deprecations', () => { + const { exists, find } = testBed; + // Verify container exists + expect(exists('esDeprecationsContent')).toBe(true); + + // Verify all deprecations appear in the table + expect(find('deprecationTableRow').length).toEqual( + esDeprecationsMockResponse.deprecations.length + ); + }); + + it('refreshes deprecation data', async () => { + const { actions } = testBed; + const totalRequests = server.requests.length; + + await actions.clickRefreshButton(); + + const mlDeprecation = esDeprecationsMockResponse.deprecations[0]; + const reindexDeprecation = esDeprecationsMockResponse.deprecations[3]; + + // Since upgradeStatusMockResponse includes ML and reindex actions (which require fetching status), there will be 3 requests made + expect(server.requests.length).toBe(totalRequests + 3); + expect(server.requests[server.requests.length - 3].url).toBe( + `${API_BASE_PATH}/es_deprecations` + ); + expect(server.requests[server.requests.length - 2].url).toBe( + `${API_BASE_PATH}/ml_snapshots/${(mlDeprecation.correctiveAction as MlAction).jobId}/${ + (mlDeprecation.correctiveAction as MlAction).snapshotId + }` + ); + expect(server.requests[server.requests.length - 1].url).toBe( + `${API_BASE_PATH}/reindex/${reindexDeprecation.index}` + ); + }); + + describe('search bar', () => { + it('filters results by "critical" status', async () => { + const { find, actions } = testBed; + + await actions.clickCriticalFilterButton(); + + const criticalDeprecations = esDeprecationsMockResponse.deprecations.filter( + (deprecation) => deprecation.isCritical + ); + + expect(find('deprecationTableRow').length).toEqual(criticalDeprecations.length); + + await actions.clickCriticalFilterButton(); + + expect(find('deprecationTableRow').length).toEqual( + esDeprecationsMockResponse.deprecations.length + ); + }); + + it('filters results by type', async () => { + const { component, find, actions } = testBed; + + await actions.clickTypeFilterDropdownAt(0); + + // We need to read the document "body" as the filter dropdown options are added there and not inside + // the component DOM tree. + const clusterTypeFilterButton: HTMLButtonElement | null = document.body.querySelector( + '.euiFilterSelect__items .euiFilterSelectItem' + ); + + expect(clusterTypeFilterButton).not.toBeNull(); + + await act(async () => { + clusterTypeFilterButton!.click(); + }); + + component.update(); + + const clusterDeprecations = esDeprecationsMockResponse.deprecations.filter( + (deprecation) => deprecation.type === 'cluster_settings' + ); + + expect(find('deprecationTableRow').length).toEqual(clusterDeprecations.length); + }); + + it('filters results by query string', async () => { + const { find, actions } = testBed; + const multiFieldsDeprecation = esDeprecationsMockResponse.deprecations[2]; + + await actions.setSearchInputValue(multiFieldsDeprecation.message); + + expect(find('deprecationTableRow').length).toEqual(1); + expect(find('deprecationTableRow').at(0).text()).toContain(multiFieldsDeprecation.message); + }); + + it('shows error for invalid search queries', async () => { + const { find, exists, actions } = testBed; + + await actions.setSearchInputValue('%'); + + expect(exists('invalidSearchQueryMessage')).toBe(true); + expect(find('invalidSearchQueryMessage').text()).toContain('Invalid search'); + }); + + it('shows message when search query does not return results', async () => { + const { find, actions, exists } = testBed; + + await actions.setSearchInputValue('foobarbaz'); + + expect(exists('noDeprecationsRow')).toBe(true); + expect(find('noDeprecationsRow').text()).toContain( + 'No Elasticsearch deprecation issues found' + ); + }); + }); + + describe('pagination', () => { + const esDeprecationsMockResponseWithManyDeprecations = createEsDeprecationsMockResponse(20); + const { deprecations } = esDeprecationsMockResponseWithManyDeprecations; + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse( + esDeprecationsMockResponseWithManyDeprecations + ); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + }); + + it('shows the correct number of pages and deprecations per page', async () => { + const { find, actions } = testBed; + + expect(find('esDeprecationsPagination').find('.euiPagination__item').length).toEqual( + Math.round(deprecations.length / 50) // Default rows per page is 50 + ); + expect(find('deprecationTableRow').length).toEqual(50); + + // Navigate to the next page + await actions.clickPaginationAt(1); + + // On the second (last) page, we expect to see the remaining deprecations + expect(find('deprecationTableRow').length).toEqual(deprecations.length - 50); + }); + + it('allows the number of viewable rows to change', async () => { + const { find, actions, component } = testBed; + + await actions.clickRowsPerPageDropdown(); + + // We need to read the document "body" as the rows-per-page dropdown options are added there and not inside + // the component DOM tree. + const rowsPerPageButton: HTMLButtonElement | null = document.body.querySelector( + '[data-test-subj="tablePagination-100-rows"]' + ); + + expect(rowsPerPageButton).not.toBeNull(); + + await act(async () => { + rowsPerPageButton!.click(); + }); + + component.update(); + + expect(find('esDeprecationsPagination').find('.euiPagination__item').length).toEqual( + Math.round(deprecations.length / 100) // Rows per page is now 100 + ); + expect(find('deprecationTableRow').length).toEqual(deprecations.length); + }); + + it('updates pagination when filters change', async () => { + const { actions, find } = testBed; + + const criticalDeprecations = deprecations.filter((deprecation) => deprecation.isCritical); + + await actions.clickCriticalFilterButton(); + + // Only 40 critical deprecations, so only one page should show + expect(find('esDeprecationsPagination').find('.euiPagination__item').length).toEqual(1); + expect(find('deprecationTableRow').length).toEqual(criticalDeprecations.length); + }); + + it('updates pagination on search', async () => { + const { actions, find } = testBed; + const reindexDeprecations = deprecations.filter( + (deprecation) => deprecation.correctiveAction?.type === 'reindex' + ); + + await actions.setSearchInputValue('Index created before 7.0'); + + // Only 20 deprecations that match, so only one page should show + expect(find('esDeprecationsPagination').find('.euiPagination__item').length).toEqual(1); + expect(find('deprecationTableRow').length).toEqual(reindexDeprecations.length); + }); + }); + + describe('no deprecations', () => { + beforeEach(async () => { + const noDeprecationsResponse = { + totalCriticalDeprecations: 0, + deprecations: [], + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(noDeprecationsResponse); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + }); + + test('renders prompt', () => { + const { exists, find } = testBed; + expect(exists('noDeprecationsPrompt')).toBe(true); + expect(find('noDeprecationsPrompt').text()).toContain( + 'Your Elasticsearch configuration is up to date' + ); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts new file mode 100644 index 0000000000000..8d3616a1b9d6b --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 'react-dom/test-utils'; + +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; + +describe('Error handling', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + afterAll(() => { + server.restore(); + }); + + it('handles 403', async () => { + const error = { + statusCode: 403, + error: 'Forbidden', + message: 'Forbidden', + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('permissionsError')).toBe(true); + expect(find('permissionsError').text()).toContain( + 'You are not authorized to view Elasticsearch deprecations.' + ); + }); + + it('shows upgraded message when all nodes have been upgraded', async () => { + const error = { + statusCode: 426, + error: 'Upgrade required', + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + // This is marked true in the scenario where none of the nodes have the same major version of Kibana, + // and therefore we assume all have been upgraded + allNodesUpgraded: true, + }, + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('upgradedCallout')).toBe(true); + expect(find('upgradedCallout').text()).toContain('All Elasticsearch nodes have been upgraded.'); + }); + + it('shows partially upgrade error when nodes are running different versions', async () => { + const error = { + statusCode: 426, + error: 'Upgrade required', + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: false, + }, + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('partiallyUpgradedWarning')).toBe(true); + expect(find('partiallyUpgradedWarning').text()).toContain( + 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' + ); + }); + + it('handles generic error', async () => { + const error = { + statusCode: 500, + error: 'Internal server error', + message: 'Internal server error', + }; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { component, exists, find } = testBed; + + component.update(); + + expect(exists('requestError')).toBe(true); + expect(find('requestError').text()).toContain('Could not retrieve Elasticsearch deprecations.'); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts new file mode 100644 index 0000000000000..efeb78a507160 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.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. + */ + +import { act } from 'react-dom/test-utils'; + +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; + +import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; + +describe('Index settings deprecation flyout', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + const indexSettingDeprecation = esDeprecationsMockResponse.deprecations[1]; + + afterAll(() => { + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { find, exists, actions, component } = testBed; + + component.update(); + + await actions.clickIndexSettingsDeprecationAt(0); + + expect(exists('indexSettingsDetails')).toBe(true); + expect(find('indexSettingsDetails.flyoutTitle').text()).toContain( + indexSettingDeprecation.message + ); + expect(exists('removeSettingsPrompt')).toBe(true); + }); + + it('removes deprecated index settings', async () => { + const { find, actions } = testBed; + + httpRequestsMockHelpers.setUpdateIndexSettingsResponse({ + acknowledged: true, + }); + + await actions.clickDeleteSettingsButton(); + + const request = server.requests[server.requests.length - 1]; + + expect(request.method).toBe('POST'); + expect(request.url).toBe( + `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings` + ); + expect(request.status).toEqual(200); + + // Verify the "Resolution" column of the table is updated + expect(find('indexSettingsResolutionStatusCell').at(0).text()).toEqual( + 'Deprecated settings removed' + ); + + // Reopen the flyout + await actions.clickIndexSettingsDeprecationAt(0); + + // Verify prompt to remove setting no longer displays + expect(find('removeSettingsPrompt').length).toEqual(0); + // Verify the action button no longer displays + expect(find('indexSettingsDetails.deleteSettingsButton').length).toEqual(0); + }); + + it('handles failure', async () => { + const { find, actions } = testBed; + const error = { + statusCode: 500, + error: 'Remove index settings error', + message: 'Remove index settings error', + }; + + httpRequestsMockHelpers.setUpdateIndexSettingsResponse(undefined, error); + + await actions.clickDeleteSettingsButton(); + + const request = server.requests[server.requests.length - 1]; + + expect(request.method).toBe('POST'); + expect(request.url).toBe( + `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings` + ); + expect(request.status).toEqual(500); + + // Verify the "Resolution" column of the table is updated + expect(find('indexSettingsResolutionStatusCell').at(0).text()).toEqual( + 'Settings removal failed' + ); + + // Reopen the flyout + await actions.clickIndexSettingsDeprecationAt(0); + + // Verify the flyout shows an error message + expect(find('indexSettingsDetails.deleteSettingsError').text()).toContain( + 'Error deleting index settings' + ); + // Verify the remove settings button text changes + expect(find('indexSettingsDetails.deleteSettingsButton').text()).toEqual( + 'Retry removing deprecated settings' + ); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts new file mode 100644 index 0000000000000..909976355cd31 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 'react-dom/test-utils'; + +import type { MlAction } from '../../../common/types'; +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; +import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; + +describe('Machine learning deprecation flyout', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + const mlDeprecation = esDeprecationsMockResponse.deprecations[0]; + + afterAll(() => { + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + const { find, exists, actions, component } = testBed; + + component.update(); + + await actions.clickMlDeprecationAt(0); + + expect(exists('mlSnapshotDetails')).toBe(true); + expect(find('mlSnapshotDetails.flyoutTitle').text()).toContain( + 'Upgrade or delete model snapshot' + ); + }); + + describe('upgrade snapshots', () => { + it('successfully upgrades snapshots', async () => { + const { find, actions, exists } = testBed; + + httpRequestsMockHelpers.setUpgradeMlSnapshotResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'in_progress', + }); + + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'complete', + }); + + expect(find('mlSnapshotDetails.upgradeSnapshotButton').text()).toEqual('Upgrade'); + + await actions.clickUpgradeMlSnapshot(); + + // First, we expect a POST request to upgrade the snapshot + const upgradeRequest = server.requests[server.requests.length - 2]; + expect(upgradeRequest.method).toBe('POST'); + expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); + + // Next, we expect a GET request to check the status of the upgrade + const statusRequest = server.requests[server.requests.length - 1]; + expect(statusRequest.method).toBe('GET'); + expect(statusRequest.url).toBe( + `/api/upgrade_assistant/ml_snapshots/${MOCK_JOB_ID}/${MOCK_SNAPSHOT_ID}` + ); + + // Verify the "Resolution" column of the table is updated + expect(find('mlActionResolutionCell').text()).toContain('Upgrade complete'); + + // Reopen the flyout + await actions.clickMlDeprecationAt(0); + + // Flyout actions should not be visible if deprecation was resolved + expect(exists('mlSnapshotDetails.upgradeSnapshotButton')).toBe(false); + expect(exists('mlSnapshotDetails.deleteSnapshotButton')).toBe(false); + }); + + it('handles upgrade failure', async () => { + const { find, actions } = testBed; + + const error = { + statusCode: 500, + error: 'Upgrade snapshot error', + message: 'Upgrade snapshot error', + }; + + httpRequestsMockHelpers.setUpgradeMlSnapshotResponse(undefined, error); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'error', + error, + }); + + await actions.clickUpgradeMlSnapshot(); + + const upgradeRequest = server.requests[server.requests.length - 1]; + expect(upgradeRequest.method).toBe('POST'); + expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); + + // Verify the "Resolution" column of the table is updated + expect(find('mlActionResolutionCell').text()).toContain('Upgrade failed'); + + // Reopen the flyout + await actions.clickMlDeprecationAt(0); + + // Verify the flyout shows an error message + expect(find('mlSnapshotDetails.resolveSnapshotError').text()).toContain( + 'Error upgrading snapshot' + ); + // Verify the upgrade button text changes + expect(find('mlSnapshotDetails.upgradeSnapshotButton').text()).toEqual('Retry upgrade'); + }); + }); + + describe('delete snapshots', () => { + it('successfully deletes snapshots', async () => { + const { find, actions } = testBed; + + httpRequestsMockHelpers.setDeleteMlSnapshotResponse({ + acknowledged: true, + }); + + expect(find('mlSnapshotDetails.deleteSnapshotButton').text()).toEqual('Delete'); + + await actions.clickDeleteMlSnapshot(); + + const request = server.requests[server.requests.length - 1]; + + expect(request.method).toBe('DELETE'); + expect(request.url).toBe( + `/api/upgrade_assistant/ml_snapshots/${ + (mlDeprecation.correctiveAction! as MlAction).jobId + }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` + ); + + // Verify the "Resolution" column of the table is updated + expect(find('mlActionResolutionCell').at(0).text()).toEqual('Deletion complete'); + + // Reopen the flyout + await actions.clickMlDeprecationAt(0); + }); + + it('handles delete failure', async () => { + const { find, actions } = testBed; + + const error = { + statusCode: 500, + error: 'Upgrade snapshot error', + message: 'Upgrade snapshot error', + }; + + httpRequestsMockHelpers.setDeleteMlSnapshotResponse(undefined, error); + + await actions.clickDeleteMlSnapshot(); + + const request = server.requests[server.requests.length - 1]; + + expect(request.method).toBe('DELETE'); + expect(request.url).toBe( + `/api/upgrade_assistant/ml_snapshots/${ + (mlDeprecation.correctiveAction! as MlAction).jobId + }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` + ); + + // Verify the "Resolution" column of the table is updated + expect(find('mlActionResolutionCell').at(0).text()).toEqual('Deletion failed'); + + // Reopen the flyout + await actions.clickMlDeprecationAt(0); + + // Verify the flyout shows an error message + expect(find('mlSnapshotDetails.resolveSnapshotError').text()).toContain( + 'Error deleting snapshot' + ); + // Verify the upgrade button text changes + expect(find('mlSnapshotDetails.deleteSnapshotButton').text()).toEqual('Retry delete'); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts new file mode 100644 index 0000000000000..ddf477195063c --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ESUpgradeStatus, EnrichedDeprecationInfo } from '../../../common/types'; +import { indexSettingDeprecations } from '../../../common/constants'; + +export const MOCK_SNAPSHOT_ID = '1'; +export const MOCK_JOB_ID = 'deprecation_check_job'; + +export const MOCK_ML_DEPRECATION: EnrichedDeprecationInfo = { + isCritical: true, + resolveDuringUpgrade: false, + type: 'ml_settings', + message: 'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded', + details: + 'model snapshot [%s] for job [%s] supports minimum version [%s] and needs to be at least [%s]', + url: 'doc_url', + correctiveAction: { + type: 'mlSnapshot', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + }, +}; + +const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = { + isCritical: true, + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'Index created before 7.0', + details: 'deprecation details', + url: 'doc_url', + index: 'reindex_index', + correctiveAction: { + type: 'reindex', + }, +}; + +const MOCK_INDEX_SETTING_DEPRECATION: EnrichedDeprecationInfo = { + isCritical: false, + resolveDuringUpgrade: false, + type: 'index_settings', + message: indexSettingDeprecations.translog.deprecationMessage, + details: 'deprecation details', + url: 'doc_url', + index: 'my_index', + correctiveAction: { + type: 'indexSetting', + deprecatedSettings: indexSettingDeprecations.translog.settings, + }, +}; + +const MOCK_DEFAULT_DEPRECATION: EnrichedDeprecationInfo = { + isCritical: false, + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'multi-fields within multi-fields', + details: 'deprecation details', + url: 'doc_url', + index: 'nested_multi-fields', +}; + +export const esDeprecationsMockResponse: ESUpgradeStatus = { + totalCriticalDeprecations: 2, + deprecations: [ + MOCK_ML_DEPRECATION, + MOCK_INDEX_SETTING_DEPRECATION, + MOCK_DEFAULT_DEPRECATION, + MOCK_REINDEX_DEPRECATION, + ], +}; + +// Useful for testing pagination where a large number of deprecations are needed +export const createEsDeprecationsMockResponse = ( + numDeprecationsPerType: number +): ESUpgradeStatus => { + const mlDeprecations: EnrichedDeprecationInfo[] = Array.from( + { + length: numDeprecationsPerType, + }, + () => MOCK_ML_DEPRECATION + ); + + const indexSettingsDeprecations: EnrichedDeprecationInfo[] = Array.from( + { + length: numDeprecationsPerType, + }, + () => MOCK_INDEX_SETTING_DEPRECATION + ); + + const reindexDeprecations: EnrichedDeprecationInfo[] = Array.from( + { + length: numDeprecationsPerType, + }, + () => MOCK_REINDEX_DEPRECATION + ); + + const defaultDeprecations: EnrichedDeprecationInfo[] = Array.from( + { + length: numDeprecationsPerType, + }, + () => MOCK_DEFAULT_DEPRECATION + ); + + const deprecations: EnrichedDeprecationInfo[] = [ + ...defaultDeprecations, + ...reindexDeprecations, + ...indexSettingsDeprecations, + ...mlDeprecations, + ]; + + return { + totalCriticalDeprecations: mlDeprecations.length + reindexDeprecations.length, + deprecations, + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts new file mode 100644 index 0000000000000..c93cdcb1f4d97 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { ElasticsearchTestBed, setupElasticsearchPage, setupEnvironment } from '../helpers'; + +import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; + +// Note: The reindexing flyout UX is subject to change; more tests should be added here once functionality is built out +describe('Reindex deprecation flyout', () => { + let testBed: ElasticsearchTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + afterAll(() => { + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ + nodeId: 'my_node', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + status: 'idle', + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + }); + + it('renders a flyout with reindexing details', async () => { + const reindexDeprecation = esDeprecationsMockResponse.deprecations[3]; + const { actions, find, exists } = testBed; + + await actions.clickReindexDeprecationAt(0); + + expect(exists('reindexDetails')).toBe(true); + expect(find('reindexDetails.flyoutTitle').text()).toContain( + `Reindex ${reindexDeprecation.index}` + ); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/cluster.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/cluster.helpers.ts deleted file mode 100644 index 2aedface1e32b..0000000000000 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/cluster.helpers.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; -import { EsDeprecationsContent } from '../../../public/application/components/es_deprecations'; -import { WithAppDependencies } from './setup_environment'; - -const testBedConfig: TestBedConfig = { - memoryRouter: { - initialEntries: ['/es_deprecations/cluster'], - componentRoutePath: '/es_deprecations/:tabName', - }, - doMountAsync: true, -}; - -export type ClusterTestBed = TestBed & { - actions: ReturnType; -}; - -const createActions = (testBed: TestBed) => { - /** - * User Actions - */ - const clickTab = (tabName: string) => { - const { find } = testBed; - const camelcaseTabName = tabName.charAt(0).toUpperCase() + tabName.slice(1); - - find(`upgradeAssistant${camelcaseTabName}Tab`).simulate('click'); - }; - - const clickExpandAll = () => { - const { find } = testBed; - find('expandAll').simulate('click'); - }; - - return { - clickTab, - clickExpandAll, - }; -}; - -export const setup = async (overrides?: Record): Promise => { - const initTestBed = registerTestBed( - WithAppDependencies(EsDeprecationsContent, overrides), - testBedConfig - ); - const testBed = await initTestBed(); - - return { - ...testBed, - actions: createActions(testBed), - }; -}; - -export type ClusterTestSubjects = - | 'expandAll' - | 'deprecationsContainer' - | 'permissionsError' - | 'requestError' - | 'upgradedCallout' - | 'partiallyUpgradedWarning' - | 'noDeprecationsPrompt' - | string; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/elasticsearch.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/elasticsearch.helpers.ts new file mode 100644 index 0000000000000..86737d4925927 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/elasticsearch.helpers.ts @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act } from 'react-dom/test-utils'; + +import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; +import { EsDeprecations } from '../../../public/application/components/es_deprecations'; +import { WithAppDependencies } from './setup_environment'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + initialEntries: ['/es_deprecations'], + componentRoutePath: '/es_deprecations', + }, + doMountAsync: true, +}; + +export type ElasticsearchTestBed = TestBed & { + actions: ReturnType; +}; + +const createActions = (testBed: TestBed) => { + const { component, find } = testBed; + + /** + * User Actions + */ + const clickRefreshButton = async () => { + await act(async () => { + find('refreshButton').simulate('click'); + }); + + component.update(); + }; + + const clickMlDeprecationAt = async (index: number) => { + await act(async () => { + find('deprecation-mlSnapshot').at(index).simulate('click'); + }); + + component.update(); + }; + + const clickUpgradeMlSnapshot = async () => { + await act(async () => { + find('mlSnapshotDetails.upgradeSnapshotButton').simulate('click'); + }); + + component.update(); + }; + + const clickDeleteMlSnapshot = async () => { + await act(async () => { + find('mlSnapshotDetails.deleteSnapshotButton').simulate('click'); + }); + + component.update(); + }; + + const clickIndexSettingsDeprecationAt = async (index: number) => { + await act(async () => { + find('deprecation-indexSetting').at(index).simulate('click'); + }); + + component.update(); + }; + + const clickDeleteSettingsButton = async () => { + await act(async () => { + find('deleteSettingsButton').simulate('click'); + }); + + component.update(); + }; + + const clickReindexDeprecationAt = async (index: number) => { + await act(async () => { + find('deprecation-reindex').at(index).simulate('click'); + }); + + component.update(); + }; + + const clickDefaultDeprecationAt = async (index: number) => { + await act(async () => { + find('deprecation-default').at(index).simulate('click'); + }); + + component.update(); + }; + + const clickCriticalFilterButton = async () => { + await act(async () => { + // EUI doesn't support data-test-subj's on the filter buttons, so we must access via CSS selector + find('searchBarContainer').find('.euiFilterButton').at(0).simulate('click'); + }); + + component.update(); + }; + + const clickTypeFilterDropdownAt = async (index: number) => { + await act(async () => { + // EUI doesn't support data-test-subj's on the filter buttons, so we must access via CSS selector + find('searchBarContainer') + .find('.euiPopover') + .find('.euiFilterButton') + .at(index) + .simulate('click'); + }); + + component.update(); + }; + + const setSearchInputValue = async (searchValue: string) => { + await act(async () => { + find('searchBarContainer') + .find('input') + .simulate('keyup', { target: { value: searchValue } }); + }); + + component.update(); + }; + + const clickPaginationAt = async (index: number) => { + await act(async () => { + find(`pagination-button-${index}`).simulate('click'); + }); + + component.update(); + }; + + const clickRowsPerPageDropdown = async () => { + await act(async () => { + find('tablePaginationPopoverButton').simulate('click'); + }); + + component.update(); + }; + + return { + clickRefreshButton, + clickMlDeprecationAt, + clickUpgradeMlSnapshot, + clickDeleteMlSnapshot, + clickIndexSettingsDeprecationAt, + clickDeleteSettingsButton, + clickReindexDeprecationAt, + clickDefaultDeprecationAt, + clickCriticalFilterButton, + clickTypeFilterDropdownAt, + setSearchInputValue, + clickPaginationAt, + clickRowsPerPageDropdown, + }; +}; + +export const setup = async (overrides?: Record): Promise => { + const initTestBed = registerTestBed( + WithAppDependencies(EsDeprecations, overrides), + testBedConfig + ); + const testBed = await initTestBed(); + + return { + ...testBed, + actions: createActions(testBed), + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts index 74fcf14fdf597..d0c93d74f31f4 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts @@ -51,11 +51,13 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; - const setUpdateIndexSettingsResponse = (response?: object) => { + const setUpdateIndexSettingsResponse = (response?: object, error?: ResponseError) => { + const status = error ? error.statusCode || 400 : 200; + const body = error ? error : response; server.respondWith('POST', `${API_BASE_PATH}/:indexName/index_settings`, [ - 200, + status, { 'Content-Type': 'application/json' }, - JSON.stringify(response), + JSON.stringify(body), ]); }; @@ -70,6 +72,17 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; + const setUpgradeMlSnapshotStatusResponse = (response?: object, error?: ResponseError) => { + const status = error ? error.statusCode || 400 : 200; + const body = error ? error : response; + + server.respondWith('GET', `${API_BASE_PATH}/ml_snapshots/:jobId/:snapshotId`, [ + status, + { 'Content-Type': 'application/json' }, + JSON.stringify(body), + ]); + }; + const setDeleteMlSnapshotResponse = (response?: object, error?: ResponseError) => { const status = error ? error.statusCode || 400 : 200; const body = error ? error : response; @@ -88,6 +101,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { setUpdateIndexSettingsResponse, setUpgradeMlSnapshotResponse, setDeleteMlSnapshotResponse, + setUpgradeMlSnapshotStatusResponse, }; }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts index 8e256680253be..b19c8b3d0f082 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts @@ -6,8 +6,7 @@ */ export { setup as setupOverviewPage, OverviewTestBed } from './overview.helpers'; -export { setup as setupIndicesPage, IndicesTestBed } from './indices.helpers'; -export { setup as setupClusterPage, ClusterTestBed } from './cluster.helpers'; +export { setup as setupElasticsearchPage, ElasticsearchTestBed } from './elasticsearch.helpers'; export { setup as setupKibanaPage, KibanaTestBed } from './kibana.helpers'; export { setupEnvironment } from './setup_environment'; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/indices.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/indices.helpers.ts deleted file mode 100644 index 5189ddc420b08..0000000000000 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/indices.helpers.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; -import { EsDeprecationsContent } from '../../../public/application/components/es_deprecations'; -import { WithAppDependencies } from './setup_environment'; - -const testBedConfig: TestBedConfig = { - memoryRouter: { - initialEntries: ['/es_deprecations/indices'], - componentRoutePath: '/es_deprecations/:tabName', - }, - doMountAsync: true, -}; - -export type IndicesTestBed = TestBed & { - actions: ReturnType; -}; - -const createActions = (testBed: TestBed) => { - /** - * User Actions - */ - const clickTab = (tabName: string) => { - const { find } = testBed; - const camelcaseTabName = tabName.charAt(0).toUpperCase() + tabName.slice(1); - - find(`upgradeAssistant${camelcaseTabName}Tab`).simulate('click'); - }; - - const clickFixButton = () => { - const { find } = testBed; - find('removeIndexSettingsButton').simulate('click'); - }; - - const clickExpandAll = () => { - const { find } = testBed; - find('expandAll').simulate('click'); - }; - - return { - clickTab, - clickFixButton, - clickExpandAll, - }; -}; - -export const setup = async (overrides?: Record): Promise => { - const initTestBed = registerTestBed( - WithAppDependencies(EsDeprecationsContent, overrides), - testBedConfig - ); - const testBed = await initTestBed(); - - return { - ...testBed, - actions: createActions(testBed), - }; -}; - -export type IndicesTestSubjects = - | 'expandAll' - | 'removeIndexSettingsButton' - | 'deprecationsContainer' - | 'permissionsError' - | 'requestError' - | 'indexCount' - | 'upgradedCallout' - | 'partiallyUpgradedWarning' - | 'noDeprecationsPrompt' - | string; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx index 53b4b5d75931b..c5de02bebd512 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx @@ -23,9 +23,12 @@ import { mockKibanaSemverVersion } from '../../../common/constants'; import { AppContextProvider } from '../../../public/application/app_context'; import { apiService } from '../../../public/application/lib/api'; import { breadcrumbService } from '../../../public/application/lib/breadcrumbs'; +import { GlobalFlyout } from '../../../public/shared_imports'; import { servicesMock } from './services_mock'; import { init as initHttpRequests } from './http_requests'; +const { GlobalFlyoutProvider } = GlobalFlyout; + const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); export const WithAppDependencies = (Comp: any, overrides: Record = {}) => ( @@ -55,7 +58,9 @@ export const WithAppDependencies = (Comp: any, overrides: Record - + + + ); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts deleted file mode 100644 index 89f648c98437e..0000000000000 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/indices.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 'react-dom/test-utils'; -import { indexSettingDeprecations } from '../../common/constants'; -import { ESUpgradeStatus } from '../../common/types'; - -import { IndicesTestBed, setupIndicesPage, setupEnvironment } from './helpers'; - -describe('Indices tab', () => { - let testBed: IndicesTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); - - describe('with deprecations', () => { - const esDeprecationsMockResponse: ESUpgradeStatus = { - totalCriticalDeprecations: 0, - cluster: [], - indices: [ - { - level: 'warning', - message: indexSettingDeprecations.translog.deprecationMessage, - url: 'doc_url', - index: 'my_index', - correctiveAction: { - type: 'indexSetting', - deprecatedSettings: indexSettingDeprecations.translog.settings, - }, - }, - ], - }; - - beforeEach(async () => { - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ - isDeprecationLogIndexingEnabled: true, - isDeprecationLoggingEnabled: true, - }); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { actions, component } = testBed; - - component.update(); - - // Navigate to the indices tab - await act(async () => { - actions.clickTab('indices'); - }); - - component.update(); - }); - - test('renders deprecations', () => { - const { exists, find } = testBed; - expect(exists('indexTabContent')).toBe(true); - expect(exists('deprecationsContainer')).toBe(true); - expect(find('indexCount').text()).toEqual('1'); - }); - - describe('fix indices button', () => { - test('removes deprecated index settings', async () => { - const { component, actions, exists, find } = testBed; - - expect(exists('deprecationsContainer')).toBe(true); - - // Open all deprecations - actions.clickExpandAll(); - - const accordionTestSubj = `depgroup_${indexSettingDeprecations.translog.deprecationMessage - .split(' ') - .join('_')}`; - - await act(async () => { - find(`${accordionTestSubj}.removeIndexSettingsButton`).simulate('click'); - }); - - // We need to read the document "body" as the modal is added there and not inside - // the component DOM tree. - const modal = document.body.querySelector( - '[data-test-subj="indexSettingsDeleteConfirmModal"]' - ); - const confirmButton: HTMLButtonElement | null = modal!.querySelector( - '[data-test-subj="confirmModalConfirmButton"]' - ); - - expect(modal).not.toBe(null); - expect(modal!.textContent).toContain('Remove deprecated settings'); - - const indexName = esDeprecationsMockResponse.indices[0].index; - - httpRequestsMockHelpers.setUpdateIndexSettingsResponse({ - acknowledged: true, - }); - - await act(async () => { - confirmButton!.click(); - }); - - component.update(); - - const request = server.requests[server.requests.length - 1]; - - expect(request.method).toBe('POST'); - expect(request.url).toBe(`/api/upgrade_assistant/${indexName}/index_settings`); - expect(request.status).toEqual(200); - }); - }); - }); - - describe('no deprecations', () => { - beforeEach(async () => { - const noDeprecationsResponse = { - totalCriticalDeprecations: 0, - cluster: [], - indices: [], - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(noDeprecationsResponse); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { component } = testBed; - - component.update(); - }); - - test('renders prompt', () => { - const { exists, find } = testBed; - expect(exists('noDeprecationsPrompt')).toBe(true); - expect(find('noDeprecationsPrompt').text()).toContain('Ready to upgrade!'); - }); - }); - - describe('error handling', () => { - test('handles 403', async () => { - const error = { - statusCode: 403, - error: 'Forbidden', - message: 'Forbidden', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('permissionsError')).toBe(true); - expect(find('permissionsError').text()).toContain( - 'You are not authorized to view Elasticsearch deprecations.' - ); - }); - - test('handles upgrade error', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: true, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('upgradedCallout')).toBe(true); - expect(find('upgradedCallout').text()).toContain( - 'Your configuration is up to date. Kibana and all Elasticsearch nodes are running the same version.' - ); - }); - - test('handles partially upgrade error', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: false, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('partiallyUpgradedWarning')).toBe(true); - expect(find('partiallyUpgradedWarning').text()).toContain( - 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' - ); - }); - - test('handles generic error', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await act(async () => { - testBed = await setupIndicesPage({ isReadOnlyMode: false }); - }); - - const { component, exists, find } = testBed; - - component.update(); - - expect(exists('requestError')).toBe(true); - expect(find('requestError').text()).toContain( - 'Could not retrieve Elasticsearch deprecations.' - ); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana.test.ts index b14ec26e5c8af..5de290e325fe4 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana.test.ts @@ -78,7 +78,7 @@ describe('Kibana deprecations', () => { // the component DOM tree. let modal = document.body.querySelector('[data-test-subj="stepsModal"]'); - expect(modal).not.toBe(null); + expect(modal).not.toBeNull(); expect(modal!.textContent).toContain(`Resolve deprecation in '${deprecation.domainId}'`); const steps: NodeListOf | null = modal!.querySelectorAll( @@ -160,7 +160,9 @@ describe('Kibana deprecations', () => { test('renders prompt', () => { const { exists, find } = testBed; expect(exists('noDeprecationsPrompt')).toBe(true); - expect(find('noDeprecationsPrompt').text()).toContain('Ready to upgrade!'); + expect(find('noDeprecationsPrompt').text()).toContain( + 'Your Kibana configuration is up to date' + ); }); }); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts index ba8f9f8b67d0c..0bf9f9932b8a1 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/mocked_responses.ts @@ -10,19 +10,21 @@ import { ESUpgradeStatus } from '../../../../common/types'; export const esDeprecations: ESUpgradeStatus = { totalCriticalDeprecations: 1, - cluster: [ + deprecations: [ { - level: 'critical', + isCritical: true, + type: 'cluster_settings', + resolveDuringUpgrade: false, message: 'Index Lifecycle Management poll interval is set too low', url: 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#ilm-poll-interval-limit', details: 'The Index Lifecycle Management poll interval setting [indices.lifecycle.poll_interval] is currently set to [500ms], but must be 1s or greater', }, - ], - indices: [ { - level: 'warning', + isCritical: false, + type: 'index_settings', + resolveDuringUpgrade: false, message: 'translog retention settings are ignored', url: 'https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html', @@ -35,8 +37,7 @@ export const esDeprecations: ESUpgradeStatus = { export const esDeprecationsEmpty: ESUpgradeStatus = { totalCriticalDeprecations: 0, - cluster: [], - indices: [], + deprecations: [], }; export const kibanaDeprecations: DomainDeprecationDetails[] = [ diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx index 254242ab338a0..2afffe989ed1b 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/review_logs_step/review_logs_step.test.tsx @@ -84,7 +84,7 @@ describe('Overview - Fix deprecated settings step', () => { component.update(); expect(exists('esStatsPanel')).toBe(true); - expect(find('esStatsPanel').find('a').props().href).toBe('/es_deprecations/cluster'); + expect(find('esStatsPanel').find('a').props().href).toBe('/es_deprecations'); }); describe('Renders ES errors', () => { diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 35c514a0a95bb..a390dd26a0747 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { + MigrationDeprecationInfoDeprecation, + MigrationDeprecationInfoResponse, +} from '@elastic/elasticsearch/api/types'; import { SavedObject, SavedObjectAttributes } from 'src/core/public'; export enum ReindexStep { @@ -116,13 +120,12 @@ export enum IndexGroup { // Telemetry types export const UPGRADE_ASSISTANT_TYPE = 'upgrade-assistant-telemetry'; export const UPGRADE_ASSISTANT_DOC_ID = 'upgrade-assistant-telemetry'; -export type UIOpenOption = 'overview' | 'cluster' | 'indices' | 'kibana'; +export type UIOpenOption = 'overview' | 'elasticsearch' | 'kibana'; export type UIReindexOption = 'close' | 'open' | 'start' | 'stop'; export interface UIOpen { overview: boolean; - cluster: boolean; - indices: boolean; + elasticsearch: boolean; kibana: boolean; } @@ -136,8 +139,7 @@ export interface UIReindex { export interface UpgradeAssistantTelemetrySavedObject { ui_open: { overview: number; - cluster: number; - indices: number; + elasticsearch: number; kibana: number; }; ui_reindex: { @@ -151,8 +153,7 @@ export interface UpgradeAssistantTelemetrySavedObject { export interface UpgradeAssistantTelemetry { ui_open: { overview: number; - cluster: number; - indices: number; + elasticsearch: number; kibana: number; }; ui_reindex: { @@ -186,13 +187,6 @@ export interface DeprecationInfo { export interface IndexSettingsDeprecationInfo { [indexName: string]: DeprecationInfo[]; } -export interface DeprecationAPIResponse { - cluster_settings: DeprecationInfo[]; - ml_settings: DeprecationInfo[]; - node_settings: DeprecationInfo[]; - index_settings: IndexSettingsDeprecationInfo; -} - export interface ReindexAction { type: 'reindex'; /** @@ -215,15 +209,18 @@ export interface IndexSettingAction { type: 'indexSetting'; deprecatedSettings: string[]; } -export interface EnrichedDeprecationInfo extends DeprecationInfo { +export interface EnrichedDeprecationInfo + extends Omit { + type: keyof MigrationDeprecationInfoResponse; + isCritical: boolean; index?: string; correctiveAction?: ReindexAction | MlAction | IndexSettingAction; + resolveDuringUpgrade: boolean; } export interface ESUpgradeStatus { totalCriticalDeprecations: number; - cluster: EnrichedDeprecationInfo[]; - indices: EnrichedDeprecationInfo[]; + deprecations: EnrichedDeprecationInfo[]; } export interface ResolveIndexResponseFromES { diff --git a/x-pack/plugins/upgrade_assistant/public/application/app.tsx b/x-pack/plugins/upgrade_assistant/public/application/app.tsx index b1571b9e45461..864be6e5d996d 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/app.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/app.tsx @@ -8,17 +8,19 @@ import React from 'react'; import { Router, Switch, Route, Redirect } from 'react-router-dom'; import { I18nStart, ScopedHistory } from 'src/core/public'; - import { ApplicationStart } from 'kibana/public'; +import { GlobalFlyout } from '../shared_imports'; + import { KibanaContextProvider } from '../shared_imports'; import { AppServicesContext } from '../types'; import { AppContextProvider, ContextValue, useAppContext } from './app_context'; import { ComingSoonPrompt } from './components/coming_soon_prompt'; -import { EsDeprecationsContent } from './components/es_deprecations'; +import { EsDeprecations } from './components/es_deprecations'; import { KibanaDeprecationsContent } from './components/kibana_deprecations'; import { Overview } from './components/overview'; import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; +const { GlobalFlyoutProvider } = GlobalFlyout; export interface AppDependencies extends ContextValue { i18n: I18nStart; history: ScopedHistory; @@ -37,7 +39,7 @@ const App: React.FunctionComponent = () => { return ( - + @@ -64,7 +66,9 @@ export const RootComponent = ({ - + + + diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/constants.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/constants.tsx index 7b4bee75bc757..c7f974fab6a89 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/constants.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/constants.tsx @@ -7,6 +7,8 @@ import { IconColor } from '@elastic/eui'; import { invert } from 'lodash'; +import { i18n } from '@kbn/i18n'; + import { DeprecationInfo } from '../../../common/types'; export const LEVEL_MAP: { [level: string]: number } = { @@ -26,3 +28,24 @@ export const COLOR_MAP: { [level: string]: IconColor } = { }; export const DEPRECATIONS_PER_PAGE = 25; + +export const DEPRECATION_TYPE_MAP = { + cluster_settings: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterDeprecationTypeLabel', + { + defaultMessage: 'Cluster', + } + ), + index_settings: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexDeprecationTypeLabel', + { + defaultMessage: 'Index', + } + ), + node_settings: i18n.translate('xpack.upgradeAssistant.esDeprecations.nodeDeprecationTypeLabel', { + defaultMessage: 'Node', + }), + ml_settings: i18n.translate('xpack.upgradeAssistant.esDeprecations.mlDeprecationTypeLabel', { + defaultMessage: 'Machine Learning', + }), +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/checkup_api_response.json b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/checkup_api_response.json deleted file mode 100644 index 531bc229b39ea..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/checkup_api_response.json +++ /dev/null @@ -1,870 +0,0 @@ -{ - "cluster": [ - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 3", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 3 4", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 3 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 1 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - }, - { - "level": "warning", - "message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" - }, - { - "level": "warning", - "message": "one or more templates use deprecated mapping settings 0 1 2 3 4 5", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" - } - ], - "nodes": [], - "indices": [ - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]", - "index": ".monitoring-es-6-2018.11.07" - }, - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: tweet, field: liked]]", - "index": "twitter" - }, - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]", - "index": ".kibana" - }, - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]", - "index": ".watcher-history-6-2018.11.07" - }, - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: snapshot]]", - "index": ".monitoring-kibana-6-2018.11.07" - }, - { - "level": "warning", - "message": "Coercion of boolean fields", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: tweet, field: liked]]", - "index": "twitter2" - }, - { - "index": "twitter", - "level": "critical", - "message": "This index must be reindexed in order to upgrade the Elastic Stack.", - "details": "Reindexing is irreversible, so always back up your index before proceeding.", - "actions": [ - { - "label": "Reindex in Console", - "url": "/app/dev_tools#/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter.json" - } - ], - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html" - }, - { - "index": ".triggered_watches", - "level": "critical", - "message": "This index must be upgraded in order to upgrade the Elastic Stack.", - "details": "Upgrading is irreversible, so always back up your index before proceeding.", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html" - }, - { - "index": ".reindex-status", - "level": "critical", - "message": "This index must be reindexed in order to upgrade the Elastic Stack.", - "details": "Reindexing is irreversible, so always back up your index before proceeding.", - "actions": [ - { - "label": "Reindex in Console", - "url": "/app/dev_tools#/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2F.reindex-status.json" - } - ], - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html" - }, - { - "index": "twitter2", - "level": "critical", - "message": "This index must be reindexed in order to upgrade the Elastic Stack.", - "details": "Reindexing is irreversible, so always back up your index before proceeding.", - "actions": [ - { - "label": "Reindex in Console", - "url": "/app/dev_tools#/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter2.json" - } - ], - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html" - }, - { - "index": ".watches", - "level": "critical", - "message": "This index must be upgraded in order to upgrade the Elastic Stack.", - "details": "Upgrading is irreversible, so always back up your index before proceeding.", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html" - } - ] -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/_index.scss index d64400a8abdcf..4865e977f5261 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/_index.scss +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/_index.scss @@ -1 +1 @@ -@import 'deprecations/index'; +@import 'deprecation_types/index'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_tab_content.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_tab_content.tsx deleted file mode 100644 index 8be407371f038..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_tab_content.tsx +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { find, groupBy } from 'lodash'; -import React, { FunctionComponent, useState, useEffect } from 'react'; -import { i18n } from '@kbn/i18n'; - -import { EuiSpacer, EuiHorizontalRule } from '@elastic/eui'; - -import { EnrichedDeprecationInfo } from '../../../../common/types'; -import { SectionLoading } from '../../../shared_imports'; -import { GroupByOption, LevelFilterOption, UpgradeAssistantTabProps } from '../types'; -import { - NoDeprecationsPrompt, - SearchBar, - DeprecationPagination, - DeprecationListBar, -} from '../shared'; -import { DEPRECATIONS_PER_PAGE } from '../constants'; -import { EsDeprecationErrors } from './es_deprecation_errors'; -import { EsDeprecationAccordion } from './deprecations'; - -const i18nTexts = { - isLoading: i18n.translate('xpack.upgradeAssistant.esDeprecations.loadingText', { - defaultMessage: 'Loading deprecations…', - }), -}; - -export interface CheckupTabProps extends UpgradeAssistantTabProps { - checkupLabel: string; -} - -export const createDependenciesFilter = (level: LevelFilterOption, search: string = '') => { - const conditions: Array<(dep: EnrichedDeprecationInfo) => boolean> = []; - - if (level !== 'all') { - conditions.push((dep: EnrichedDeprecationInfo) => dep.level === level); - } - - if (search.length > 0) { - conditions.push((dep) => { - try { - // 'i' is used for case-insensitive matching - const searchReg = new RegExp(search, 'i'); - return searchReg.test(dep.message); - } catch (e) { - // ignore any regexp errors. - return true; - } - }); - } - - // Return true if every condition function returns true (boolean AND) - return (dep: EnrichedDeprecationInfo) => conditions.map((c) => c(dep)).every((t) => t); -}; - -const filterDeprecations = ( - deprecations: EnrichedDeprecationInfo[] = [], - currentFilter: LevelFilterOption, - search: string -) => deprecations.filter(createDependenciesFilter(currentFilter, search)); - -const groupDeprecations = ( - deprecations: EnrichedDeprecationInfo[], - currentFilter: LevelFilterOption, - search: string, - currentGroupBy: GroupByOption -) => groupBy(filterDeprecations(deprecations, currentFilter, search), currentGroupBy); - -const getPageCount = ( - deprecations: EnrichedDeprecationInfo[], - currentFilter: LevelFilterOption, - search: string, - currentGroupBy: GroupByOption -) => - Math.ceil( - Object.keys(groupDeprecations(deprecations, currentFilter, search, currentGroupBy)).length / - DEPRECATIONS_PER_PAGE - ); - -/** - * Displays a list of deprecations that are filterable and groupable. Can be used for cluster, - * nodes, or indices deprecations. - */ -export const DeprecationTabContent: FunctionComponent = ({ - checkupLabel, - deprecations, - error, - isLoading, - refreshCheckupData, - navigateToOverviewPage, -}) => { - const [currentFilter, setCurrentFilter] = useState('all'); - const [search, setSearch] = useState(''); - const [currentGroupBy, setCurrentGroupBy] = useState(GroupByOption.message); - const [expandState, setExpandState] = useState({ - forceExpand: false, - expandNumber: 0, - }); - const [currentPage, setCurrentPage] = useState(0); - - const getAvailableGroupByOptions = () => { - if (!deprecations) { - return []; - } - - return Object.keys(GroupByOption).filter((opt) => find(deprecations, opt)) as GroupByOption[]; - }; - - const setExpandAll = (expandAll: boolean) => { - setExpandState({ forceExpand: expandAll, expandNumber: expandState.expandNumber + 1 }); - }; - - useEffect(() => { - if (deprecations) { - const pageCount = getPageCount(deprecations, currentFilter, search, currentGroupBy); - - if (currentPage >= pageCount) { - setCurrentPage(0); - } - } - }, [currentPage, deprecations, currentFilter, search, currentGroupBy]); - - if (deprecations && deprecations.length === 0) { - return ( -
- -
- ); - } - - let content: React.ReactNode; - - if (isLoading) { - content = {i18nTexts.isLoading}; - } else if (deprecations?.length) { - const levelGroups = groupBy(deprecations, 'level'); - const levelToDeprecationCountMap = Object.keys(levelGroups).reduce((counts, level) => { - counts[level] = levelGroups[level].length; - return counts; - }, {} as Record); - - const filteredDeprecations = filterDeprecations(deprecations, currentFilter, search); - - const groups = groupDeprecations(deprecations, currentFilter, search, currentGroupBy); - - content = ( -
- - - - - - - <> - {Object.keys(groups) - .sort() - // Apply pagination - .slice(currentPage * DEPRECATIONS_PER_PAGE, (currentPage + 1) * DEPRECATIONS_PER_PAGE) - .map((groupName, index) => [ -
- - -
, - ])} - - {/* Only show pagination if we have more than DEPRECATIONS_PER_PAGE. */} - {Object.keys(groups).length > DEPRECATIONS_PER_PAGE && ( - <> - - - - - )} - -
- ); - } else if (error) { - content = ; - } - - return ( -
- - - {content} -
- ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/_index.scss similarity index 60% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_index.scss rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/_index.scss index 1f4f0352e7939..c3e842941a250 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_index.scss +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/_index.scss @@ -1,2 +1 @@ -@import 'cell'; @import 'reindex/index'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.tsx new file mode 100644 index 0000000000000..439062e027650 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonEmpty, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiText, + EuiTextColor, + EuiLink, +} from '@elastic/eui'; + +import { EnrichedDeprecationInfo } from '../../../../../../common/types'; + +export interface DefaultDeprecationFlyoutProps { + deprecation: EnrichedDeprecationInfo; + closeFlyout: () => void; +} + +const i18nTexts = { + getFlyoutDescription: (indexName: string) => + i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.secondaryDescription', + { + defaultMessage: 'Index: {indexName}', + values: { + indexName, + }, + } + ), + learnMoreLinkLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.learnMoreLinkLabel', + { + defaultMessage: 'Learn more about this deprecation', + } + ), + closeButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.closeButtonLabel', + { + defaultMessage: 'Close', + } + ), +}; + +export const DefaultDeprecationFlyout = ({ + deprecation, + closeFlyout, +}: DefaultDeprecationFlyoutProps) => { + const { message, url, details, index } = deprecation; + + return ( + <> + + +

{message}

+
+ {index && ( + +

+ {i18nTexts.getFlyoutDescription(index)} +

+
+ )} +
+ + +

{details}

+

+ + {i18nTexts.learnMoreLinkLabel} + +

+
+
+ + + + + {i18nTexts.closeButtonLabel} + + + + + + ); +}; diff --git a/x-pack/plugins/dashboard_mode/common/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/index.ts similarity index 84% rename from x-pack/plugins/dashboard_mode/common/index.ts rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/index.ts index e055aa4f577f5..ea537b642d8e4 100644 --- a/x-pack/plugins/dashboard_mode/common/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { UI_SETTINGS } from './constants'; +export { DefaultTableRow } from './table_row'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.tsx new file mode 100644 index 0000000000000..7f4b2e3be3479 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiTableRowCell } from '@elastic/eui'; +import { GlobalFlyout } from '../../../../../shared_imports'; +import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { DeprecationTableColumns } from '../../../types'; +import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; +import { DefaultDeprecationFlyout, DefaultDeprecationFlyoutProps } from './flyout'; + +const { useGlobalFlyout } = GlobalFlyout; + +interface Props { + rowFieldNames: DeprecationTableColumns[]; + deprecation: EnrichedDeprecationInfo; +} + +export const DefaultTableRow: React.FunctionComponent = ({ rowFieldNames, deprecation }) => { + const [showFlyout, setShowFlyout] = useState(false); + + const { + addContent: addContentToGlobalFlyout, + removeContent: removeContentFromGlobalFlyout, + } = useGlobalFlyout(); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + removeContentFromGlobalFlyout('deprecationDetails'); + }, [removeContentFromGlobalFlyout]); + + useEffect(() => { + if (showFlyout) { + addContentToGlobalFlyout({ + id: 'deprecationDetails', + Component: DefaultDeprecationFlyout, + props: { + deprecation, + closeFlyout, + }, + flyoutProps: { + onClose: closeFlyout, + 'data-test-subj': 'defaultDeprecationDetails', + 'aria-labelledby': 'defaultDeprecationDetailsFlyoutTitle', + }, + }); + } + }, [addContentToGlobalFlyout, closeFlyout, deprecation, showFlyout]); + + return ( + <> + {rowFieldNames.map((field) => { + return ( + + setShowFlyout(true)} + deprecation={deprecation} + /> + + ); + })} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx similarity index 55% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx index a4152e52a35b7..eb0221a722a30 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx @@ -5,4 +5,7 @@ * 2.0. */ -export { EsDeprecationAccordion } from './deprecation_group_item'; +export { MlSnapshotsTableRow } from './ml_snapshots'; +export { IndexSettingsTableRow } from './index_settings'; +export { DefaultTableRow } from './default'; +export { ReindexTableRow } from './reindex'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx new file mode 100644 index 0000000000000..1567562db53ee --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButton, + EuiButtonEmpty, + EuiCode, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiText, + EuiTextColor, + EuiLink, + EuiSpacer, + EuiCallOut, +} from '@elastic/eui'; +import { EnrichedDeprecationInfo, IndexSettingAction } from '../../../../../../common/types'; +import type { ResponseError } from '../../../../lib/api'; +import type { Status } from '../../../types'; + +export interface RemoveIndexSettingsFlyoutProps { + deprecation: EnrichedDeprecationInfo; + closeFlyout: () => void; + removeIndexSettings: (index: string, settings: string[]) => Promise; + status: { + statusType: Status; + details?: ResponseError; + }; +} + +const i18nTexts = { + getFlyoutDescription: (indexName: string) => + i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.secondaryDescription', + { + defaultMessage: 'Index: {indexName}', + values: { + indexName, + }, + } + ), + learnMoreLinkLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.learnMoreLinkLabel', + { + defaultMessage: 'Learn more about this deprecation', + } + ), + removeButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.removeButtonLabel', + { + defaultMessage: 'Remove deprecated settings', + } + ), + retryRemoveButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.retryRemoveButtonLabel', + { + defaultMessage: 'Retry removing deprecated settings', + } + ), + resolvedButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.resolvedButtonLabel', + { + defaultMessage: 'Resolved', + } + ), + closeButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.closeButtonLabel', + { + defaultMessage: 'Close', + } + ), + getConfirmationText: (indexSettingsCount: number) => + i18n.translate('xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.description', { + defaultMessage: + 'Remove the following deprecated index {indexSettingsCount, plural, one {setting} other {settings}}?', + values: { + indexSettingsCount, + }, + }), + errorTitle: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.deleteErrorTitle', + { + defaultMessage: 'Error deleting index settings', + } + ), +}; + +export const RemoveIndexSettingsFlyout = ({ + deprecation, + closeFlyout, + removeIndexSettings, + status, +}: RemoveIndexSettingsFlyoutProps) => { + const { index, message, details, url, correctiveAction } = deprecation; + const { statusType, details: statusDetails } = status; + + // Flag used to hide certain parts of the UI if the deprecation has been resolved or is in progress + const isResolvable = ['idle', 'error'].includes(statusType); + + return ( + <> + + +

{message}

+
+ +

+ {i18nTexts.getFlyoutDescription(index!)} +

+
+
+ + {statusType === 'error' && ( + <> + + {statusDetails!.message} + + + + )} + + +

{details}

+

+ + {i18nTexts.learnMoreLinkLabel} + +

+
+ + {isResolvable && ( +
+ + + +

+ {i18nTexts.getConfirmationText( + (correctiveAction as IndexSettingAction).deprecatedSettings.length + )} +

+
+ + + + +
    + {(correctiveAction as IndexSettingAction).deprecatedSettings.map( + (setting, settingIndex) => ( +
  • + {setting} +
  • + ) + )} +
+
+
+ )} +
+ + + + + {i18nTexts.closeButtonLabel} + + + + {isResolvable && ( + + + removeIndexSettings( + index!, + (correctiveAction as IndexSettingAction).deprecatedSettings + ) + } + > + {statusType === 'error' + ? i18nTexts.retryRemoveButtonLabel + : i18nTexts.removeButtonLabel} + + + )} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/index.ts similarity index 82% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/index.ts rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/index.ts index e8a83790ee2a6..282b8308f403f 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { FixIndexSettingsButton } from './button'; +export { IndexSettingsTableRow } from './table_row'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/resolution_table_cell.tsx new file mode 100644 index 0000000000000..a5a586927c811 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/resolution_table_cell.tsx @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiIcon, + EuiLoadingSpinner, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Status } from '../../../types'; + +const i18nTexts = { + deleteInProgressText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexSettings.deletingButtonLabel', + { + defaultMessage: 'Settings removal in progress…', + } + ), + deleteCompleteText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexSettings.deleteCompleteText', + { + defaultMessage: 'Deprecated settings removed', + } + ), + deleteFailedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexSettings.deleteFailedText', + { + defaultMessage: 'Settings removal failed', + } + ), + resolutionText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexSettings.resolutionText', + { + defaultMessage: 'Remove settings', + } + ), + resolutionTooltipLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.indexSettings.resolutionTooltipLabel', + { + defaultMessage: + 'Resolve this deprecation by removing settings from this index. This is an automated resolution.', + } + ), +}; + +interface Props { + status: { + statusType: Status; + }; +} + +export const IndexSettingsResolutionCell: React.FunctionComponent = ({ status }) => { + const { statusType } = status; + if (statusType === 'in_progress') { + return ( + + + + + + {i18nTexts.deleteInProgressText} + + + ); + } + + if (statusType === 'complete') { + return ( + + + + + + {i18nTexts.deleteCompleteText} + + + ); + } + + if (statusType === 'error') { + return ( + + + + + + {i18nTexts.deleteFailedText} + + + ); + } + + return ( + + + + + + + {i18nTexts.resolutionText} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx new file mode 100644 index 0000000000000..3a1706b08c0ee --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiTableRowCell } from '@elastic/eui'; +import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { GlobalFlyout } from '../../../../../shared_imports'; +import { useAppContext } from '../../../../app_context'; +import type { ResponseError } from '../../../../lib/api'; +import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; +import { DeprecationTableColumns, Status } from '../../../types'; +import { IndexSettingsResolutionCell } from './resolution_table_cell'; +import { RemoveIndexSettingsFlyout, RemoveIndexSettingsFlyoutProps } from './flyout'; + +const { useGlobalFlyout } = GlobalFlyout; + +interface Props { + deprecation: EnrichedDeprecationInfo; + rowFieldNames: DeprecationTableColumns[]; +} + +export const IndexSettingsTableRow: React.FunctionComponent = ({ + rowFieldNames, + deprecation, +}) => { + const [showFlyout, setShowFlyout] = useState(false); + const [status, setStatus] = useState<{ + statusType: Status; + details?: ResponseError; + }>({ statusType: 'idle' }); + + const { api } = useAppContext(); + + const { + addContent: addContentToGlobalFlyout, + removeContent: removeContentFromGlobalFlyout, + } = useGlobalFlyout(); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + removeContentFromGlobalFlyout('indexSettingsFlyout'); + }, [removeContentFromGlobalFlyout]); + + const removeIndexSettings = useCallback( + async (index: string, settings: string[]) => { + setStatus({ statusType: 'in_progress' }); + + const { error } = await api.updateIndexSettings(index, settings); + + setStatus({ + statusType: error ? 'error' : 'complete', + details: error ?? undefined, + }); + closeFlyout(); + }, + [api, closeFlyout] + ); + + useEffect(() => { + if (showFlyout) { + addContentToGlobalFlyout({ + id: 'indexSettingsFlyout', + Component: RemoveIndexSettingsFlyout, + props: { + closeFlyout, + deprecation, + removeIndexSettings, + status, + }, + flyoutProps: { + onClose: closeFlyout, + 'data-test-subj': 'indexSettingsDetails', + 'aria-labelledby': 'indexSettingsDetailsFlyoutTitle', + }, + }); + } + }, [addContentToGlobalFlyout, deprecation, removeIndexSettings, showFlyout, closeFlyout, status]); + + return ( + <> + {rowFieldNames.map((field: DeprecationTableColumns) => { + return ( + + setShowFlyout(true)} + deprecation={deprecation} + resolutionTableCell={} + /> + + ); + })} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/context.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/context.tsx new file mode 100644 index 0000000000000..972d640d18c5a --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/context.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, createContext, useContext } from 'react'; +import { ApiService } from '../../../../lib/api'; + +import { useSnapshotState, SnapshotState } from './use_snapshot_state'; + +export interface MlSnapshotContext { + snapshotState: SnapshotState; + upgradeSnapshot: () => Promise; + deleteSnapshot: () => Promise; +} + +const MlSnapshotsContext = createContext(undefined); + +export const useMlSnapshotContext = () => { + const context = useContext(MlSnapshotsContext); + if (context === undefined) { + throw new Error('useMlSnapshotContext must be used within a '); + } + return context; +}; + +interface Props { + api: ApiService; + children: React.ReactNode; + snapshotId: string; + jobId: string; +} + +export const MlSnapshotsStatusProvider: React.FunctionComponent = ({ + api, + snapshotId, + jobId, + children, +}) => { + const { updateSnapshotStatus, snapshotState, upgradeSnapshot, deleteSnapshot } = useSnapshotState( + { + jobId, + snapshotId, + api, + } + ); + + useEffect(() => { + updateSnapshotStatus(); + }, [updateSnapshotStatus]); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/fix_snapshots_flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx similarity index 61% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/fix_snapshots_flyout.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx index 7dafab011a69a..ba72faf2f8c3f 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/fix_snapshots_flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx @@ -13,28 +13,22 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, - EuiFlyout, EuiFlyoutBody, EuiFlyoutFooter, EuiFlyoutHeader, - EuiPortal, EuiTitle, EuiText, EuiCallOut, EuiSpacer, + EuiLink, } from '@elastic/eui'; -import { SnapshotStatus } from './use_snapshot_state'; -import { ResponseError } from '../../../../lib/api'; -interface SnapshotState extends SnapshotStatus { - error?: ResponseError; -} -interface Props { - upgradeSnapshot: () => Promise; - deleteSnapshot: () => Promise; - description: string; +import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { MlSnapshotContext } from './context'; + +export interface FixSnapshotsFlyoutProps extends MlSnapshotContext { + deprecation: EnrichedDeprecationInfo; closeFlyout: () => void; - snapshotState: SnapshotState; } const i18nTexts = { @@ -51,7 +45,7 @@ const i18nTexts = { } ), closeButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.flyout.cancelButtonLabel', + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.flyout.closeButtonLabel', { defaultMessage: 'Close', } @@ -83,15 +77,24 @@ const i18nTexts = { defaultMessage: 'Error upgrading snapshot', } ), + learnMoreLinkLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.learnMoreLinkLabel', + { + defaultMessage: 'Learn more about this deprecation', + } + ), }; export const FixSnapshotsFlyout = ({ - upgradeSnapshot, - deleteSnapshot, - description, + deprecation, closeFlyout, snapshotState, -}: Props) => { + upgradeSnapshot, + deleteSnapshot, +}: FixSnapshotsFlyoutProps) => { + // Flag used to hide certain parts of the UI if the deprecation has been resolved or is in progress + const isResolvable = ['idle', 'error'].includes(snapshotState.status); + const onUpgradeSnapshot = () => { upgradeSnapshot(); closeFlyout(); @@ -103,48 +106,48 @@ export const FixSnapshotsFlyout = ({ }; return ( - - - - -

{i18nTexts.flyoutTitle}

-
-
- - {snapshotState.error && ( - <> - - {snapshotState.error.message} - - - - )} - -

{description}

-
-
- - - - - {i18nTexts.closeButtonLabel} - - + <> + + +

{i18nTexts.flyoutTitle}

+
+
+ + {snapshotState.error && ( + <> + + {snapshotState.error.message} + + + + )} + +

{deprecation.details}

+

+ + {i18nTexts.learnMoreLinkLabel} + +

+
+
+ + + + + {i18nTexts.closeButtonLabel} + + + + {isResolvable && ( @@ -173,9 +176,9 @@ export const FixSnapshotsFlyout = ({ - - -
-
+ )} + + + ); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/index.ts similarity index 83% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/index.ts rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/index.ts index d537c94cf67ae..d523184454533 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { FixMlSnapshotsButton } from './button'; +export { MlSnapshotsTableRow } from './table_row'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/resolution_table_cell.tsx new file mode 100644 index 0000000000000..7963701b5c543 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/resolution_table_cell.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiToolTip, + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiIcon, + EuiLoadingSpinner, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useMlSnapshotContext } from './context'; + +const i18nTexts = { + upgradeInProgressText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeInProgressText', + { + defaultMessage: 'Upgrade in progress…', + } + ), + deleteInProgressText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.deletingButtonLabel', + { + defaultMessage: 'Deletion in progress…', + } + ), + upgradeCompleteText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeCompleteText', + { + defaultMessage: 'Upgrade complete', + } + ), + deleteCompleteText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.deleteCompleteText', + { + defaultMessage: 'Deletion complete', + } + ), + upgradeFailedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeFailedText', + { + defaultMessage: 'Upgrade failed', + } + ), + deleteFailedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.deleteFailedText', + { + defaultMessage: 'Deletion failed', + } + ), + resolutionText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.resolutionText', + { + defaultMessage: 'Upgrade or delete snapshots', + } + ), + resolutionTooltipLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.resolutionTooltipLabel', + { + defaultMessage: + 'Resolve this deprecation by upgrading or deleting a job model snapshot. This is an automated resolution.', + } + ), +}; + +export const MlSnapshotsResolutionCell: React.FunctionComponent = () => { + const { snapshotState } = useMlSnapshotContext(); + + if (snapshotState.status === 'in_progress') { + return ( + + + + + + + {snapshotState.action === 'delete' + ? i18nTexts.deleteInProgressText + : i18nTexts.upgradeInProgressText} + + + + ); + } + + if (snapshotState.status === 'complete') { + return ( + + + + + + + {snapshotState.action === 'delete' + ? i18nTexts.deleteCompleteText + : i18nTexts.upgradeCompleteText} + + + + ); + } + + if (snapshotState.status === 'error') { + return ( + + + + + + + {snapshotState.action === 'delete' + ? i18nTexts.deleteFailedText + : i18nTexts.upgradeFailedText} + + + + ); + } + + return ( + + + + + + + {i18nTexts.resolutionText} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/table_row.tsx new file mode 100644 index 0000000000000..73921b235d88c --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/table_row.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiTableRowCell } from '@elastic/eui'; +import { EnrichedDeprecationInfo, MlAction } from '../../../../../../common/types'; +import { GlobalFlyout } from '../../../../../shared_imports'; +import { useAppContext } from '../../../../app_context'; +import { DeprecationTableColumns } from '../../../types'; +import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; +import { MlSnapshotsResolutionCell } from './resolution_table_cell'; +import { FixSnapshotsFlyout, FixSnapshotsFlyoutProps } from './flyout'; +import { MlSnapshotsStatusProvider, useMlSnapshotContext } from './context'; + +const { useGlobalFlyout } = GlobalFlyout; + +interface TableRowProps { + deprecation: EnrichedDeprecationInfo; + rowFieldNames: DeprecationTableColumns[]; +} + +export const MlSnapshotsTableRowCells: React.FunctionComponent = ({ + rowFieldNames, + deprecation, +}) => { + const [showFlyout, setShowFlyout] = useState(false); + const snapshotState = useMlSnapshotContext(); + + const { + addContent: addContentToGlobalFlyout, + removeContent: removeContentFromGlobalFlyout, + } = useGlobalFlyout(); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + removeContentFromGlobalFlyout('mlFlyout'); + }, [removeContentFromGlobalFlyout]); + + useEffect(() => { + if (showFlyout) { + addContentToGlobalFlyout({ + id: 'mlFlyout', + Component: FixSnapshotsFlyout, + props: { + deprecation, + closeFlyout, + ...snapshotState, + }, + flyoutProps: { + onClose: closeFlyout, + 'data-test-subj': 'mlSnapshotDetails', + 'aria-labelledby': 'mlSnapshotDetailsFlyoutTitle', + }, + }); + } + }, [snapshotState, addContentToGlobalFlyout, showFlyout, deprecation, closeFlyout]); + + return ( + <> + {rowFieldNames.map((field: DeprecationTableColumns) => { + return ( + + setShowFlyout(true)} + deprecation={deprecation} + resolutionTableCell={} + /> + + ); + })} + + ); +}; + +export const MlSnapshotsTableRow: React.FunctionComponent = (props) => { + const { api } = useAppContext(); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/use_snapshot_state.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx similarity index 94% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/use_snapshot_state.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx index 2dd4638c772b3..a724922563e05 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/use_snapshot_state.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx @@ -8,16 +8,21 @@ import { useRef, useCallback, useState, useEffect } from 'react'; import { ApiService, ResponseError } from '../../../../lib/api'; +import { Status } from '../../../types'; const POLL_INTERVAL_MS = 1000; -export interface SnapshotStatus { +interface SnapshotStatus { snapshotId: string; jobId: string; - status: 'complete' | 'in_progress' | 'error' | 'idle'; + status: Status; action?: 'upgrade' | 'delete'; } +export interface SnapshotState extends SnapshotStatus { + error: ResponseError | undefined; +} + export const useSnapshotState = ({ jobId, snapshotId, diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/_index.scss similarity index 57% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_index.scss rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/_index.scss index 014edc96b0565..4cd55614ab4e6 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_index.scss +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/_index.scss @@ -1,2 +1 @@ -@import 'button'; @import 'flyout/index'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/context.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/context.tsx new file mode 100644 index 0000000000000..2d34253d2c426 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/context.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, createContext, useContext } from 'react'; + +import { ApiService } from '../../../../lib/api'; +import { useReindexStatus, ReindexState } from './use_reindex_state'; + +export interface ReindexStateContext { + reindexState: ReindexState; + startReindex: () => Promise; + cancelReindex: () => Promise; +} + +const ReindexContext = createContext(undefined); + +export const useReindexContext = () => { + const context = useContext(ReindexContext); + if (context === undefined) { + throw new Error('useReindexContext must be used within a '); + } + return context; +}; + +interface Props { + api: ApiService; + children: React.ReactNode; + indexName: string; +} + +export const ReindexStatusProvider: React.FunctionComponent = ({ + api, + indexName, + children, +}) => { + const { reindexState, startReindex, cancelReindex, updateStatus } = useReindexStatus({ + indexName, + api, + }); + + useEffect(() => { + updateStatus(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/warning_step.test.tsx.snap similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/__snapshots__/warning_step.test.tsx.snap rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/warning_step.test.tsx.snap diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/_index.scss similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/_index.scss rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/_index.scss diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/_step_progress.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/_step_progress.scss similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/_step_progress.scss rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/_step_progress.scss diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.test.tsx similarity index 97% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.test.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.test.tsx index f8d72addc2d18..a3a0f15188fca 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { ReindexStatus } from '../../../../../../../common/types'; import { LoadingState } from '../../../../types'; -import { ReindexState } from '../polling_service'; +import type { ReindexState } from '../use_reindex_state'; import { ChecklistFlyoutStep } from './checklist_step'; describe('ChecklistFlyout', () => { diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.tsx similarity index 98% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.tsx index e852171a696b4..856e2a57649df 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/checklist_step.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/checklist_step.tsx @@ -22,7 +22,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { ReindexStatus } from '../../../../../../../common/types'; import { LoadingState } from '../../../../types'; -import { ReindexState } from '../polling_service'; +import type { ReindexState } from '../use_reindex_state'; import { ReindexProgress } from './progress'; const buttonLabel = (status?: ReindexStatus) => { @@ -45,7 +45,7 @@ const buttonLabel = (status?: ReindexStatus) => { return ( ); case ReindexStatus.paused: diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/container.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/container.tsx new file mode 100644 index 0000000000000..f10e7b4cc687e --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/container.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { DocLinksStart } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCallOut, EuiFlyoutHeader, EuiLink, EuiSpacer, EuiTitle } from '@elastic/eui'; + +import { + EnrichedDeprecationInfo, + ReindexAction, + ReindexStatus, +} from '../../../../../../../common/types'; +import { useAppContext } from '../../../../../app_context'; + +import type { ReindexStateContext } from '../context'; +import { ChecklistFlyoutStep } from './checklist_step'; +import { WarningsFlyoutStep } from './warnings_step'; + +enum ReindexFlyoutStep { + reindexWarnings, + checklist, +} + +export interface ReindexFlyoutProps extends ReindexStateContext { + deprecation: EnrichedDeprecationInfo; + closeFlyout: () => void; +} + +const getOpenAndCloseIndexDocLink = (docLinks: DocLinksStart) => ( + + {i18n.translate( + 'xpack.upgradeAssistant.checkupTab.reindexing.flyout.openAndCloseDocumentation', + { defaultMessage: 'documentation' } + )} + +); + +const getIndexClosedCallout = (docLinks: DocLinksStart) => ( + <> + +

+ + {i18n.translate( + 'xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails.reindexingTakesLongerEmphasis', + { defaultMessage: 'Reindexing may take longer than usual' } + )} + + ), + }} + /> +

+
+ + +); + +export const ReindexFlyout: React.FunctionComponent = ({ + reindexState, + startReindex, + cancelReindex, + closeFlyout, + deprecation, +}) => { + const { status, reindexWarnings } = reindexState; + const { index, correctiveAction } = deprecation; + const { docLinks } = useAppContext(); + // If there are any warnings and we haven't started reindexing, show the warnings step first. + const [currentFlyoutStep, setCurrentFlyoutStep] = useState( + reindexWarnings && reindexWarnings.length > 0 && status === undefined + ? ReindexFlyoutStep.reindexWarnings + : ReindexFlyoutStep.checklist + ); + + let flyoutContents: React.ReactNode; + + const globalCallout = + (correctiveAction as ReindexAction).blockerForReindexing === 'index-closed' && + reindexState.status !== ReindexStatus.completed + ? getIndexClosedCallout(docLinks) + : undefined; + switch (currentFlyoutStep) { + case ReindexFlyoutStep.reindexWarnings: + flyoutContents = ( + globalCallout} + closeFlyout={closeFlyout} + warnings={reindexState.reindexWarnings!} + advanceNextStep={() => setCurrentFlyoutStep(ReindexFlyoutStep.checklist)} + /> + ); + break; + case ReindexFlyoutStep.checklist: + flyoutContents = ( + globalCallout} + closeFlyout={closeFlyout} + reindexState={reindexState} + startReindex={startReindex} + cancelReindex={cancelReindex} + /> + ); + break; + default: + throw new Error(`Invalid flyout step: ${currentFlyoutStep}`); + } + + return ( + <> + + +

+ +

+
+
+ {flyoutContents} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/index.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/index.tsx similarity index 79% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/index.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/index.tsx index facc830234667..6b9eee80acb57 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/index.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/index.tsx @@ -5,4 +5,4 @@ * 2.0. */ -export { ReindexFlyout } from './container'; +export { ReindexFlyout, ReindexFlyoutProps } from './container'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx similarity index 99% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.test.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx index 24a00af7a9fee..b49d816302213 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx @@ -9,7 +9,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../common/types'; -import { ReindexState } from '../polling_service'; +import type { ReindexState } from '../use_reindex_state'; import { ReindexProgress } from './progress'; describe('ReindexProgress', () => { diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx similarity index 99% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx index 088266f3a4840..65a790fe96691 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/progress.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx @@ -19,7 +19,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../common/types'; import { LoadingState } from '../../../../types'; -import { ReindexState } from '../polling_service'; +import type { ReindexState } from '../use_reindex_state'; import { StepProgress, StepProgressStep } from './step_progress'; const ErrorCallout: React.FunctionComponent<{ errorMessage: string | null }> = ({ diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/step_progress.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/step_progress.tsx similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/step_progress.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/step_progress.tsx diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warning_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warning_step.test.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warning_step_checkbox.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step_checkbox.tsx similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warning_step_checkbox.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step_checkbox.tsx diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warnings_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warnings_step.tsx similarity index 100% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/warnings_step.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warnings_step.tsx diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/index.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/index.tsx similarity index 84% rename from x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/index.tsx rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/index.tsx index 6fbb38b04bbd6..bbb1493f15bcc 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/index.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/index.tsx @@ -5,4 +5,4 @@ * 2.0. */ -export { ReindexButton } from './button'; +export { ReindexTableRow } from './table_row'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx new file mode 100644 index 0000000000000..6ea9a0277059a --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiIcon, + EuiLoadingSpinner, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, +} from '@elastic/eui'; +import { ReindexStatus } from '../../../../../../common/types'; +import { LoadingState } from '../../../types'; +import { useReindexContext } from './context'; + +const i18nTexts = { + reindexLoadingStatusText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexLoadingStatusText', + { + defaultMessage: 'Loading status…', + } + ), + reindexInProgressText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexInProgressText', + { + defaultMessage: 'Reindexing in progress…', + } + ), + reindexCompleteText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexCompleteText', + { + defaultMessage: 'Reindex complete', + } + ), + reindexFailedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexFailedText', + { + defaultMessage: 'Reindex failed', + } + ), + reindexCanceledText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexCanceledText', + { + defaultMessage: 'Reindex canceled', + } + ), + reindexPausedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.reindexPausedText', + { + defaultMessage: 'Reindex paused', + } + ), + resolutionText: i18n.translate('xpack.upgradeAssistant.esDeprecations.reindex.resolutionLabel', { + defaultMessage: 'Reindex', + }), + resolutionTooltipLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.reindex.resolutionTooltipLabel', + { + defaultMessage: + 'Resolve this deprecation by reindexing this index. This is an automated resolution.', + } + ), +}; + +export const ReindexResolutionCell: React.FunctionComponent = () => { + const { reindexState } = useReindexContext(); + + if (reindexState.loadingState === LoadingState.Loading) { + return ( + + + + + + {i18nTexts.reindexLoadingStatusText} + + + ); + } + + switch (reindexState.status) { + case ReindexStatus.inProgress: + return ( + + + + + + {i18nTexts.reindexInProgressText} + + + ); + case ReindexStatus.completed: + return ( + + + + + + {i18nTexts.reindexCompleteText} + + + ); + case ReindexStatus.failed: + return ( + + + + + + {i18nTexts.reindexFailedText} + + + ); + case ReindexStatus.paused: + return ( + + + + + + {i18nTexts.reindexPausedText} + + + ); + case ReindexStatus.cancelled: + return ( + + + + + + {i18nTexts.reindexCanceledText} + + + ); + } + + return ( + + + + + + + {i18nTexts.resolutionText} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx new file mode 100644 index 0000000000000..95d65f1e77771 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiTableRowCell } from '@elastic/eui'; +import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { GlobalFlyout } from '../../../../../shared_imports'; +import { useAppContext } from '../../../../app_context'; +import { DeprecationTableColumns } from '../../../types'; +import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; +import { ReindexResolutionCell } from './resolution_table_cell'; +import { ReindexFlyout, ReindexFlyoutProps } from './flyout'; +import { ReindexStatusProvider, useReindexContext } from './context'; + +const { useGlobalFlyout } = GlobalFlyout; + +interface TableRowProps { + deprecation: EnrichedDeprecationInfo; + rowFieldNames: DeprecationTableColumns[]; +} + +const ReindexTableRowCells: React.FunctionComponent = ({ + rowFieldNames, + deprecation, +}) => { + const [showFlyout, setShowFlyout] = useState(false); + const reindexState = useReindexContext(); + const { api } = useAppContext(); + + const { + addContent: addContentToGlobalFlyout, + removeContent: removeContentFromGlobalFlyout, + } = useGlobalFlyout(); + + const closeFlyout = useCallback(async () => { + removeContentFromGlobalFlyout('reindexFlyout'); + setShowFlyout(false); + await api.sendReindexTelemetryData({ close: true }); + }, [api, removeContentFromGlobalFlyout]); + + useEffect(() => { + if (showFlyout) { + addContentToGlobalFlyout({ + id: 'reindexFlyout', + Component: ReindexFlyout, + props: { + deprecation, + closeFlyout, + ...reindexState, + }, + flyoutProps: { + onClose: closeFlyout, + 'data-test-subj': 'reindexDetails', + 'aria-labelledby': 'reindexDetailsFlyoutTitle', + }, + }); + } + }, [addContentToGlobalFlyout, deprecation, showFlyout, reindexState, closeFlyout]); + + useEffect(() => { + if (showFlyout) { + async function sendTelemetry() { + await api.sendReindexTelemetryData({ open: true }); + } + + sendTelemetry(); + } + }, [showFlyout, api]); + + return ( + <> + {rowFieldNames.map((field: DeprecationTableColumns) => { + return ( + + setShowFlyout(true)} + deprecation={deprecation} + resolutionTableCell={} + /> + + ); + })} + + ); +}; + +export const ReindexTableRow: React.FunctionComponent = (props) => { + const { api } = useAppContext(); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx new file mode 100644 index 0000000000000..b87a509d25a55 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useRef, useCallback, useState, useEffect } from 'react'; + +import { + IndexGroup, + ReindexOperation, + ReindexStatus, + ReindexStep, + ReindexWarning, +} from '../../../../../../common/types'; +import { LoadingState } from '../../../types'; +import { ApiService } from '../../../../lib/api'; + +const POLL_INTERVAL = 1000; + +export interface ReindexState { + loadingState: LoadingState; + cancelLoadingState?: LoadingState; + lastCompletedStep?: ReindexStep; + status?: ReindexStatus; + reindexTaskPercComplete: number | null; + errorMessage: string | null; + reindexWarnings?: ReindexWarning[]; + hasRequiredPrivileges?: boolean; + indexGroup?: IndexGroup; +} + +interface StatusResponse { + warnings?: ReindexWarning[]; + reindexOp?: ReindexOperation; + hasRequiredPrivileges?: boolean; + indexGroup?: IndexGroup; +} + +const getReindexState = ( + reindexState: ReindexState, + { reindexOp, warnings, hasRequiredPrivileges, indexGroup }: StatusResponse +) => { + const newReindexState = { + ...reindexState, + loadingState: LoadingState.Success, + }; + + if (warnings) { + newReindexState.reindexWarnings = warnings; + } + + if (hasRequiredPrivileges !== undefined) { + newReindexState.hasRequiredPrivileges = hasRequiredPrivileges; + } + + if (indexGroup) { + newReindexState.indexGroup = indexGroup; + } + + if (reindexOp) { + // Prevent the UI flickering back to inProgress after cancelling + newReindexState.lastCompletedStep = reindexOp.lastCompletedStep; + newReindexState.status = reindexOp.status; + newReindexState.reindexTaskPercComplete = reindexOp.reindexTaskPercComplete; + newReindexState.errorMessage = reindexOp.errorMessage; + + if (reindexOp.status === ReindexStatus.cancelled) { + newReindexState.cancelLoadingState = LoadingState.Success; + } + } + + return newReindexState; +}; + +export const useReindexStatus = ({ indexName, api }: { indexName: string; api: ApiService }) => { + const [reindexState, setReindexState] = useState({ + loadingState: LoadingState.Loading, + errorMessage: null, + reindexTaskPercComplete: null, + }); + + const pollIntervalIdRef = useRef | null>(null); + const isMounted = useRef(false); + + const clearPollInterval = useCallback(() => { + if (pollIntervalIdRef.current) { + clearTimeout(pollIntervalIdRef.current); + pollIntervalIdRef.current = null; + } + }, []); + + const updateStatus = useCallback(async () => { + clearPollInterval(); + + const { data, error } = await api.getReindexStatus(indexName); + + if (error) { + setReindexState({ + ...reindexState, + loadingState: LoadingState.Error, + status: ReindexStatus.failed, + }); + return; + } + + setReindexState(getReindexState(reindexState, data)); + + // Only keep polling if it exists and is in progress. + if (data.reindexOp && data.reindexOp.status === ReindexStatus.inProgress) { + pollIntervalIdRef.current = setTimeout(updateStatus, POLL_INTERVAL); + } + }, [clearPollInterval, api, indexName, reindexState]); + + const startReindex = useCallback(async () => { + const currentReindexState = { + ...reindexState, + }; + + setReindexState({ + ...currentReindexState, + // Only reset last completed step if we aren't currently paused + lastCompletedStep: + currentReindexState.status === ReindexStatus.paused + ? currentReindexState.lastCompletedStep + : undefined, + status: ReindexStatus.inProgress, + reindexTaskPercComplete: null, + errorMessage: null, + cancelLoadingState: undefined, + }); + + api.sendReindexTelemetryData({ start: true }); + + const { data, error } = await api.startReindexTask(indexName); + + if (error) { + setReindexState({ + ...reindexState, + loadingState: LoadingState.Error, + status: ReindexStatus.failed, + }); + return; + } + + setReindexState(getReindexState(reindexState, data)); + updateStatus(); + }, [api, indexName, reindexState, updateStatus]); + + const cancelReindex = useCallback(async () => { + api.sendReindexTelemetryData({ stop: true }); + + const { error } = await api.cancelReindexTask(indexName); + + setReindexState({ + ...reindexState, + cancelLoadingState: LoadingState.Loading, + }); + + if (error) { + setReindexState({ + ...reindexState, + cancelLoadingState: LoadingState.Error, + }); + return; + } + }, [api, indexName, reindexState]); + + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + + // Clean up on unmount. + clearPollInterval(); + }; + }, [clearPollInterval]); + + return { + reindexState, + startReindex, + cancelReindex, + updateStatus, + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_cell.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_cell.scss deleted file mode 100644 index e53fd9b254cf0..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/_cell.scss +++ /dev/null @@ -1,4 +0,0 @@ -.upgDeprecationCell { - overflow: hidden; - padding: $euiSize 0 0 $euiSizeL; -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/cell.tsx deleted file mode 100644 index 4324379f456ea..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/cell.tsx +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ReactNode, FunctionComponent } from 'react'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiLink, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - EnrichedDeprecationInfo, - MlAction, - ReindexAction, - IndexSettingAction, -} from '../../../../../common/types'; -import { AppContext } from '../../../app_context'; -import { ReindexButton } from './reindex'; -import { FixIndexSettingsButton } from './index_settings'; -import { FixMlSnapshotsButton } from './ml_snapshots'; - -interface DeprecationCellProps { - items?: Array<{ title?: string; body: string }>; - docUrl?: string; - headline?: string; - healthColor?: string; - children?: ReactNode; - correctiveAction?: EnrichedDeprecationInfo['correctiveAction']; - indexName?: string; -} - -interface CellActionProps { - correctiveAction: EnrichedDeprecationInfo['correctiveAction']; - indexName?: string; - items: Array<{ title?: string; body: string }>; -} - -const CellAction: FunctionComponent = ({ correctiveAction, indexName, items }) => { - const { type: correctiveActionType } = correctiveAction!; - switch (correctiveActionType) { - case 'mlSnapshot': - const { jobId, snapshotId } = correctiveAction as MlAction; - return ( - - ); - - case 'reindex': - const { blockerForReindexing } = correctiveAction as ReindexAction; - - return ( - - {({ http, docLinks }) => ( - - )} - - ); - - case 'indexSetting': - const { deprecatedSettings } = correctiveAction as IndexSettingAction; - - return ; - - default: - throw new Error(`No UI defined for corrective action: ${correctiveActionType}`); - } -}; - -/** - * Used to display a deprecation with links to docs, a health indicator, and other descriptive information. - */ -export const DeprecationCell: FunctionComponent = ({ - headline, - healthColor, - correctiveAction, - indexName, - docUrl, - items = [], - children, -}) => ( -
- - {healthColor && ( - - - - )} - - - {headline && ( - -

{headline}

-
- )} - - {items.map((item, index) => ( - - {item.title &&
{item.title}
} -

{item.body}

-
- ))} - - {docUrl && ( - <> - - - - - - - )} -
- - {correctiveAction && ( - - - - )} -
- - - - {children} -
-); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/deprecation_group_item.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/deprecation_group_item.tsx deleted file mode 100644 index 66e2a5d25998b..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/deprecation_group_item.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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, { FunctionComponent } from 'react'; -import { EuiAccordion, EuiBadge } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { EnrichedDeprecationInfo } from '../../../../../common/types'; -import { DeprecationHealth } from '../../shared'; -import { GroupByOption } from '../../types'; -import { EsDeprecationList } from './list'; -import { LEVEL_MAP } from '../../constants'; - -export interface Props { - id: string; - deprecations: EnrichedDeprecationInfo[]; - title: string; - currentGroupBy: GroupByOption; - forceExpand: boolean; - dataTestSubj: string; -} - -/** - * A single accordion item for a grouped deprecation item. - */ -export const EsDeprecationAccordion: FunctionComponent = ({ - id, - deprecations, - title, - currentGroupBy, - forceExpand, - dataTestSubj, -}) => { - const hasIndices = Boolean( - currentGroupBy === GroupByOption.message && - (deprecations as EnrichedDeprecationInfo[]).filter((d) => d.index).length - ); - const numIndices = hasIndices ? deprecations.length : null; - - return ( - - {hasIndices && ( - <> - - {numIndices}{' '} - - -   - - )} - LEVEL_MAP[d.level])} - /> - - } - > - - - ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/button.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/button.tsx deleted file mode 100644 index e63e26f3ecc61..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/button.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { EuiButton } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { RemoveIndexSettingsProvider } from './remove_settings_provider'; - -const i18nTexts = { - fixButtonLabel: i18n.translate('xpack.upgradeAssistant.checkupTab.indexSettings.fixButtonLabel', { - defaultMessage: 'Fix', - }), - doneButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.indexSettings.doneButtonLabel', - { - defaultMessage: 'Done', - } - ), -}; - -interface Props { - settings: string[]; - index: string; -} - -/** - * Renders a button if the given index contains deprecated index settings - */ -export const FixIndexSettingsButton: React.FunctionComponent = ({ settings, index }) => { - return ( - - {(removeIndexSettingsPrompt, successfulRequests) => { - const isSuccessfulRequest = successfulRequests[index] === true; - return ( - removeIndexSettingsPrompt(index, settings)} - isDisabled={isSuccessfulRequest} - iconType={isSuccessfulRequest ? 'check' : undefined} - > - {isSuccessfulRequest ? i18nTexts.doneButtonLabel : i18nTexts.fixButtonLabel} - - ); - }} - - ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/remove_settings_provider.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/remove_settings_provider.tsx deleted file mode 100644 index 1fd0c79dbbef3..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_settings/remove_settings_provider.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState, useRef } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiCode, EuiConfirmModal } from '@elastic/eui'; -import { useAppContext } from '../../../../app_context'; - -interface Props { - children: ( - removeSettingsPrompt: (index: string, settings: string[]) => void, - successfulRequests: { [key: string]: boolean } - ) => React.ReactNode; -} - -const i18nTexts = { - removeButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.confirmationModal.removeButtonLabel', - { - defaultMessage: 'Remove', - } - ), - cancelButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.cancelButtonLabel', - { - defaultMessage: 'Cancel', - } - ), - modalDescription: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.description', - { - defaultMessage: 'The following deprecated index settings were detected and will be removed:', - } - ), - successNotificationText: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.successNotificationText', - { - defaultMessage: 'Index settings removed', - } - ), - errorNotificationText: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.errorNotificationText', - { - defaultMessage: 'Error removing index settings', - } - ), -}; - -export const RemoveIndexSettingsProvider = ({ children }: Props) => { - const [isModalOpen, setIsModalOpen] = useState(false); - const [successfulRequests, setSuccessfulRequests] = useState<{ [key: string]: boolean }>({}); - const [isLoading, setIsLoading] = useState(false); - - const deprecatedSettings = useRef([]); - const indexName = useRef(undefined); - - const { api, notifications } = useAppContext(); - - const removeIndexSettings = async () => { - setIsLoading(true); - - const { error } = await api.updateIndexSettings(indexName.current!, deprecatedSettings.current); - - setIsLoading(false); - closeModal(); - - if (error) { - notifications.toasts.addDanger(i18nTexts.errorNotificationText); - } else { - setSuccessfulRequests({ - [indexName.current!]: true, - }); - notifications.toasts.addSuccess(i18nTexts.successNotificationText); - } - }; - - const closeModal = () => { - setIsModalOpen(false); - }; - - const removeSettingsPrompt = (index: string, settings: string[]) => { - setIsModalOpen(true); - setSuccessfulRequests({ - [index]: false, - }); - indexName.current = index; - deprecatedSettings.current = settings; - }; - - return ( - <> - {children(removeSettingsPrompt, successfulRequests)} - - {isModalOpen && ( - - <> -

{i18nTexts.modalDescription}

-
    - {deprecatedSettings.current.map((setting, index) => ( -
  • - {setting} -
  • - ))} -
- -
- )} - - ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.test.tsx deleted file mode 100644 index f4ac573d86b11..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { IndexDeprecationTableProps, IndexDeprecationTable } from './index_table'; - -describe('IndexDeprecationTable', () => { - const defaultProps = { - indices: [ - { index: 'index1', details: 'Index 1 deets', correctiveAction: { type: 'reindex' } }, - { index: 'index2', details: 'Index 2 deets', correctiveAction: { type: 'reindex' } }, - { index: 'index3', details: 'Index 3 deets', correctiveAction: { type: 'reindex' } }, - ], - } as IndexDeprecationTableProps; - - // Relying pretty heavily on EUI to implement the table functionality correctly. - // This test simply verifies that the props passed to EuiBaseTable are the ones - // expected. - test('render', () => { - expect(shallow()).toMatchInlineSnapshot(` - - `); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.tsx deleted file mode 100644 index 6b0f94ea24bc7..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/index_table.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { sortBy } from 'lodash'; -import React from 'react'; - -import { EuiBasicTable } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { - EnrichedDeprecationInfo, - IndexSettingAction, - ReindexAction, -} from '../../../../../common/types'; -import { AppContext } from '../../../app_context'; -import { ReindexButton } from './reindex'; -import { FixIndexSettingsButton } from './index_settings'; - -const PAGE_SIZES = [10, 25, 50, 100, 250, 500, 1000]; - -export interface IndexDeprecationDetails { - index: string; - correctiveAction?: EnrichedDeprecationInfo['correctiveAction']; - details?: string; -} - -export interface IndexDeprecationTableProps { - indices: IndexDeprecationDetails[]; -} - -interface IndexDeprecationTableState { - sortField: string; - sortDirection: 'asc' | 'desc'; - pageIndex: number; - pageSize: number; -} - -export class IndexDeprecationTable extends React.Component< - IndexDeprecationTableProps, - IndexDeprecationTableState -> { - constructor(props: IndexDeprecationTableProps) { - super(props); - - this.state = { - sortField: 'index', - sortDirection: 'asc', - pageIndex: 0, - pageSize: 10, - }; - } - - public render() { - const { pageIndex, pageSize, sortField, sortDirection } = this.state; - - const columns = [ - { - field: 'index', - name: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.deprecations.indexTable.indexColumnLabel', - { - defaultMessage: 'Index', - } - ), - sortable: true, - }, - { - field: 'details', - name: i18n.translate( - 'xpack.upgradeAssistant.checkupTab.deprecations.indexTable.detailsColumnLabel', - { - defaultMessage: 'Details', - } - ), - }, - ]; - - const actionsColumn = this.generateActionsColumn(); - - if (actionsColumn) { - columns.push(actionsColumn as any); - } - - const sorting = { - sort: { field: sortField as keyof IndexDeprecationDetails, direction: sortDirection }, - }; - const pagination = { - pageIndex, - pageSize, - ...this.pageSizeOptions(), - }; - - return ( - { - return { - 'data-test-subj': `indexTableRow-${indexDetails.index}`, - }; - }} - /> - ); - } - - private getRows() { - const { sortField, sortDirection, pageIndex, pageSize } = this.state; - const { indices } = this.props; - - let sorted = sortBy(indices, sortField); - if (sortDirection === 'desc') { - sorted = sorted.reverse(); - } - - const start = pageIndex * pageSize; - return sorted.slice(start, start + pageSize); - } - - private onTableChange = (tableProps: any) => { - this.setState({ - sortField: tableProps.sort.field, - sortDirection: tableProps.sort.direction, - pageIndex: tableProps.page.index, - pageSize: tableProps.page.size, - }); - }; - - private pageSizeOptions() { - const { indices } = this.props; - const totalItemCount = indices.length; - - // If we only have that smallest page size, don't show any page size options. - if (totalItemCount <= PAGE_SIZES[0]) { - return { totalItemCount, pageSizeOptions: [], hidePerPageOptions: true }; - } - - // Keep a size option if the # of items is larger than the previous option. - // This avoids having a long list of useless page sizes. - const pageSizeOptions = PAGE_SIZES.filter((perPage, idx) => { - return idx === 0 || totalItemCount > PAGE_SIZES[idx - 1]; - }); - - return { totalItemCount, pageSizeOptions, hidePerPageOptions: false }; - } - - private generateActionsColumn() { - // NOTE: this naive implementation assumes all indices in the table - // should show the reindex button or fix indices button. This should work for known use cases. - const { indices } = this.props; - const showReindexButton = Boolean(indices.find((i) => i.correctiveAction?.type === 'reindex')); - const showFixSettingsButton = Boolean( - indices.find((i) => i.correctiveAction?.type === 'indexSetting') - ); - - if (showReindexButton === false && showFixSettingsButton === false) { - return null; - } - - return { - actions: [ - { - render(indexDep: IndexDeprecationDetails) { - if (showReindexButton) { - return ( - - {({ http, docLinks }) => { - return ( - - ); - }} - - ); - } - - return ( - - ); - }, - }, - ], - }; - } -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.test.tsx deleted file mode 100644 index 2bfa8119e41bc..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.test.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import { EnrichedDeprecationInfo } from '../../../../../common/types'; -import { GroupByOption } from '../../types'; -import { EsDeprecationList } from './list'; - -describe('EsDeprecationList', () => { - describe('group by message', () => { - const defaultProps = { - deprecations: [ - { message: 'Issue 1', url: '', level: 'warning' }, - { message: 'Issue 1', url: '', level: 'warning' }, - ] as EnrichedDeprecationInfo[], - currentGroupBy: GroupByOption.message, - }; - - test('shows simple messages when index field is not present', () => { - expect(shallow()).toMatchInlineSnapshot(` -
- - -
- `); - }); - - test('shows index deprecation when index field is present', () => { - // Add index fields to deprecation items - const props = { - ...defaultProps, - deprecations: defaultProps.deprecations.map((d, index) => ({ - ...d, - index: index.toString(), - })), - }; - const wrapper = shallow(); - expect(wrapper).toMatchInlineSnapshot(` - - `); - }); - }); - - describe('group by index', () => { - const defaultProps = { - deprecations: [ - { message: 'Issue 1', index: 'index1', url: '', level: 'warning' }, - { message: 'Issue 2', index: 'index1', url: '', level: 'warning' }, - ] as EnrichedDeprecationInfo[], - currentGroupBy: GroupByOption.index, - }; - - test('shows detailed messages', () => { - expect(shallow()).toMatchInlineSnapshot(` -
- - -
- `); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.tsx deleted file mode 100644 index 7b543a7e94b33..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/list.tsx +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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, { FunctionComponent } from 'react'; - -import { DeprecationInfo, EnrichedDeprecationInfo } from '../../../../../common/types'; -import { GroupByOption } from '../../types'; - -import { COLOR_MAP, LEVEL_MAP } from '../../constants'; -import { DeprecationCell } from './cell'; -import { IndexDeprecationDetails, IndexDeprecationTable } from './index_table'; - -const sortByLevelDesc = (a: DeprecationInfo, b: DeprecationInfo) => { - return -1 * (LEVEL_MAP[a.level] - LEVEL_MAP[b.level]); -}; - -/** - * Used to show a single deprecation message with any detailed information. - */ -const MessageDeprecation: FunctionComponent<{ - deprecation: EnrichedDeprecationInfo; -}> = ({ deprecation }) => { - const items = []; - - if (deprecation.details) { - items.push({ body: deprecation.details }); - } - - return ( - - ); -}; - -/** - * Used to show a single (simple) deprecation message with any detailed information. - */ -const SimpleMessageDeprecation: FunctionComponent<{ deprecation: EnrichedDeprecationInfo }> = ({ - deprecation, -}) => { - const items = []; - - if (deprecation.details) { - items.push({ body: deprecation.details }); - } - - return ( - - ); -}; - -interface IndexDeprecationProps { - deprecation: EnrichedDeprecationInfo; - indices: IndexDeprecationDetails[]; -} - -/** - * Shows a single deprecation and table of affected indices with details for each index. - */ -const IndexDeprecation: FunctionComponent = ({ deprecation, indices }) => { - return ( - - - - ); -}; - -/** - * A list of deprecations that is either shown as individual deprecation cells or as a - * deprecation summary for a list of indices. - */ -export const EsDeprecationList: FunctionComponent<{ - deprecations: EnrichedDeprecationInfo[]; - currentGroupBy: GroupByOption; -}> = ({ deprecations, currentGroupBy }) => { - // If we're grouping by message and the first deprecation has an index field, show an index - // group deprecation. Otherwise, show each message. - if (currentGroupBy === GroupByOption.message && deprecations[0].index !== undefined) { - // We assume that every deprecation message is the same issue (since they have the same - // message) and that each deprecation will have an index associated with it. - - const indices = deprecations.map((dep) => ({ - index: dep.index!, - details: dep.details, - correctiveAction: dep.correctiveAction, - })); - return ; - } else if (currentGroupBy === GroupByOption.index) { - return ( -
- {deprecations.sort(sortByLevelDesc).map((dep, index) => ( - - ))} -
- ); - } else { - return ( -
- {deprecations.sort(sortByLevelDesc).map((dep, index) => ( - - ))} -
- ); - } -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/button.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/button.tsx deleted file mode 100644 index 13b7dacc3b598..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/ml_snapshots/button.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useState } from 'react'; - -import { ButtonSize, EuiButton } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { FixSnapshotsFlyout } from './fix_snapshots_flyout'; -import { useAppContext } from '../../../../app_context'; -import { useSnapshotState } from './use_snapshot_state'; - -const i18nTexts = { - fixButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.fixButtonLabel', - { - defaultMessage: 'Fix', - } - ), - upgradingButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradingButtonLabel', - { - defaultMessage: 'Upgrading…', - } - ), - deletingButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.deletingButtonLabel', - { - defaultMessage: 'Deleting…', - } - ), - doneButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.doneButtonLabel', - { - defaultMessage: 'Done', - } - ), - failedButtonLabel: i18n.translate( - 'xpack.upgradeAssistant.esDeprecations.mlSnapshots.failedButtonLabel', - { - defaultMessage: 'Failed', - } - ), -}; - -interface Props { - snapshotId: string; - jobId: string; - description: string; -} - -export const FixMlSnapshotsButton: React.FunctionComponent = ({ - snapshotId, - jobId, - description, -}) => { - const { api } = useAppContext(); - const { snapshotState, upgradeSnapshot, deleteSnapshot, updateSnapshotStatus } = useSnapshotState( - { - jobId, - snapshotId, - api, - } - ); - - const [showFlyout, setShowFlyout] = useState(false); - - useEffect(() => { - updateSnapshotStatus(); - }, [updateSnapshotStatus]); - - const commonButtonProps = { - size: 's' as ButtonSize, - onClick: () => setShowFlyout(true), - 'data-test-subj': 'fixMlSnapshotsButton', - }; - - let button = {i18nTexts.fixButtonLabel}; - - switch (snapshotState.status) { - case 'in_progress': - button = ( - - {snapshotState.action === 'delete' - ? i18nTexts.deletingButtonLabel - : i18nTexts.upgradingButtonLabel} - - ); - break; - case 'complete': - button = ( - - {i18nTexts.doneButtonLabel} - - ); - break; - case 'error': - button = ( - - {i18nTexts.failedButtonLabel} - - ); - break; - } - - return ( - <> - {button} - - {showFlyout && ( - setShowFlyout(false)} - /> - )} - - ); -}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_button.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_button.scss deleted file mode 100644 index f12149f9e88cb..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/_button.scss +++ /dev/null @@ -1,5 +0,0 @@ -.upgReindexButton__spinner { - position: relative; - top: $euiSizeXS / 2; - margin-right: $euiSizeXS; -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/button.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/button.tsx deleted file mode 100644 index 646f253931664..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/button.tsx +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { set } from '@elastic/safer-lodash-set'; -import React, { Fragment, ReactNode } from 'react'; -import { i18n } from '@kbn/i18n'; -import { Subscription } from 'rxjs'; - -import { EuiButton, EuiLoadingSpinner, EuiText, EuiToolTip } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { DocLinksStart, HttpSetup } from 'src/core/public'; -import { API_BASE_PATH } from '../../../../../../common/constants'; -import { ReindexAction, ReindexStatus, UIReindexOption } from '../../../../../../common/types'; -import { LoadingState } from '../../../types'; -import { ReindexFlyout } from './flyout'; -import { ReindexPollingService, ReindexState } from './polling_service'; - -interface ReindexButtonProps { - indexName: string; - http: HttpSetup; - docLinks: DocLinksStart; - reindexBlocker?: ReindexAction['blockerForReindexing']; -} - -interface ReindexButtonState { - flyoutVisible: boolean; - reindexState: ReindexState; -} - -/** - * Displays a button that will display a flyout when clicked with the reindexing status for - * the given `indexName`. - */ -export class ReindexButton extends React.Component { - private service: ReindexPollingService; - private subscription?: Subscription; - - constructor(props: ReindexButtonProps) { - super(props); - - this.service = this.newService(); - this.state = { - flyoutVisible: false, - reindexState: this.service.status$.value, - }; - } - - public async componentDidMount() { - this.subscribeToUpdates(); - } - - public async componentWillUnmount() { - this.unsubscribeToUpdates(); - } - - public componentDidUpdate(prevProps: ReindexButtonProps) { - if (prevProps.indexName !== this.props.indexName) { - this.unsubscribeToUpdates(); - this.service = this.newService(); - this.subscribeToUpdates(); - } - } - - public render() { - const { indexName, reindexBlocker, docLinks } = this.props; - const { flyoutVisible, reindexState } = this.state; - - const buttonProps: any = { size: 's', onClick: this.showFlyout }; - let buttonContent: ReactNode = ( - - ); - - if (reindexState.loadingState === LoadingState.Loading) { - buttonProps.disabled = true; - buttonContent = ( - - ); - } else { - switch (reindexState.status) { - case ReindexStatus.inProgress: - buttonContent = ( - - Reindexing… - - ); - break; - case ReindexStatus.completed: - buttonProps.color = 'secondary'; - buttonProps.iconSide = 'left'; - buttonProps.iconType = 'check'; - buttonContent = ( - - ); - break; - case ReindexStatus.failed: - buttonProps.color = 'danger'; - buttonProps.iconSide = 'left'; - buttonProps.iconType = 'cross'; - buttonContent = ( - - ); - break; - case ReindexStatus.paused: - buttonProps.color = 'warning'; - buttonProps.iconSide = 'left'; - buttonProps.iconType = 'pause'; - buttonContent = ( - - ); - case ReindexStatus.cancelled: - buttonProps.color = 'danger'; - buttonProps.iconSide = 'left'; - buttonProps.iconType = 'cross'; - buttonContent = ( - - ); - break; - } - } - - const showIndexedClosedWarning = - reindexBlocker === 'index-closed' && reindexState.status !== ReindexStatus.completed; - - if (showIndexedClosedWarning) { - buttonProps.color = 'warning'; - buttonProps.iconType = 'alert'; - } - - const button = {buttonContent}; - - return ( - - {showIndexedClosedWarning ? ( - - {i18n.translate( - 'xpack.upgradeAssistant.checkupTab.reindexing.reindexButton.indexClosedToolTipDetails', - { - defaultMessage: - '"{indexName}" needs to be reindexed, but it is currently closed. The Upgrade Assistant will open, reindex and then close the index. Reindexing may take longer than usual.', - values: { indexName }, - } - )} - - } - > - {button} - - ) : ( - button - )} - - {flyoutVisible && ( - - )} - - ); - } - - private newService() { - const { indexName, http } = this.props; - return new ReindexPollingService(indexName, http); - } - - private subscribeToUpdates() { - this.service.updateStatus(); - this.subscription = this.service!.status$.subscribe((reindexState) => - this.setState({ reindexState }) - ); - } - - private unsubscribeToUpdates() { - if (this.subscription) { - this.subscription.unsubscribe(); - delete this.subscription; - } - - if (this.service) { - this.service.stopPolling(); - } - } - - private startReindex = async () => { - if (!this.state.reindexState.status) { - // if status didn't exist we are starting a reindex action - this.sendUIReindexTelemetryInfo('start'); - } - - await this.service.startReindex(); - }; - - private cancelReindex = async () => { - this.sendUIReindexTelemetryInfo('stop'); - await this.service.cancelReindex(); - }; - - private showFlyout = () => { - this.sendUIReindexTelemetryInfo('open'); - this.setState({ flyoutVisible: true }); - }; - - private closeFlyout = () => { - this.sendUIReindexTelemetryInfo('close'); - this.setState({ flyoutVisible: false }); - }; - - private async sendUIReindexTelemetryInfo(uiReindexAction: UIReindexOption) { - await this.props.http.put(`${API_BASE_PATH}/stats/ui_reindex`, { - body: JSON.stringify(set({}, uiReindexAction, true)), - }); - } -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/container.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/container.tsx deleted file mode 100644 index 97031dd08ee2a..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/flyout/container.tsx +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { DocLinksStart } from 'kibana/public'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiCallOut, - EuiFlyout, - EuiFlyoutHeader, - EuiLink, - EuiPortal, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; - -import { ReindexAction, ReindexStatus } from '../../../../../../../common/types'; - -import { ReindexState } from '../polling_service'; -import { ChecklistFlyoutStep } from './checklist_step'; -import { WarningsFlyoutStep } from './warnings_step'; - -enum ReindexFlyoutStep { - reindexWarnings, - checklist, -} - -interface ReindexFlyoutProps { - indexName: string; - closeFlyout: () => void; - reindexState: ReindexState; - startReindex: () => void; - cancelReindex: () => void; - docLinks: DocLinksStart; - reindexBlocker?: ReindexAction['blockerForReindexing']; -} - -interface ReindexFlyoutState { - currentFlyoutStep: ReindexFlyoutStep; -} - -const getOpenAndCloseIndexDocLink = (docLinks: DocLinksStart) => ( - - {i18n.translate( - 'xpack.upgradeAssistant.checkupTab.reindexing.flyout.openAndCloseDocumentation', - { defaultMessage: 'documentation' } - )} - -); - -const getIndexClosedCallout = (docLinks: DocLinksStart) => ( - <> - -

- - {i18n.translate( - 'xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutDetails.reindexingTakesLongerEmphasis', - { defaultMessage: 'Reindexing may take longer than usual' } - )} - - ), - }} - /> -

-
- - -); - -/** - * Wrapper for the contents of the flyout that manages which step of the flyout to show. - */ -export class ReindexFlyout extends React.Component { - constructor(props: ReindexFlyoutProps) { - super(props); - const { status, reindexWarnings } = props.reindexState; - - this.state = { - // If there are any warnings and we haven't started reindexing, show the warnings step first. - currentFlyoutStep: - reindexWarnings && reindexWarnings.length > 0 && status === undefined - ? ReindexFlyoutStep.reindexWarnings - : ReindexFlyoutStep.checklist, - }; - } - - public render() { - const { - closeFlyout, - indexName, - reindexState, - startReindex, - cancelReindex, - reindexBlocker, - docLinks, - } = this.props; - const { currentFlyoutStep } = this.state; - - let flyoutContents: React.ReactNode; - - const globalCallout = - reindexBlocker === 'index-closed' && reindexState.status !== ReindexStatus.completed - ? getIndexClosedCallout(docLinks) - : undefined; - switch (currentFlyoutStep) { - case ReindexFlyoutStep.reindexWarnings: - flyoutContents = ( - globalCallout} - closeFlyout={closeFlyout} - warnings={reindexState.reindexWarnings!} - advanceNextStep={this.advanceNextStep} - /> - ); - break; - case ReindexFlyoutStep.checklist: - flyoutContents = ( - globalCallout} - closeFlyout={closeFlyout} - reindexState={reindexState} - startReindex={startReindex} - cancelReindex={cancelReindex} - /> - ); - break; - default: - throw new Error(`Invalid flyout step: ${currentFlyoutStep}`); - } - - return ( - - - - -

- -

-
-
- {flyoutContents} -
-
- ); - } - - public advanceNextStep = () => { - this.setState({ currentFlyoutStep: ReindexFlyoutStep.checklist }); - }; -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.test.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.test.ts deleted file mode 100644 index 13818e864783e..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ReindexStatus, ReindexStep } from '../../../../../../common/types'; -import { ReindexPollingService } from './polling_service'; -import { httpServiceMock } from 'src/core/public/mocks'; - -const mockClient = httpServiceMock.createSetupContract(); - -describe('ReindexPollingService', () => { - beforeEach(() => { - mockClient.post.mockReset(); - mockClient.get.mockReset(); - }); - - it('does not poll when reindexOp is null', async () => { - mockClient.get.mockResolvedValueOnce({ - warnings: [], - reindexOp: null, - }); - - const service = new ReindexPollingService('myIndex', mockClient); - service.updateStatus(); - await new Promise((resolve) => setTimeout(resolve, 1200)); // wait for poll interval - - expect(mockClient.get).toHaveBeenCalledTimes(1); - service.stopPolling(); - }); - - it('does not poll when first check is a 200 and status is failed', async () => { - mockClient.get.mockResolvedValue({ - warnings: [], - reindexOp: { - lastCompletedStep: ReindexStep.created, - status: ReindexStatus.failed, - errorMessage: `Oh no!`, - }, - }); - - const service = new ReindexPollingService('myIndex', mockClient); - service.updateStatus(); - await new Promise((resolve) => setTimeout(resolve, 1200)); // wait for poll interval - - expect(mockClient.get).toHaveBeenCalledTimes(1); - expect(service.status$.value.errorMessage).toEqual(`Oh no!`); - service.stopPolling(); - }); - - it('begins to poll when first check is a 200 and status is inProgress', async () => { - mockClient.get.mockResolvedValue({ - warnings: [], - reindexOp: { - lastCompletedStep: ReindexStep.created, - status: ReindexStatus.inProgress, - }, - }); - - const service = new ReindexPollingService('myIndex', mockClient); - service.updateStatus(); - await new Promise((resolve) => setTimeout(resolve, 1200)); // wait for poll interval - - expect(mockClient.get).toHaveBeenCalledTimes(2); - service.stopPolling(); - }); - - describe('startReindex', () => { - it('posts to endpoint', async () => { - const service = new ReindexPollingService('myIndex', mockClient); - await service.startReindex(); - - expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex'); - }); - }); - - describe('cancelReindex', () => { - it('posts to cancel endpoint', async () => { - const service = new ReindexPollingService('myIndex', mockClient); - await service.cancelReindex(); - - expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex/cancel'); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.ts deleted file mode 100644 index 239bd56bd2fa5..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecations/reindex/polling_service.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BehaviorSubject } from 'rxjs'; - -import { HttpSetup } from 'src/core/public'; -import { API_BASE_PATH } from '../../../../../../common/constants'; -import { - IndexGroup, - ReindexOperation, - ReindexStatus, - ReindexStep, - ReindexWarning, -} from '../../../../../../common/types'; -import { LoadingState } from '../../../types'; - -const POLL_INTERVAL = 1000; - -export interface ReindexState { - loadingState: LoadingState; - cancelLoadingState?: LoadingState; - lastCompletedStep?: ReindexStep; - status?: ReindexStatus; - reindexTaskPercComplete: number | null; - errorMessage: string | null; - reindexWarnings?: ReindexWarning[]; - hasRequiredPrivileges?: boolean; - indexGroup?: IndexGroup; -} - -interface StatusResponse { - warnings?: ReindexWarning[]; - reindexOp?: ReindexOperation; - hasRequiredPrivileges?: boolean; - indexGroup?: IndexGroup; -} - -/** - * Service used by the frontend to start reindexing and get updates on the state of a reindex - * operation. Exposes an Observable that can be used to subscribe to state updates. - */ -export class ReindexPollingService { - public status$: BehaviorSubject; - private pollTimeout?: NodeJS.Timeout; - - constructor(private indexName: string, private http: HttpSetup) { - this.status$ = new BehaviorSubject({ - loadingState: LoadingState.Loading, - errorMessage: null, - reindexTaskPercComplete: null, - }); - } - - public updateStatus = async () => { - // Prevent two loops from being started. - this.stopPolling(); - - try { - const data = await this.http.get( - `${API_BASE_PATH}/reindex/${this.indexName}` - ); - this.updateWithResponse(data); - - // Only keep polling if it exists and is in progress. - if (data.reindexOp && data.reindexOp.status === ReindexStatus.inProgress) { - this.pollTimeout = setTimeout(this.updateStatus, POLL_INTERVAL); - } - } catch (e) { - this.status$.next({ - ...this.status$.value, - status: ReindexStatus.failed, - }); - } - }; - - public stopPolling = () => { - if (this.pollTimeout) { - clearTimeout(this.pollTimeout); - } - }; - - public startReindex = async () => { - try { - // Optimistically assume it will start, reset other state. - const currentValue = this.status$.value; - this.status$.next({ - ...currentValue, - // Only reset last completed step if we aren't currently paused - lastCompletedStep: - currentValue.status === ReindexStatus.paused ? currentValue.lastCompletedStep : undefined, - status: ReindexStatus.inProgress, - reindexTaskPercComplete: null, - errorMessage: null, - cancelLoadingState: undefined, - }); - - const data = await this.http.post( - `${API_BASE_PATH}/reindex/${this.indexName}` - ); - - this.updateWithResponse({ reindexOp: data }); - this.updateStatus(); - } catch (e) { - this.status$.next({ ...this.status$.value, status: ReindexStatus.failed }); - } - }; - - public cancelReindex = async () => { - try { - this.status$.next({ - ...this.status$.value, - cancelLoadingState: LoadingState.Loading, - }); - - await this.http.post(`${API_BASE_PATH}/reindex/${this.indexName}/cancel`); - } catch (e) { - this.status$.next({ - ...this.status$.value, - cancelLoadingState: LoadingState.Error, - }); - } - }; - - private updateWithResponse = ({ - reindexOp, - warnings, - hasRequiredPrivileges, - indexGroup, - }: StatusResponse) => { - const currentValue = this.status$.value; - // Next value should always include the entire state, not just what changes. - // We make a shallow copy as a starting new state. - const nextValue = { - ...currentValue, - // If we're getting any updates, set to success. - loadingState: LoadingState.Success, - }; - - if (warnings) { - nextValue.reindexWarnings = warnings; - } - - if (hasRequiredPrivileges !== undefined) { - nextValue.hasRequiredPrivileges = hasRequiredPrivileges; - } - - if (indexGroup) { - nextValue.indexGroup = indexGroup; - } - - if (reindexOp) { - // Prevent the UI flickering back to inProgres after cancelling. - nextValue.lastCompletedStep = reindexOp.lastCompletedStep; - nextValue.status = reindexOp.status; - nextValue.reindexTaskPercComplete = reindexOp.reindexTaskPercComplete; - nextValue.errorMessage = reindexOp.errorMessage; - - if (reindexOp.status === ReindexStatus.cancelled) { - nextValue.cancelLoadingState = LoadingState.Success; - } - } - - this.status$.next(nextValue); - }; -} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecation_errors.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecation_errors.tsx index 239433808c5af..5e3c7a5fe6cef 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecation_errors.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecation_errors.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiCallOut } from '@elastic/eui'; import { ResponseError } from '../../lib/api'; -import { getEsDeprecationError } from '../../lib/es_deprecation_errors'; +import { getEsDeprecationError } from '../../lib/get_es_deprecation_error'; interface Props { error: ResponseError; } diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx index 4fc4d691c4038..38367bd3cfaff 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx @@ -5,204 +5,88 @@ * 2.0. */ -import React, { useMemo, useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { - EuiButton, - EuiButtonEmpty, - EuiPageHeader, - EuiTabbedContent, - EuiTabbedContentTab, - EuiToolTip, - EuiNotificationBadge, - EuiSpacer, -} from '@elastic/eui'; +import { EuiPageHeader, EuiSpacer, EuiPageContent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { SectionLoading } from '../../../shared_imports'; import { useAppContext } from '../../app_context'; -import { UpgradeAssistantTabProps, EsTabs, TelemetryState } from '../types'; -import { DeprecationTabContent } from './deprecation_tab_content'; +import { EsDeprecationsTable } from './es_deprecations_table'; +import { EsDeprecationErrors } from './es_deprecation_errors'; +import { NoDeprecationsPrompt } from '../shared'; const i18nTexts = { pageTitle: i18n.translate('xpack.upgradeAssistant.esDeprecations.pageTitle', { - defaultMessage: 'Elasticsearch', + defaultMessage: 'Elasticsearch deprecation warnings', }), pageDescription: i18n.translate('xpack.upgradeAssistant.esDeprecations.pageDescription', { defaultMessage: - 'Review the deprecated cluster and index settings. You must resolve any critical issues before upgrading.', + 'You must resolve all critical issues before upgrading. Back up recommended. Make sure you have a current snapshot before modifying your configuration or reindexing.', }), - docLinkText: i18n.translate('xpack.upgradeAssistant.esDeprecations.docLinkText', { - defaultMessage: 'Documentation', + isLoading: i18n.translate('xpack.upgradeAssistant.esDeprecations.loadingText', { + defaultMessage: 'Loading deprecations…', }), - backupDataButton: { - label: i18n.translate('xpack.upgradeAssistant.esDeprecations.backupDataButtonLabel', { - defaultMessage: 'Back up your data', - }), - tooltipText: i18n.translate('xpack.upgradeAssistant.esDeprecations.backupDataTooltipText', { - defaultMessage: 'Take a snapshot before you make any changes.', - }), - }, - clusterTab: { - tabName: i18n.translate('xpack.upgradeAssistant.esDeprecations.clusterTabLabel', { - defaultMessage: 'Cluster', - }), - deprecationType: i18n.translate('xpack.upgradeAssistant.esDeprecations.clusterLabel', { - defaultMessage: 'cluster', - }), - }, - indicesTab: { - tabName: i18n.translate('xpack.upgradeAssistant.esDeprecations.indicesTabLabel', { - defaultMessage: 'Indices', - }), - deprecationType: i18n.translate('xpack.upgradeAssistant.esDeprecations.indexLabel', { - defaultMessage: 'index', - }), - }, }; -interface MatchParams { - tabName: EsTabs; -} - -export const EsDeprecationsContent = withRouter( - ({ - match: { - params: { tabName }, - }, - history, - }: RouteComponentProps) => { - const [telemetryState, setTelemetryState] = useState(TelemetryState.Complete); - - const { api, breadcrumbs, getUrlForApp, docLinks } = useAppContext(); - - const { data: checkupData, isLoading, error, resendRequest } = api.useLoadUpgradeStatus(); - - const onTabClick = (selectedTab: EuiTabbedContentTab) => { - history.push(`/es_deprecations/${selectedTab.id}`); - }; - - const tabs = useMemo(() => { - const commonTabProps: UpgradeAssistantTabProps = { - error, - isLoading, - refreshCheckupData: resendRequest, - navigateToOverviewPage: () => history.push('/overview'), - }; - - return [ - { - id: 'cluster', - 'data-test-subj': 'upgradeAssistantClusterTab', - name: ( - - {i18nTexts.clusterTab.tabName} - {checkupData && checkupData.cluster.length > 0 && ( - <> - {' '} - {checkupData.cluster.length} - - )} - - ), - content: ( - - ), - }, - { - id: 'indices', - 'data-test-subj': 'upgradeAssistantIndicesTab', - name: ( - - {i18nTexts.indicesTab.tabName} - {checkupData && checkupData.indices.length > 0 && ( - <> - {' '} - {checkupData.indices.length} - - )} - - ), - content: ( - - ), - }, - ]; - }, [checkupData, error, history, isLoading, resendRequest]); - - useEffect(() => { - breadcrumbs.setBreadcrumbs('esDeprecations'); - }, [breadcrumbs]); - - useEffect(() => { - if (isLoading === false) { - setTelemetryState(TelemetryState.Running); +export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => { + const { api, breadcrumbs } = useAppContext(); + + const { + data: esDeprecations, + isLoading, + error, + resendRequest, + isInitialRequest, + } = api.useLoadEsDeprecations(); + + useEffect(() => { + breadcrumbs.setBreadcrumbs('esDeprecations'); + }, [breadcrumbs]); + + useEffect(() => { + if (isLoading === false && isInitialRequest) { + async function sendTelemetryData() { + await api.sendPageTelemetryData({ + elasticsearch: true, + }); + } - async function sendTelemetryData() { - await api.sendTelemetryData({ - [tabName]: true, - }); - setTelemetryState(TelemetryState.Complete); - } + sendTelemetryData(); + } + }, [api, isLoading, isInitialRequest]); - sendTelemetryData(); - } - }, [api, tabName, isLoading]); + if (error) { + return ; + } + if (isLoading) { return ( - <> - - {i18nTexts.docLinkText} - , - ]} - > - - - {i18nTexts.backupDataButton.label} - - - - - + + {i18nTexts.isLoading} + + ); + } - tab.id === tabName)} + if (esDeprecations?.deprecations?.length === 0) { + return ( + + history.push('/overview')} /> - + ); } -); + + return ( +
+ + + + + +
+ ); +}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx new file mode 100644 index 0000000000000..5f742a3c63ae6 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx @@ -0,0 +1,316 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { sortBy } from 'lodash'; +import { + EuiButton, + EuiFlexGroup, + EuiTable, + EuiTableRow, + EuiTableHeaderCell, + EuiTableHeader, + EuiSearchBar, + EuiSpacer, + EuiFlexItem, + EuiTableBody, + EuiTablePagination, + EuiCallOut, + EuiTableRowCell, + Pager, + Query, +} from '@elastic/eui'; +import { EnrichedDeprecationInfo } from '../../../../common/types'; +import { + MlSnapshotsTableRow, + DefaultTableRow, + IndexSettingsTableRow, + ReindexTableRow, +} from './deprecation_types'; +import { DeprecationTableColumns } from '../types'; +import { DEPRECATION_TYPE_MAP } from '../constants'; + +const i18nTexts = { + refreshButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.table.refreshButtonLabel', + { + defaultMessage: 'Refresh', + } + ), + noDeprecationsMessage: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.table.noDeprecationsMessage', + { + defaultMessage: 'No Elasticsearch deprecation issues found', + } + ), + typeFilterLabel: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.typeFilterLabel', { + defaultMessage: 'Type', + }), + criticalFilterLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.table.criticalFilterLabel', + { + defaultMessage: 'Critical', + } + ), + searchPlaceholderLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.table.searchPlaceholderLabel', + { + defaultMessage: 'Filter', + } + ), +}; + +const cellToLabelMap = { + isCritical: { + label: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.statusColumnTitle', { + defaultMessage: 'Status', + }), + width: '8px', + }, + message: { + label: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.issueColumnTitle', { + defaultMessage: 'Issue', + }), + width: '36px', + }, + type: { + label: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.typeColumnTitle', { + defaultMessage: 'Type', + }), + width: '10px', + }, + index: { + label: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.nameColumnTitle', { + defaultMessage: 'Name', + }), + width: '24px', + }, + correctiveAction: { + label: i18n.translate('xpack.upgradeAssistant.esDeprecations.table.resolutionColumnTitle', { + defaultMessage: 'Resolution', + }), + width: '24px', + }, +}; + +const cellTypes = Object.keys(cellToLabelMap) as DeprecationTableColumns[]; +const pageSizeOptions = [50, 100, 200]; + +const renderTableRowCells = (deprecation: EnrichedDeprecationInfo) => { + switch (deprecation.correctiveAction?.type) { + case 'mlSnapshot': + return ; + + case 'indexSetting': + return ; + + case 'reindex': + return ; + + default: + return ; + } +}; + +interface Props { + deprecations?: EnrichedDeprecationInfo[]; + reload: () => void; +} + +interface SortConfig { + isSortAscending: boolean; + sortField: DeprecationTableColumns; +} + +const getSortedItems = (deprecations: EnrichedDeprecationInfo[], sortConfig: SortConfig) => { + const { isSortAscending, sortField } = sortConfig; + const sorted = sortBy(deprecations, [ + (deprecation) => { + if (sortField === 'isCritical') { + // Critical deprecations should take precendence in ascending order + return deprecation.isCritical !== true; + } + return deprecation[sortField]; + }, + ]); + + return isSortAscending ? sorted : sorted.reverse(); +}; + +export const EsDeprecationsTable: React.FunctionComponent = ({ + deprecations = [], + reload, +}) => { + const [sortConfig, setSortConfig] = useState({ + isSortAscending: true, + sortField: 'isCritical', + }); + + const [itemsPerPage, setItemsPerPage] = useState(pageSizeOptions[0]); + const [currentPageIndex, setCurrentPageIndex] = useState(0); + const [searchQuery, setSearchQuery] = useState(EuiSearchBar.Query.MATCH_ALL); + const [searchError, setSearchError] = useState<{ message: string } | undefined>(undefined); + + const [filteredDeprecations, setFilteredDeprecations] = useState( + getSortedItems(deprecations, sortConfig) + ); + + const pager = useMemo(() => new Pager(deprecations.length, itemsPerPage, currentPageIndex), [ + currentPageIndex, + deprecations, + itemsPerPage, + ]); + + const visibleDeprecations = useMemo( + () => filteredDeprecations.slice(pager.firstItemIndex, pager.lastItemIndex + 1), + [filteredDeprecations, pager] + ); + + const handleSort = useCallback( + (fieldName: DeprecationTableColumns) => { + const newSortConfig = { + isSortAscending: sortConfig.sortField === fieldName ? !sortConfig.isSortAscending : true, + sortField: fieldName, + }; + setSortConfig(newSortConfig); + }, + [sortConfig] + ); + + const handleSearch = useCallback(({ query, error }) => { + if (error) { + setSearchError(error); + } else { + setSearchError(undefined); + setSearchQuery(query); + } + }, []); + + useEffect(() => { + const { setTotalItems, goToPageIndex } = pager; + const deprecationsFilteredByQuery = EuiSearchBar.Query.execute(searchQuery, deprecations); + const deprecationsSortedByFieldType = getSortedItems(deprecationsFilteredByQuery, sortConfig); + + setTotalItems(deprecationsSortedByFieldType.length); + setFilteredDeprecations(deprecationsSortedByFieldType); + + // Reset pagination if the filtered results return a different length + if (deprecationsSortedByFieldType.length !== filteredDeprecations.length) { + goToPageIndex(0); + } + }, [deprecations, sortConfig, pager, searchQuery, filteredDeprecations.length]); + + return ( + <> + + + ).map((type) => ({ + value: type, + name: DEPRECATION_TYPE_MAP[type], + })), + }, + ]} + onChange={handleSearch} + /> + + + + {i18nTexts.refreshButtonLabel} + + + + + {searchError && ( +
+ + + +
+ )} + + + + + + {Object.entries(cellToLabelMap).map(([fieldName, cell]) => { + return ( + handleSort(fieldName as DeprecationTableColumns)} + isSorted={sortConfig.sortField === fieldName} + isSortAscending={sortConfig.isSortAscending} + > + {cell.label} + + ); + })} + + + {filteredDeprecations.length === 0 ? ( + + + + {i18nTexts.noDeprecationsMessage} + + + + ) : ( + + {visibleDeprecations.map((deprecation, index) => { + return ( + + {renderTableRowCells(deprecation)} + + ); + })} + + )} + + + + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table_cells.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table_cells.tsx new file mode 100644 index 0000000000000..dd187f19d5e96 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table_cells.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiBadge, EuiLink } from '@elastic/eui'; +import { EnrichedDeprecationInfo } from '../../../../common/types'; +import { DEPRECATION_TYPE_MAP } from '../constants'; +import { DeprecationTableColumns } from '../types'; + +interface Props { + resolutionTableCell?: React.ReactNode; + fieldName: DeprecationTableColumns; + deprecation: EnrichedDeprecationInfo; + openFlyout: () => void; +} + +const i18nTexts = { + criticalBadgeLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.defaultDeprecation.criticalBadgeLabel', + { + defaultMessage: 'Critical', + } + ), +}; + +export const EsDeprecationsTableCells: React.FunctionComponent = ({ + resolutionTableCell, + fieldName, + deprecation, + openFlyout, +}) => { + // "Status column" + if (fieldName === 'isCritical') { + if (deprecation.isCritical === true) { + return {i18nTexts.criticalBadgeLabel}; + } + + return <>{''}; + } + + // "Issue" column + if (fieldName === 'message') { + return ( + + {deprecation.message} + + ); + } + + // "Type" column + if (fieldName === 'type') { + return <>{DEPRECATION_TYPE_MAP[deprecation.type as EnrichedDeprecationInfo['type']]}; + } + + // "Resolution column" + if (fieldName === 'correctiveAction') { + if (resolutionTableCell) { + return <>{resolutionTableCell}; + } + + return <>{''}; + } + + // Default behavior: render value or empty string if undefined + return <>{deprecation[fieldName] ?? ''}; +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/index.ts index 0e69259adc609..1783745843070 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { EsDeprecationsContent } from './es_deprecations'; +export { EsDeprecations } from './es_deprecations'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx index 31b5c80d5b377..56d6e23d9d4f3 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx @@ -112,7 +112,7 @@ export const KibanaDeprecationsContent = withRouter(({ history }: RouteComponent useEffect(() => { async function sendTelemetryData() { - await api.sendTelemetryData({ + await api.sendPageTelemetryData({ kibana: true, }); } diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx index b7cac67ca5a96..f900416873b83 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx @@ -31,7 +31,7 @@ export const Overview: FunctionComponent = () => { useEffect(() => { async function sendTelemetryData() { - await api.sendTelemetryData({ + await api.sendPageTelemetryData({ overview: true, }); } diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx index 97306dac287ba..ef0b3f438da03 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats.tsx @@ -50,13 +50,12 @@ const i18nTexts = { criticalDeprecations, }, }), - getWarningDeprecationMessage: (clusterCount: number, indexCount: number) => - i18n.translate('xpack.upgradeAssistant.esDeprecationStats.totalDeprecationsTooltip', { + getWarningDeprecationMessage: (warningDeprecations: number) => + i18n.translate('xpack.upgradeAssistant.esDeprecationStats.warningDeprecationsTooltip', { defaultMessage: - 'This cluster is using {clusterCount} deprecated cluster settings and {indexCount} deprecated index settings', + 'This cluster has {warningDeprecations} non-critical {warningDeprecations, plural, one {deprecation} other {deprecations}}', values: { - clusterCount, - indexCount, + warningDeprecations, }, }), }; @@ -65,15 +64,12 @@ export const ESDeprecationStats: FunctionComponent = () => { const history = useHistory(); const { api } = useAppContext(); - const { data: esDeprecations, isLoading, error } = api.useLoadUpgradeStatus(); + const { data: esDeprecations, isLoading, error } = api.useLoadEsDeprecations(); - const allDeprecations = esDeprecations?.cluster?.concat(esDeprecations?.indices) ?? []; - const warningDeprecations = allDeprecations.filter( - (deprecation) => deprecation.level === 'warning' - ); - const criticalDeprecations = allDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - ); + const warningDeprecations = + esDeprecations?.deprecations?.filter((deprecation) => deprecation.isCritical === false) || []; + const criticalDeprecations = + esDeprecations?.deprecations?.filter((deprecation) => deprecation.isCritical) || []; const hasWarnings = warningDeprecations.length > 0; const hasCritical = criticalDeprecations.length > 0; @@ -90,7 +86,7 @@ export const ESDeprecationStats: FunctionComponent = () => { {error && } } - {...(!hasNoDeprecations && reactRouterNavigate(history, '/es_deprecations/cluster'))} + {...(!hasNoDeprecations && reactRouterNavigate(history, '/es_deprecations'))} > @@ -137,10 +133,7 @@ export const ESDeprecationStats: FunctionComponent = () => {

{isLoading ? i18nTexts.loadingText - : i18nTexts.getWarningDeprecationMessage( - esDeprecations?.cluster.length ?? 0, - esDeprecations?.indices.length ?? 0 - )} + : i18nTexts.getWarningDeprecationMessage(warningDeprecations.length)}

)} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx index 5db5b80cc42eb..c717a8a2e12e8 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/review_logs_step/es_stats/es_stats_error.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiIconTip } from '@elastic/eui'; import { ResponseError } from '../../../../lib/api'; -import { getEsDeprecationError } from '../../../../lib/es_deprecation_errors'; +import { getEsDeprecationError } from '../../../../lib/get_es_deprecation_error'; interface Props { error: ResponseError; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/shared/no_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/shared/no_deprecations.tsx index 3626151b63bbf..7763450c6cfcf 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/shared/no_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/shared/no_deprecations.tsx @@ -12,14 +12,14 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; const i18nTexts = { - emptyPromptTitle: i18n.translate('xpack.upgradeAssistant.noDeprecationsPrompt.title', { - defaultMessage: 'Ready to upgrade!', - }), - getEmptyPromptDescription: (deprecationType: string) => + getEmptyPromptTitle: (deprecationType: string) => i18n.translate('xpack.upgradeAssistant.noDeprecationsPrompt.description', { - defaultMessage: 'Your configuration is up to date.', + defaultMessage: 'Your {deprecationType} configuration is up to date', + values: { + deprecationType, + }, }), - getEmptyPromptNextStepsDescription: (navigateToOverviewPage: () => void) => ( + getEmptyPromptDescription: (navigateToOverviewPage: () => void) => ( = ({ }) => { return ( {i18nTexts.emptyPromptTitle}
} + title={

{i18nTexts.getEmptyPromptTitle(deprecationType)}

} body={ <>

- {i18nTexts.getEmptyPromptDescription(deprecationType)} + {i18nTexts.getEmptyPromptDescription(navigateToOverviewPage)}

-

{i18nTexts.getEmptyPromptNextStepsDescription(navigateToOverviewPage)}

} /> diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts index b4fd78252b2ff..b46bb583244f0 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts @@ -5,27 +5,8 @@ * 2.0. */ -import React from 'react'; - -import { EnrichedDeprecationInfo, ESUpgradeStatus } from '../../../common/types'; import { ResponseError } from '../lib/api'; -export interface UpgradeAssistantTabProps { - alertBanner?: React.ReactNode; - checkupData?: ESUpgradeStatus | null; - deprecations?: EnrichedDeprecationInfo[]; - refreshCheckupData: () => void; - error: ResponseError | null; - isLoading: boolean; - navigateToOverviewPage: () => void; -} - -// eslint-disable-next-line react/prefer-stateless-function -export class UpgradeAssistantTabComponent< - T extends UpgradeAssistantTabProps = UpgradeAssistantTabProps, - S = {} -> extends React.Component {} - export enum LoadingState { Loading, Success, @@ -40,13 +21,14 @@ export enum GroupByOption { node = 'node', } -export enum TelemetryState { - Running, - Complete, -} - -export type EsTabs = 'cluster' | 'indices'; +export type DeprecationTableColumns = + | 'type' + | 'index' + | 'message' + | 'correctiveAction' + | 'isCritical'; +export type Status = 'in_progress' | 'complete' | 'idle' | 'error'; export interface DeprecationLoggingPreviewProps { isDeprecationLogIndexingEnabled: boolean; onlyDeprecationLogWritingEnabled: boolean; diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts index 1b22d26ea7218..78070c5717496 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts @@ -45,14 +45,14 @@ export class ApiService { this.client = httpClient; } - public useLoadUpgradeStatus() { + public useLoadEsDeprecations() { return this.useRequest({ path: `${API_BASE_PATH}/es_deprecations`, method: 'get', }); } - public async sendTelemetryData(telemetryData: { [tabName: string]: boolean }) { + public async sendPageTelemetryData(telemetryData: { [tabName: string]: boolean }) { const result = await this.sendRequest({ path: `${API_BASE_PATH}/stats/ui_open`, method: 'put', @@ -125,6 +125,37 @@ export class ApiService { method: 'get', }); } + + public async sendReindexTelemetryData(telemetryData: { [key: string]: boolean }) { + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/stats/ui_reindex`, + method: 'put', + body: JSON.stringify(telemetryData), + }); + + return result; + } + + public async getReindexStatus(indexName: string) { + return await this.sendRequest({ + path: `${API_BASE_PATH}/reindex/${indexName}`, + method: 'get', + }); + } + + public async startReindexTask(indexName: string) { + return await this.sendRequest({ + path: `${API_BASE_PATH}/reindex/${indexName}`, + method: 'post', + }); + } + + public async cancelReindexTask(indexName: string) { + return await this.sendRequest({ + path: `${API_BASE_PATH}/reindex/${indexName}/cancel`, + method: 'post', + }); + } } export const apiService = new ApiService(); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts index 00359988d5e2a..f36dc2096ddc7 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts @@ -16,7 +16,7 @@ const i18nTexts = { defaultMessage: 'Upgrade Assistant', }), esDeprecations: i18n.translate('xpack.upgradeAssistant.breadcrumb.esDeprecationsLabel', { - defaultMessage: 'Elasticsearch deprecations', + defaultMessage: 'Elasticsearch deprecation warnings', }), kibanaDeprecations: i18n.translate( 'xpack.upgradeAssistant.breadcrumb.kibanaDeprecationsLabel', diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/es_deprecation_errors.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts similarity index 93% rename from x-pack/plugins/upgrade_assistant/public/application/lib/es_deprecation_errors.ts rename to x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts index 4220f0eef8d42..85cfd2a3fd16c 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/es_deprecation_errors.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts @@ -25,8 +25,7 @@ const i18nTexts = { upgradedMessage: i18n.translate( 'xpack.upgradeAssistant.esDeprecationErrors.upgradedWarningMessage', { - defaultMessage: - 'Your configuration is up to date. Kibana and all Elasticsearch nodes are running the same version.', + defaultMessage: 'All Elasticsearch nodes have been upgraded.', } ), loadingError: i18n.translate('xpack.upgradeAssistant.esDeprecationErrors.loadingErrorMessage', { diff --git a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts index c3ffd44662ec2..64b52065f63e6 100644 --- a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts +++ b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts @@ -15,6 +15,7 @@ export { useRequest, UseRequestConfig, SectionLoading, + GlobalFlyout, } from '../../../../src/plugins/es_ui_shared/public/'; export { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json index ef724e3bf892e..617bb02ff9dfc 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json +++ b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json @@ -4,13 +4,15 @@ "level": "warning", "message": "Template patterns are no longer using `template` field, but `index_patterns` instead", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", - "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template" + "details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template", + "resolve_during_rolling_upgrade": false }, { "level": "warning", "message": "one or more templates use deprecated mapping settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", - "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}" + "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}", + "resolve_during_rolling_upgrade": false } ], "ml_settings": [ @@ -18,7 +20,8 @@ "level": "warning", "message": "Datafeed [deprecation-datafeed] uses deprecated query options", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes", - "details": "[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]" + "details": "[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]", + "resolve_during_rolling_upgrade": false }, { "level": "critical", @@ -28,7 +31,8 @@ "_meta": { "snapshot_id": "1", "job_id": "deprecation_check_job" - } + }, + "resolve_during_rolling_upgrade": false } ], "node_settings": [ @@ -36,7 +40,8 @@ "level": "critical", "message": "A node-level issue", "url": "http://nodeissue.com", - "details": "This node thing is wrong" + "details": "This node thing is wrong", + "resolve_during_rolling_upgrade": true } ], "index_settings": { @@ -45,7 +50,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]" + "details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]", + "resolve_during_rolling_upgrade": false } ], "twitter": [ @@ -53,7 +59,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: tweet, field: liked]]" + "details": "[[type: tweet, field: liked]]", + "resolve_during_rolling_upgrade": false } ], "old_index": [ @@ -62,7 +69,8 @@ "message": "Index created before 7.0", "url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html", - "details": "This index was created using version: 6.8.13" + "details": "This index was created using version: 6.8.13", + "resolve_during_rolling_upgrade": false } ], "closed_index": [ @@ -70,7 +78,8 @@ "level": "critical", "message": "Index created before 7.0", "url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html", - "details": "This index was created using version: 6.8.13" + "details": "This index was created using version: 6.8.13", + "resolve_during_rolling_upgrade": false } ], "deprecated_settings": [ @@ -80,7 +89,8 @@ "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html", "details": - "translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)" + "translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)", + "resolve_during_rolling_upgrade": false } ], ".kibana": [ @@ -88,7 +98,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]" + "details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]", + "resolve_during_rolling_upgrade": false } ], ".watcher-history-6-2018.11.07": [ @@ -96,7 +107,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]" + "details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]", + "resolve_during_rolling_upgrade": false } ], ".monitoring-kibana-6-2018.11.07": [ @@ -104,7 +116,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: doc, field: snapshot]]" + "details": "[[type: doc, field: snapshot]]", + "resolve_during_rolling_upgrade": false } ], "twitter2": [ @@ -112,7 +125,8 @@ "level": "warning", "message": "Coercion of boolean fields", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", - "details": "[[type: tweet, field: liked]]" + "details": "[[type: tweet, field: liked]]", + "resolve_during_rolling_upgrade": false } ] } diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap index 3e847eef18f07..be9ea11a4886e 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap +++ b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap @@ -2,26 +2,32 @@ exports[`getESUpgradeStatus returns the correct shape of data 1`] = ` Object { - "cluster": Array [ + "deprecations": Array [ Object { "correctiveAction": undefined, "details": "templates using \`template\` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template", - "level": "warning", + "isCritical": false, "message": "Template patterns are no longer using \`template\` field, but \`index_patterns\` instead", + "resolveDuringUpgrade": false, + "type": "cluster_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal", }, Object { "correctiveAction": undefined, "details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}", - "level": "warning", + "isCritical": false, "message": "one or more templates use deprecated mapping settings", + "resolveDuringUpgrade": false, + "type": "cluster_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html", }, Object { "correctiveAction": undefined, "details": "[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]", - "level": "warning", + "isCritical": false, "message": "Datafeed [deprecation-datafeed] uses deprecated query options", + "resolveDuringUpgrade": false, + "type": "ml_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes", }, Object { @@ -31,33 +37,39 @@ Object { "type": "mlSnapshot", }, "details": "details", - "level": "critical", + "isCritical": true, "message": "model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded", + "resolveDuringUpgrade": false, + "type": "ml_settings", "url": "", }, Object { "correctiveAction": undefined, "details": "This node thing is wrong", - "level": "critical", + "isCritical": true, "message": "A node-level issue", + "resolveDuringUpgrade": true, + "type": "node_settings", "url": "http://nodeissue.com", }, - ], - "indices": Array [ Object { "correctiveAction": undefined, "details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]", "index": ".monitoring-es-6-2018.11.07", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, Object { "correctiveAction": undefined, "details": "[[type: tweet, field: liked]]", "index": "twitter", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, Object { @@ -67,8 +79,10 @@ Object { }, "details": "This index was created using version: 6.8.13", "index": "old_index", - "level": "critical", + "isCritical": true, "message": "Index created before 7.0", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html", }, Object { @@ -78,8 +92,10 @@ Object { }, "details": "This index was created using version: 6.8.13", "index": "closed_index", - "level": "critical", + "isCritical": true, "message": "Index created before 7.0", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html", }, Object { @@ -92,40 +108,50 @@ Object { }, "details": "translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)", "index": "deprecated_settings", - "level": "warning", + "isCritical": false, "message": "translog retention settings are ignored", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html", }, Object { "correctiveAction": undefined, "details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]", "index": ".kibana", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, Object { "correctiveAction": undefined, "details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]", "index": ".watcher-history-6-2018.11.07", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, Object { "correctiveAction": undefined, "details": "[[type: doc, field: snapshot]]", "index": ".monitoring-kibana-6-2018.11.07", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, Object { "correctiveAction": undefined, "details": "[[type: tweet, field: liked]]", "index": "twitter2", - "level": "warning", + "isCritical": false, "message": "Coercion of boolean fields", + "resolveDuringUpgrade": false, + "type": "index_settings", "url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields", }, ], diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.test.ts index f87a8916e1a52..e1a348f8ed8ee 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.test.ts @@ -8,7 +8,7 @@ import _ from 'lodash'; import { RequestEvent } from '@elastic/elasticsearch/lib/Transport'; import { elasticsearchServiceMock } from 'src/core/server/mocks'; -import { DeprecationAPIResponse } from '../../common/types'; +import { MigrationDeprecationInfoResponse } from '@elastic/elasticsearch/api/types'; import { getESUpgradeStatus } from './es_deprecations_status'; import fakeDeprecations from './__fixtures__/fake_deprecations.json'; @@ -32,12 +32,11 @@ describe('getESUpgradeStatus', () => { }; // @ts-expect-error mock data is too loosely typed - const deprecationsResponse: DeprecationAPIResponse = _.cloneDeep(fakeDeprecations); + const deprecationsResponse: MigrationDeprecationInfoResponse = _.cloneDeep(fakeDeprecations); const esClient = elasticsearchServiceMock.createScopedClusterClient(); esClient.asCurrentUser.migration.deprecations.mockResolvedValue( - // @ts-expect-error not full interface asApiResponse(deprecationsResponse) ); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts index b87d63ae36ec1..cd719cc0f32b5 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts @@ -5,13 +5,13 @@ * 2.0. */ +import { + MigrationDeprecationInfoDeprecation, + MigrationDeprecationInfoResponse, +} from '@elastic/elasticsearch/api/types'; import { IScopedClusterClient } from 'src/core/server'; import { indexSettingDeprecations } from '../../common/constants'; -import { - DeprecationAPIResponse, - EnrichedDeprecationInfo, - ESUpgradeStatus, -} from '../../common/types'; +import { EnrichedDeprecationInfo, ESUpgradeStatus } from '../../common/types'; import { esIndicesStateCheck } from './es_indices_state_check'; @@ -20,33 +20,82 @@ export async function getESUpgradeStatus( ): Promise { const { body: deprecations } = await dataClient.asCurrentUser.migration.deprecations(); - const cluster = getClusterDeprecations(deprecations); - const indices = await getCombinedIndexInfos(deprecations, dataClient); + const getCombinedDeprecations = async () => { + const indices = await getCombinedIndexInfos(deprecations, dataClient); + + return Object.keys(deprecations).reduce((combinedDeprecations, deprecationType) => { + if (deprecationType === 'index_settings') { + combinedDeprecations = combinedDeprecations.concat(indices); + } else { + const deprecationsByType = deprecations[ + deprecationType as keyof MigrationDeprecationInfoResponse + ] as MigrationDeprecationInfoDeprecation[]; + + const enrichedDeprecationInfo = deprecationsByType.map( + ({ + details, + level, + message, + url, + // @ts-expect-error @elastic/elasticsearch _meta not available yet in MigrationDeprecationInfoResponse + _meta: metadata, + // @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse + resolve_during_rolling_upgrade: resolveDuringUpgrade, + }) => { + return { + details, + message, + url, + type: deprecationType as keyof MigrationDeprecationInfoResponse, + isCritical: level === 'critical', + resolveDuringUpgrade, + correctiveAction: getCorrectiveAction(message, metadata), + }; + } + ); + + combinedDeprecations = combinedDeprecations.concat(enrichedDeprecationInfo); + } + + return combinedDeprecations; + }, [] as EnrichedDeprecationInfo[]); + }; - const totalCriticalDeprecations = cluster.concat(indices).filter((d) => d.level === 'critical') - .length; + const combinedDeprecations = await getCombinedDeprecations(); + const criticalWarnings = combinedDeprecations.filter(({ isCritical }) => isCritical === true); return { - totalCriticalDeprecations, - cluster, - indices, + totalCriticalDeprecations: criticalWarnings.length, + deprecations: combinedDeprecations, }; } // Reformats the index deprecations to an array of deprecation warnings extended with an index field. const getCombinedIndexInfos = async ( - deprecations: DeprecationAPIResponse, + deprecations: MigrationDeprecationInfoResponse, dataClient: IScopedClusterClient ) => { const indices = Object.keys(deprecations.index_settings).reduce( (indexDeprecations, indexName) => { return indexDeprecations.concat( deprecations.index_settings[indexName].map( - (d) => + ({ + details, + message, + url, + level, + // @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse + resolve_during_rolling_upgrade: resolveDuringUpgrade, + }) => ({ - ...d, + details, + message, + url, index: indexName, - correctiveAction: getCorrectiveAction(d.message), + type: 'index_settings', + isCritical: level === 'critical', + correctiveAction: getCorrectiveAction(message), + resolveDuringUpgrade, } as EnrichedDeprecationInfo) ) ); @@ -71,21 +120,10 @@ const getCombinedIndexInfos = async ( return indices as EnrichedDeprecationInfo[]; }; -const getClusterDeprecations = (deprecations: DeprecationAPIResponse) => { - const combinedDeprecations = deprecations.cluster_settings - .concat(deprecations.ml_settings) - .concat(deprecations.node_settings); - - return combinedDeprecations.map((deprecation) => { - const { _meta: metadata, ...deprecationInfo } = deprecation; - return { - ...deprecationInfo, - correctiveAction: getCorrectiveAction(deprecation.message, metadata), - }; - }) as EnrichedDeprecationInfo[]; -}; - -const getCorrectiveAction = (message: string, metadata?: { [key: string]: string }) => { +const getCorrectiveAction = ( + message: string, + metadata?: { [key: string]: string } +): EnrichedDeprecationInfo['correctiveAction'] => { const indexSettingDeprecation = Object.values(indexSettingDeprecations).find( ({ deprecationMessage }) => deprecationMessage === message ); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts index a911c5810dd0a..caff78390b9d1 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts @@ -22,13 +22,12 @@ describe('Upgrade Assistant Telemetry SavedObject UIOpen', () => { await upsertUIOpenOption({ overview: true, - cluster: true, - indices: true, + elasticsearch: true, kibana: true, savedObjects: { createInternalRepository: () => internalRepo } as any, }); - expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(4); + expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(3); expect(internalRepo.incrementCounter).toHaveBeenCalledWith( UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, @@ -37,12 +36,7 @@ describe('Upgrade Assistant Telemetry SavedObject UIOpen', () => { expect(internalRepo.incrementCounter).toHaveBeenCalledWith( UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, - ['ui_open.cluster'] - ); - expect(internalRepo.incrementCounter).toHaveBeenCalledWith( - UPGRADE_ASSISTANT_TYPE, - UPGRADE_ASSISTANT_DOC_ID, - ['ui_open.indices'] + ['ui_open.elasticsearch'] ); expect(internalRepo.incrementCounter).toHaveBeenCalledWith( UPGRADE_ASSISTANT_TYPE, diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts index ab876828a343c..3d463fe4b03ed 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts @@ -33,8 +33,7 @@ type UpsertUIOpenOptionDependencies = UIOpen & { savedObjects: SavedObjectsServi export async function upsertUIOpenOption({ overview, - cluster, - indices, + elasticsearch, savedObjects, kibana, }: UpsertUIOpenOptionDependencies): Promise { @@ -42,12 +41,8 @@ export async function upsertUIOpenOption({ await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'overview' }); } - if (cluster) { - await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'cluster' }); - } - - if (indices) { - await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'indices' }); + if (elasticsearch) { + await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'elasticsearch' }); } if (kibana) { @@ -56,8 +51,7 @@ export async function upsertUIOpenOption({ return { overview, - cluster, - indices, + elasticsearch, kibana, }; } diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts index 2227139c53cda..50c5b358aa5cb 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts @@ -54,8 +54,7 @@ describe('Upgrade Assistant Usage Collector', () => { return { attributes: { 'ui_open.overview': 10, - 'ui_open.cluster': 20, - 'ui_open.indices': 30, + 'ui_open.elasticsearch': 20, 'ui_open.kibana': 15, 'ui_reindex.close': 1, 'ui_reindex.open': 4, @@ -94,8 +93,7 @@ describe('Upgrade Assistant Usage Collector', () => { expect(upgradeAssistantStats).toEqual({ ui_open: { overview: 10, - cluster: 20, - indices: 30, + elasticsearch: 20, kibana: 15, }, ui_reindex: { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts index a6253ab1091da..ee997f5da7ab7 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts @@ -77,8 +77,7 @@ export async function fetchUpgradeAssistantMetrics( const defaultTelemetrySavedObject = { ui_open: { overview: 0, - cluster: 0, - indices: 0, + elasticsearch: 0, kibana: 0, }, ui_reindex: { @@ -96,8 +95,7 @@ export async function fetchUpgradeAssistantMetrics( return { ui_open: { overview: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.overview', 0), - cluster: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.cluster', 0), - indices: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.indices', 0), + elasticsearch: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.elasticsearch', 0), kibana: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.kibana', 0), }, ui_reindex: { @@ -146,18 +144,10 @@ export function registerUpgradeAssistantUsageCollector({ }, }, ui_open: { - cluster: { + elasticsearch: { type: 'long', _meta: { - description: - 'Number of times a user viewed the list of Elasticsearch cluster deprecations.', - }, - }, - indices: { - type: 'long', - _meta: { - description: - 'Number of times a user viewed the list of Elasticsearch index deprecations.', + description: 'Number of times a user viewed the list of Elasticsearch deprecations.', }, }, overview: { diff --git a/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.test.ts index 9603eae18d9c1..bea74f116e0e2 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.test.ts @@ -44,9 +44,8 @@ describe('ES deprecations API', () => { describe('GET /api/upgrade_assistant/es_deprecations', () => { it('returns state', async () => { ESUpgradeStatusApis.getESUpgradeStatus.mockResolvedValue({ - cluster: [], - indices: [], - nodes: [], + deprecations: [], + totalCriticalDeprecations: 0, }); const resp = await routeDependencies.router.getHandler({ method: 'get', @@ -55,15 +54,18 @@ describe('ES deprecations API', () => { expect(resp.status).toEqual(200); expect(JSON.stringify(resp.payload)).toMatchInlineSnapshot( - `"{\\"cluster\\":[],\\"indices\\":[],\\"nodes\\":[]}"` + `"{\\"deprecations\\":[],\\"totalCriticalDeprecations\\":0}"` ); }); it('returns an 403 error if it throws forbidden', async () => { - const e: any = new Error(`you can't go here!`); - e.statusCode = 403; + const error = { + name: 'ResponseError', + message: `you can't go here!`, + statusCode: 403, + }; - ESUpgradeStatusApis.getESUpgradeStatus.mockRejectedValue(e); + ESUpgradeStatusApis.getESUpgradeStatus.mockRejectedValue(error); const resp = await routeDependencies.router.getHandler({ method: 'get', pathPattern: '/api/upgrade_assistant/es_deprecations', diff --git a/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.ts b/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.ts index 395fa04af9173..eb0ade26de766 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/es_deprecations.ts @@ -11,6 +11,7 @@ import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; import { RouteDependencies } from '../types'; import { reindexActionsFactory } from '../lib/reindexing/reindex_actions'; import { reindexServiceFactory } from '../lib/reindexing'; +import { handleEsError } from '../shared_imports'; export function registerESDeprecationRoutes({ router, licensing, log }: RouteDependencies) { router.get( @@ -40,7 +41,7 @@ export function registerESDeprecationRoutes({ router, licensing, log }: RouteDep log, licensing ); - const indexNames = status.indices + const indexNames = status.deprecations .filter(({ index }) => typeof index !== 'undefined') .map(({ index }) => index as string); @@ -50,11 +51,7 @@ export function registerESDeprecationRoutes({ router, licensing, log }: RouteDep body: status, }); } catch (e) { - if (e.statusCode === 403) { - return response.forbidden(e.message); - } - - throw e; + return handleEsError({ error: e, response }); } } ) diff --git a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts index 05ad542ec9c00..578cceb702751 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts @@ -44,8 +44,8 @@ describe('Upgrade Assistant Telemetry API', () => { it('returns correct payload with single option', async () => { const returnPayload = { overview: true, - cluster: false, - indices: false, + elasticsearch: false, + kibana: false, }; (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); @@ -65,8 +65,8 @@ describe('Upgrade Assistant Telemetry API', () => { it('returns correct payload with multiple option', async () => { const returnPayload = { overview: true, - cluster: true, - indices: true, + elasticsearch: true, + kibana: true, }; (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); @@ -79,8 +79,8 @@ describe('Upgrade Assistant Telemetry API', () => { createRequestMock({ body: { overview: true, - cluster: true, - indices: true, + elasticsearch: true, + kibana: true, }, }), kibanaResponseFactory diff --git a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts index 4e9b4b9a472a9..d083b38c7c240 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts @@ -18,19 +18,17 @@ export function registerTelemetryRoutes({ router, getSavedObjectsService }: Rout validate: { body: schema.object({ overview: schema.boolean({ defaultValue: false }), - cluster: schema.boolean({ defaultValue: false }), - indices: schema.boolean({ defaultValue: false }), + elasticsearch: schema.boolean({ defaultValue: false }), kibana: schema.boolean({ defaultValue: false }), }), }, }, async (ctx, request, response) => { - const { cluster, indices, overview, kibana } = request.body; + const { elasticsearch, overview, kibana } = request.body; return response.ok({ body: await upsertUIOpenOption({ savedObjects: getSavedObjectsService(), - cluster, - indices, + elasticsearch, overview, kibana, }), diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts index f76c07da678da..42d5d339dd050 100644 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts @@ -21,11 +21,7 @@ export const telemetrySavedObjectType: SavedObjectsType = { type: 'long', null_value: 0, }, - cluster: { - type: 'long', - null_value: 0, - }, - indices: { + elasticsearch: { type: 'long', null_value: 0, }, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 7e819fb15ea1f..93535826d14e7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -39,7 +39,8 @@ export default function alertTests({ getService }: FtrProviderContext) { const esTestIndexTool = new ESTestIndexTool(es, retry); const taskManagerUtils = new TaskManagerUtils(es, retry); - describe('alerts', () => { + // FLAKY: https://github.com/elastic/kibana/issues/106492 + describe.skip('alerts', () => { const authorizationIndex = '.kibana-test-authorization'; const objectRemover = new ObjectRemover(supertest); diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js index 1f5f28744dd98..8e29604a0bf62 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js @@ -119,6 +119,38 @@ export default function ({ getService }) { }); }); + describe('edit', () => { + it('keeps _meta field intact', async () => { + const policyName = 'edit-meta-test-policy'; + const policy = { + ...getPolicyPayload(policyName), + _meta: { description: 'test policy with _meta field' }, + }; + + // Update the policy (uses the same route as create) + await createPolicy(policy).expect(200); + + // only update warm phase timing, not deleting or changing _meta field + const editedPolicy = { + ...policy, + phases: { + ...policy.phases, + warm: { + ...policy.phases.warm, + min_age: '2d', + }, + }, + }; + + await createPolicy(editedPolicy).expect(200); + + const { body } = await loadPolicies(); + const loadedPolicy = body.find((p) => p.name === policyName); + // Make sure the edited policy still has _meta field + expect(loadedPolicy.policy._meta).to.eql(editedPolicy._meta); + }); + }); + describe('delete', () => { it('should delete the policy created', async () => { const policy = getPolicyPayload('delete-test-policy'); diff --git a/x-pack/plugins/dashboard_mode/common/constants.ts b/x-pack/test/apm_api_integration/common/utils/parse_b_fetch.ts similarity index 54% rename from x-pack/plugins/dashboard_mode/common/constants.ts rename to x-pack/test/apm_api_integration/common/utils/parse_b_fetch.ts index b58afc9b663c8..79ea70f7199f9 100644 --- a/x-pack/plugins/dashboard_mode/common/constants.ts +++ b/x-pack/test/apm_api_integration/common/utils/parse_b_fetch.ts @@ -5,6 +5,11 @@ * 2.0. */ -export const UI_SETTINGS = { - CONFIG_DASHBOARD_ONLY_MODE_ROLES: 'xpackDashboardMode:roles', -}; +import request from 'superagent'; + +export function parseBfetchResponse(resp: request.Response): Array> { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts index e1bd5a8d05b48..5400c3af64b55 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts @@ -9,11 +9,11 @@ import expect from '@kbn/expect'; import { ALERT_DURATION, ALERT_END, + ALERT_RULE_UUID, ALERT_START, ALERT_STATUS, ALERT_UUID, EVENT_KIND, - ALERT_RULE_UUID, } from '@kbn/rule-data-utils'; import { merge, omit } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -393,6 +393,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { "kibana.alert.status": Array [ "open", ], + "kibana.alert.workflow_status": Array [ + "open", + ], "kibana.space_ids": Array [ "default", ], @@ -500,6 +503,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { "kibana.alert.status": Array [ "closed", ], + "kibana.alert.workflow_status": Array [ + "open", + ], "kibana.space_ids": Array [ "default", ], diff --git a/x-pack/test/apm_api_integration/tests/correlations/errors_failed_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/errors_failed_transactions.ts index 12d71530ecce1..054ccbfb4996e 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/errors_failed_transactions.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/errors_failed_transactions.ts @@ -40,7 +40,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { } ); - registry.when( + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/109660 + registry.when.skip( 'correlations errors failed transactions with data and default args', { config: 'trial', archives: ['apm_8.0.0'] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts new file mode 100644 index 0000000000000..2d014b3ee1e6b --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts @@ -0,0 +1,238 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; +import { PartialSearchRequest } from '../../../../plugins/apm/server/lib/search_strategies/correlations/search_strategy'; +import { parseBfetchResponse } from '../../common/utils/parse_b_fetch'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const retry = getService('retry'); + const supertest = getService('supertest'); + + const getRequestBody = () => { + const partialSearchRequest: PartialSearchRequest = { + params: { + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + kuery: '', + }, + }; + + return { + batch: [ + { + request: partialSearchRequest, + options: { strategy: 'apmFailedTransactionsCorrelationsSearchStrategy' }, + }, + ], + }; + }; + + registry.when('on trial license without data', { config: 'trial', archives: [] }, () => { + it('queries the search strategy and returns results', async () => { + const intialResponse = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(getRequestBody()); + + expect(intialResponse.status).to.eql( + 200, + `Expected status to be '200', got '${intialResponse.status}'` + ); + expect(intialResponse.body).to.eql( + {}, + `Expected response body to be an empty object, actual response is in the text attribute. Got: '${JSON.stringify( + intialResponse.body + )}'` + ); + + const body = parseBfetchResponse(intialResponse)[0]; + + expect(typeof body.result).to.be('object'); + const { result } = body; + + expect(typeof result?.id).to.be('string'); + + // pass on id for follow up queries + const searchStrategyId = result.id; + + // follow up request body including search strategy ID + const reqBody = getRequestBody(); + reqBody.batch[0].request.id = searchStrategyId; + + let followUpResponse: Record = {}; + + // continues querying until the search strategy finishes + await retry.waitForWithTimeout( + 'search strategy eventually completes and returns full results', + 5000, + async () => { + const response = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(reqBody); + + followUpResponse = parseBfetchResponse(response)[0]; + + return ( + followUpResponse?.result?.isRunning === false || followUpResponse?.error !== undefined + ); + } + ); + + expect(followUpResponse?.error).to.eql( + undefined, + `search strategy should not return an error, got: ${JSON.stringify( + followUpResponse?.error + )}` + ); + + const followUpResult = followUpResponse.result; + expect(followUpResult?.isRunning).to.eql(false, 'search strategy should not be running'); + expect(followUpResult?.isPartial).to.eql( + false, + 'search strategy result should not be partial' + ); + expect(followUpResult?.id).to.eql( + searchStrategyId, + 'search strategy id should match original id' + ); + expect(followUpResult?.isRestored).to.eql( + true, + 'search strategy response should be restored' + ); + expect(followUpResult?.loaded).to.eql(100, 'loaded state should be 100'); + expect(followUpResult?.total).to.eql(100, 'total state should be 100'); + + expect(typeof followUpResult?.rawResponse).to.be('object'); + + const { rawResponse: finalRawResponse } = followUpResult; + + expect(typeof finalRawResponse?.took).to.be('number'); + + expect(finalRawResponse?.values.length).to.eql( + 0, + `Expected 0 identified correlations, got ${finalRawResponse?.values.length}.` + ); + }); + }); + + registry.when('on trial license with data', { config: 'trial', archives: ['8.0.0'] }, () => { + it('queries the search strategy and returns results', async () => { + const intialResponse = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(getRequestBody()); + + expect(intialResponse.status).to.eql( + 200, + `Expected status to be '200', got '${intialResponse.status}'` + ); + expect(intialResponse.body).to.eql( + {}, + `Expected response body to be an empty object, actual response is in the text attribute. Got: '${JSON.stringify( + intialResponse.body + )}'` + ); + + const body = parseBfetchResponse(intialResponse)[0]; + + expect(typeof body.result).to.be('object'); + const { result } = body; + + expect(typeof result?.id).to.be('string'); + + // pass on id for follow up queries + const searchStrategyId = result.id; + + // follow up request body including search strategy ID + const reqBody = getRequestBody(); + reqBody.batch[0].request.id = searchStrategyId; + + let followUpResponse: Record = {}; + + // continues querying until the search strategy finishes + await retry.waitForWithTimeout( + 'search strategy eventually completes and returns full results', + 5000, + async () => { + const response = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(reqBody); + + followUpResponse = parseBfetchResponse(response)[0]; + + return ( + followUpResponse?.result?.isRunning === false || followUpResponse?.error !== undefined + ); + } + ); + + expect(followUpResponse?.error).to.eql( + undefined, + `search strategy should not return an error, got: ${JSON.stringify( + followUpResponse?.error + )}` + ); + + const followUpResult = followUpResponse.result; + expect(followUpResult?.isRunning).to.eql(false, 'search strategy should not be running'); + expect(followUpResult?.isPartial).to.eql( + false, + 'search strategy result should not be partial' + ); + expect(followUpResult?.id).to.eql( + searchStrategyId, + 'search strategy id should match original id' + ); + expect(followUpResult?.isRestored).to.eql( + true, + 'search strategy response should be restored' + ); + expect(followUpResult?.loaded).to.eql(100, 'loaded state should be 100'); + expect(followUpResult?.total).to.eql(100, 'total state should be 100'); + + expect(typeof followUpResult?.rawResponse).to.be('object'); + + const { rawResponse: finalRawResponse } = followUpResult; + + expect(typeof finalRawResponse?.took).to.be('number'); + expect(finalRawResponse?.percentileThresholdValue).to.be(undefined); + expect(finalRawResponse?.overallHistogram).to.be(undefined); + + expect(finalRawResponse?.values.length).to.eql( + 43, + `Expected 43 identified correlations, got ${finalRawResponse?.values.length}.` + ); + + expect(finalRawResponse?.log.map((d: string) => d.split(': ')[1])).to.eql([ + 'Identified 68 fieldCandidates.', + 'Identified correlations for 68 fields out of 68 candidates.', + 'Identified 43 significant correlations relating to failed transactions.', + ]); + + const sortedCorrelations = finalRawResponse?.values.sort(); + const correlation = sortedCorrelations[0]; + + expect(typeof correlation).to.be('object'); + expect(correlation?.key).to.be('HTTP 5xx'); + expect(correlation?.doc_count).to.be(31); + expect(correlation?.score).to.be(100.17736139032642); + expect(correlation?.bg_count).to.be(60); + expect(correlation?.fieldName).to.be('transaction.result'); + expect(correlation?.fieldValue).to.be('HTTP 5xx'); + expect(typeof correlation?.pValue).to.be('number'); + expect(typeof correlation?.normalizedScore).to.be('number'); + expect(typeof correlation?.failurePercentage).to.be('number'); + expect(typeof correlation?.successPercentage).to.be('number'); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts b/x-pack/test/apm_api_integration/tests/correlations/latency.ts similarity index 97% rename from x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts rename to x-pack/test/apm_api_integration/tests/correlations/latency.ts index e41a830735a89..32ca71694626f 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/latency.ts @@ -6,18 +6,10 @@ */ import expect from '@kbn/expect'; -import request from 'superagent'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; - import { PartialSearchRequest } from '../../../../plugins/apm/server/lib/search_strategies/correlations/search_strategy'; - -function parseBfetchResponse(resp: request.Response): Array> { - return resp.text - .trim() - .split('\n') - .map((item) => JSON.parse(item)); -} +import { parseBfetchResponse } from '../../common/utils/parse_b_fetch'; export default function ApiTest({ getService }: FtrProviderContext) { const retry = getService('retry'); diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency_slow_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/latency_slow_transactions.ts index fc56615b3b13a..dac9ed70bc483 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/latency_slow_transactions.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/latency_slow_transactions.ts @@ -43,7 +43,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { } ); - registry.when( + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/109583 + registry.when.skip( 'correlations latency slow transactions with data and default args', { config: 'trial', archives: ['apm_8.0.0'] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index 0e76a4ed86688..c8a57bc613a92 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -28,12 +28,17 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./alerts/rule_registry')); }); + // correlations describe('correlations/latency_slow_transactions', function () { loadTestFile(require.resolve('./correlations/latency_slow_transactions')); }); - describe('correlations/latency_ml', function () { - loadTestFile(require.resolve('./correlations/latency_ml')); + describe('correlations/failed_transactions', function () { + loadTestFile(require.resolve('./correlations/failed_transactions')); + }); + + describe('correlations/latency', function () { + loadTestFile(require.resolve('./correlations/latency')); }); describe('correlations/latency_overall', function () { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts index cd209da25e883..58df5bc3ff9e1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -193,7 +193,7 @@ export default ({ getService }: FtrProviderContext) => { index: '.ml-anomalies-custom-linux_anomalous_network_activity_ecs', depth: 0, }, - reason: `Alert Test ML rule created at ${signal._source['@timestamp']} with a critical severity and risk score of 50 by root on mothra.`, + reason: `Alert Test ML rule created with a critical severity and risk score of 50 by root on mothra.`, original_time: '2020-11-16T22:58:08.000Z', }, all_field_values: [ diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index a85b5ba764d82..46fce77678abf 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -275,7 +275,7 @@ export default ({ getService }: FtrProviderContext) => { depth: 0, }, ], - reason: `Alert Query with a rule id created at ${fullSignal['@timestamp']} with a high severity and risk score of 55 by root on zeek-sensor-amsterdam.`, + reason: `Alert Query with a rule id created with a high severity and risk score of 55 by root on zeek-sensor-amsterdam.`, rule: fullSignal.signal.rule, status: 'open', }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 1c1e2b9966b7f..a3315da2c142e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -362,7 +362,7 @@ export default ({ getService }: FtrProviderContext) => { }, }, signal: { - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1 on suricata-zeek-sensor-toronto.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1 on suricata-zeek-sensor-toronto.`, rule: fullSignal.signal.rule, original_time: fullSignal.signal.original_time, status: 'open', @@ -497,7 +497,7 @@ export default ({ getService }: FtrProviderContext) => { }, }, signal: { - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1 on suricata-zeek-sensor-toronto.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1 on suricata-zeek-sensor-toronto.`, rule: fullSignal.signal.rule, original_time: fullSignal.signal.original_time, status: 'open', @@ -662,7 +662,7 @@ export default ({ getService }: FtrProviderContext) => { }, }, signal: { - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1 by root on zeek-sensor-amsterdam.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1 by root on zeek-sensor-amsterdam.`, rule: fullSignal.signal.rule, group: fullSignal.signal.group, original_time: fullSignal.signal.original_time, @@ -753,7 +753,7 @@ export default ({ getService }: FtrProviderContext) => { status: 'open', depth: 2, group: source.signal.group, - reason: `Alert Signal Testing Query created at ${source['@timestamp']} with a high severity and risk score of 1.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1.`, rule: source.signal.rule, ancestors: [ { @@ -872,7 +872,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], status: 'open', - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1.`, rule: fullSignal.signal.rule, original_time: fullSignal.signal.original_time, depth: 1, @@ -1010,7 +1010,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], status: 'open', - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1.`, rule: fullSignal.signal.rule, original_time: fullSignal.signal.original_time, depth: 1, @@ -1094,7 +1094,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], status: 'open', - reason: `Alert Signal Testing Query created at ${fullSignal['@timestamp']} with a high severity and risk score of 1.`, + reason: `Alert Signal Testing Query created with a high severity and risk score of 1.`, rule: fullSignal.signal.rule, original_time: fullSignal.signal.original_time, depth: 1, @@ -1686,7 +1686,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], status: 'open', - reason: `Alert boot created at ${fullSignal['@timestamp']} with a high severity and risk score of 1 on zeek-sensor-amsterdam.`, + reason: `Alert boot created with a high severity and risk score of 1 on zeek-sensor-amsterdam.`, rule: { ...fullSignal.signal.rule, name: 'boot', diff --git a/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts b/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts new file mode 100644 index 0000000000000..969a543e5f97d --- /dev/null +++ b/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const find = getService('find'); + const retry = getService('retry'); + const spacesService = getService('spaces'); + const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security']); + const testSubjects = getService('testSubjects'); + const appsMenu = getService('appsMenu'); + + const testData = { + correlationsTab: 'Failed transaction correlations', + serviceName: 'opbeans-go', + transactionsTab: 'Transactions', + transaction: 'GET /api/stats', + }; + + describe('failed transactions correlations', () => { + describe('space with no features disabled', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); + await spacesService.create({ + id: 'custom_space', + name: 'custom_space', + disabledFeatures: [], + }); + }); + + after(async () => { + await spacesService.delete('custom_space'); + }); + + it('shows apm navlink', async () => { + await PageObjects.common.navigateToApp('home', { + basePath: '/s/custom_space', + }); + const navLinks = (await appsMenu.readLinks()).map((link) => link.text); + expect(navLinks).to.contain('APM'); + }); + + it('can navigate to APM app', async () => { + await PageObjects.common.navigateToApp('apm'); + + await retry.try(async () => { + await testSubjects.existOrFail('apmMainContainer', { + timeout: 10000, + }); + + const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); + const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); + expect(apmMainContainerTextItems).to.contain('No services found'); + }); + }); + + it('sets the timePicker to return data', async () => { + await PageObjects.timePicker.timePickerExists(); + + const fromTime = 'Jul 29, 2019 @ 00:00:00.000'; + const toTime = 'Jul 30, 2019 @ 00:00:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + + await retry.try(async () => { + const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); + const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); + + expect(apmMainContainerTextItems).to.not.contain('No services found'); + + expect(apmMainContainerTextItems).to.contain('opbeans-go'); + expect(apmMainContainerTextItems).to.contain('opbeans-node'); + expect(apmMainContainerTextItems).to.contain('opbeans-ruby'); + expect(apmMainContainerTextItems).to.contain('opbeans-python'); + expect(apmMainContainerTextItems).to.contain('opbeans-dotnet'); + expect(apmMainContainerTextItems).to.contain('opbeans-java'); + + expect(apmMainContainerTextItems).to.contain('development'); + + const items = await testSubjects.findAll('apmServiceListAppLink'); + expect(items.length).to.be(6); + }); + }); + + it(`navigates to the 'opbeans-go' service overview page`, async function () { + await find.clickByDisplayedLinkText(testData.serviceName); + + await retry.try(async () => { + const apmMainTemplateHeaderServiceName = await testSubjects.getVisibleTextAll( + 'apmMainTemplateHeaderServiceName' + ); + expect(apmMainTemplateHeaderServiceName).to.contain(testData.serviceName); + }); + }); + + it('navigates to the transactions tab', async function () { + await find.clickByDisplayedLinkText(testData.transactionsTab); + + await retry.try(async () => { + const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); + const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); + + expect(apmMainContainerTextItems).to.contain(testData.transaction); + }); + }); + + it(`navigates to the 'GET /api/stats' transactions`, async function () { + await find.clickByDisplayedLinkText(testData.transaction); + + await retry.try(async () => { + const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); + const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); + + expect(apmMainContainerTextItems).to.contain(testData.transaction); + expect(apmMainContainerTextItems).to.contain(testData.correlationsTab); + }); + }); + + it('shows the failed transactions correlations tab', async function () { + await testSubjects.click('apmFailedTransactionsCorrelationsTabButton'); + + await retry.try(async () => { + await testSubjects.existOrFail('apmFailedTransactionsCorrelationsTabContent'); + }); + }); + + it('loads the failed transactions correlations results', async function () { + await retry.try(async () => { + const apmFailedTransactionsCorrelationsTabTitle = await testSubjects.getVisibleText( + 'apmFailedTransactionsCorrelationsTabTitle' + ); + expect(apmFailedTransactionsCorrelationsTabTitle).to.be('Failed transactions'); + + // Assert that the data fully loaded to 100% + const apmFailedTransactionsCorrelationsProgressTitle = await testSubjects.getVisibleText( + 'apmCorrelationsProgressTitle' + ); + expect(apmFailedTransactionsCorrelationsProgressTitle).to.be('Progress: 100%'); + + // Assert that results for the given service didn't find any correlations + const apmCorrelationsTable = await testSubjects.getVisibleText('apmCorrelationsTable'); + expect(apmCorrelationsTable).to.be( + 'No significant correlations\nCorrelations will only be identified if they have significant impact.\nTry selecting another time range or remove any added filter.' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/apm/correlations/index.ts b/x-pack/test/functional/apps/apm/correlations/index.ts index ae5f594e54400..abf74910f4e8d 100644 --- a/x-pack/test/functional/apps/apm/correlations/index.ts +++ b/x-pack/test/functional/apps/apm/correlations/index.ts @@ -9,7 +9,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('correlations', function () { - this.tags('skipFirefox'); + this.tags(['skipFirefox', 'apm']); loadTestFile(require.resolve('./latency_correlations')); + loadTestFile(require.resolve('./failed_transaction_correlations')); }); } diff --git a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts index 3f743c378b7c3..af3633798133b 100644 --- a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts +++ b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts @@ -144,10 +144,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('loads the correlation results', async function () { await retry.try(async () => { // Assert that the data fully loaded to 100% - const apmCorrelationsLatencyCorrelationsProgressTitle = await testSubjects.getVisibleText( - 'apmCorrelationsLatencyCorrelationsProgressTitle' + const apmLatencyCorrelationsProgressTitle = await testSubjects.getVisibleText( + 'apmCorrelationsProgressTitle' ); - expect(apmCorrelationsLatencyCorrelationsProgressTitle).to.be('Progress: 100%'); + expect(apmLatencyCorrelationsProgressTitle).to.be('Progress: 100%'); // Assert that the Correlations Chart and its header are present const apmCorrelationsLatencyCorrelationsChartTitle = await testSubjects.getVisibleText( diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js deleted file mode 100644 index 2adf13db26250..0000000000000 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; - -export default function ({ getPageObjects, getService }) { - const testSubjects = getService('testSubjects'); - const esArchiver = getService('esArchiver'); - const dashboardPanelActions = getService('dashboardPanelActions'); - const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens']); - - // FLAKY: https://github.com/elastic/kibana/issues/102366 - describe.skip('empty dashboard', function () { - before(async () => { - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/lens/basic'); - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.preserveCrossAppState(); - await PageObjects.dashboard.clickNewDashboard(); - }); - - after(async () => { - await PageObjects.dashboard.gotoDashboardLandingPage(); - }); - - it('adds Lens visualization to empty dashboard', async () => { - const title = 'Dashboard Test Lens'; - await PageObjects.lens.createAndAddLensFromDashboard({ title, redirectToOrigin: true }); - await PageObjects.dashboard.waitForRenderComplete(); - await testSubjects.exists(`embeddablePanelHeading-${title}`); - }); - - it('redirects via save and return button after edit', async () => { - await dashboardPanelActions.openContextMenu(); - await dashboardPanelActions.clickEdit(); - await PageObjects.lens.saveAndReturn(); - }); - - it('redirects via save as button after edit, renaming itself', async () => { - const newTitle = 'wowee, looks like I have a new title'; - const originalPanelCount = await PageObjects.dashboard.getPanelCount(); - await PageObjects.dashboard.waitForRenderComplete(); - await dashboardPanelActions.openContextMenu(); - await dashboardPanelActions.clickEdit(); - await PageObjects.lens.save(newTitle, false, true); - await PageObjects.dashboard.waitForRenderComplete(); - const newPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(newPanelCount).to.eql(originalPanelCount); - const titles = await PageObjects.dashboard.getPanelTitles(); - expect(titles.indexOf(newTitle)).to.not.be(-1); - }); - - it('redirects via save as button after edit, adding a new panel', async () => { - const newTitle = 'wowee, my title just got cooler'; - const originalPanelCount = await PageObjects.dashboard.getPanelCount(); - await PageObjects.dashboard.waitForRenderComplete(); - await dashboardPanelActions.openContextMenu(); - await dashboardPanelActions.clickEdit(); - await PageObjects.lens.save(newTitle, true, true); - await PageObjects.dashboard.waitForRenderComplete(); - const newPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(newPanelCount).to.eql(originalPanelCount + 1); - const titles = await PageObjects.dashboard.getPanelTitles(); - expect(titles.indexOf(newTitle)).to.not.be(-1); - }); - - it('loses originatingApp connection after save as when redirectToOrigin is false', async () => { - await PageObjects.dashboard.saveDashboard('empty dashboard test'); - await PageObjects.dashboard.switchToEditMode(); - const newTitle = 'wowee, my title just got cooler again'; - await PageObjects.dashboard.waitForRenderComplete(); - await dashboardPanelActions.openContextMenu(); - await dashboardPanelActions.clickEdit(); - await PageObjects.lens.save(newTitle, true, false); - await PageObjects.lens.notLinkedToOriginatingApp(); - await PageObjects.common.navigateToApp('dashboard'); - }); - - it('loses originatingApp connection after first save when redirectToOrigin is false', async () => { - const title = 'non-dashboard Test Lens'; - await PageObjects.dashboard.loadSavedDashboard('empty dashboard test'); - await PageObjects.dashboard.switchToEditMode(); - await PageObjects.lens.createAndAddLensFromDashboard({ title }); - await PageObjects.lens.notLinkedToOriginatingApp(); - await PageObjects.common.navigateToApp('dashboard'); - }); - }); -} diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js deleted file mode 100644 index 74f4dca06c717..0000000000000 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; - -export default function ({ getService, getPageObjects }) { - const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const log = getService('log'); - const pieChart = getService('pieChart'); - const security = getService('security'); - const testSubjects = getService('testSubjects'); - const dashboardPanelActions = getService('dashboardPanelActions'); - const appsMenu = getService('appsMenu'); - const filterBar = getService('filterBar'); - const PageObjects = getPageObjects([ - 'security', - 'common', - 'discover', - 'dashboard', - 'header', - 'settings', - 'timePicker', - 'share', - ]); - const dashboardName = 'Dashboard View Mode Test Dashboard'; - - describe('Dashboard View Mode', function () { - this.tags(['skipFirefox']); - - before('initialize tests', async () => { - log.debug('Dashboard View Mode:initTests'); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/dashboard_view_mode' - ); - await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); - await browser.setWindowSize(1600, 1000); - - await PageObjects.common.navigateToApp('dashboard'); - }); - - after(async () => { - await kibanaServer.importExport.unload( - 'x-pack/test/functional/fixtures/kbn_archiver/dashboard_view_mode' - ); - const types = [ - 'search', - 'dashboard', - 'visualization', - 'search-session', - 'core-usage-stats', - 'event_loop_delays_daily', - 'search-telemetry', - 'core-usage-stats', - ]; - await kibanaServer.savedObjects.clean({ types }); - }); - - describe('Dashboard viewer', () => { - after(async () => { - await security.testUser.restoreDefaults(); - }); - - it('shows only the dashboard app link', async () => { - await security.testUser.setRoles(['test_logstash_reader', 'kibana_dashboard_only_user']); - await PageObjects.header.waitUntilLoadingHasFinished(); - const appLinks = await appsMenu.readLinks(); - expect(appLinks).to.have.length(1); - expect(appLinks[0]).to.have.property('text', 'Dashboard'); - }); - - it('shows the dashboard landing page by default', async () => { - const currentUrl = await browser.getCurrentUrl(); - console.log('url: ', currentUrl); - expect(currentUrl).to.contain('dashboards'); - }); - - it('does not show the create dashboard button', async () => { - const createNewButtonExists = await testSubjects.exists('newItemButton'); - expect(createNewButtonExists).to.be(false); - }); - - it('opens a dashboard up', async () => { - await PageObjects.dashboard.loadSavedDashboard(dashboardName); - const onDashboardLandingPage = await PageObjects.dashboard.onDashboardLandingPage(); - expect(onDashboardLandingPage).to.be(false); - }); - - it('can filter on a visualization', async () => { - await PageObjects.timePicker.setHistoricalDataRange(); - await pieChart.filterOnPieSlice(); - const filterCount = await filterBar.getFilterCount(); - expect(filterCount).to.equal(1); - }); - - it('shows the full screen menu item', async () => { - const fullScreenMenuItemExists = await testSubjects.exists('dashboardFullScreenMode'); - expect(fullScreenMenuItemExists).to.be(true); - }); - - it('does not show the edit menu item', async () => { - const editMenuItemExists = await testSubjects.exists('dashboardEditMode'); - expect(editMenuItemExists).to.be(false); - }); - - it('does not show the view menu item', async () => { - const viewMenuItemExists = await testSubjects.exists('dashboardViewOnlyMode'); - expect(viewMenuItemExists).to.be(false); - }); - - it('does not show the reporting menu item', async () => { - const reportingMenuItemExists = await testSubjects.exists('topNavReportingLink'); - expect(reportingMenuItemExists).to.be(false); - }); - - it('shows the sharing menu item', async () => { - const shareMenuItemExists = await testSubjects.exists('shareTopNavButton'); - expect(shareMenuItemExists).to.be(true); - }); - - it(`Permalinks doesn't show create short-url button`, async () => { - await PageObjects.share.openShareMenuItem('Permalinks'); - await PageObjects.share.createShortUrlMissingOrFail(); - }); - - it('does not show the visualization edit icon', async () => { - await dashboardPanelActions.expectMissingEditPanelAction(); - }); - - it('does not show the visualization delete icon', async () => { - await dashboardPanelActions.expectMissingRemovePanelAction(); - }); - - it('shows the timepicker', async () => { - const timePickerExists = await PageObjects.timePicker.timePickerExists(); - expect(timePickerExists).to.be(true); - }); - - it('can paginate on a saved search', async () => { - await PageObjects.dashboard.expectToolbarPaginationDisplayed({ displayed: true }); - }); - - it('is loaded for a user who is assigned a non-dashboard mode role', async () => { - await security.testUser.setRoles([ - 'test_logstash_reader', - 'kibana_dashboard_only_user', - 'kibana_admin', - ]); - await PageObjects.header.waitUntilLoadingHasFinished(); - - if (await appsMenu.linkExists('Stack Management')) { - throw new Error('Expected management nav link to not be shown'); - } - }); - - it('is not loaded for a user who is assigned a superuser role', async () => { - await security.testUser.setRoles(['kibana_dashboard_only_user', 'superuser']); - await PageObjects.header.waitUntilLoadingHasFinished(); - - if (!(await appsMenu.linkExists('Stack Management'))) { - throw new Error('Expected management nav link to be shown'); - } - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 90b34702767e4..255f2c49e5621 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -87,7 +87,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/106660 + // FLAKY: https://github.com/elastic/kibana/issues/106650 describe.skip('Saved Views', () => { before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs')); after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); diff --git a/x-pack/test/functional/apps/infra/metrics_explorer.ts b/x-pack/test/functional/apps/infra/metrics_explorer.ts index 5a8d7da628259..6b1873b7b5e39 100644 --- a/x-pack/test/functional/apps/infra/metrics_explorer.ts +++ b/x-pack/test/functional/apps/infra/metrics_explorer.ts @@ -87,8 +87,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/106651 - describe.skip('Saved Views', () => { + describe('Saved Views', () => { before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs')); after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); describe('save functionality', () => { diff --git a/x-pack/test/functional/apps/lens/chart_data.ts b/x-pack/test/functional/apps/lens/chart_data.ts index 96482b56fdbe1..61be116003ccd 100644 --- a/x-pack/test/functional/apps/lens/chart_data.ts +++ b/x-pack/test/functional/apps/lens/chart_data.ts @@ -125,11 +125,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend expect(debugState.legend!.items).to.eql([ - { color: '#6092c0', key: '≥ 5,722.775', name: '≥ 5,722.775' }, - { color: '#6092c0', key: '≥ 5,722.77', name: '≥ 5,722.77' }, - { color: '#a8bfda', key: '≥ 8,529.22', name: '≥ 8,529.22' }, - { color: '#ebeff5', key: '≥ 11,335.66', name: '≥ 11,335.66' }, - { color: '#ecb385', key: '≥ 14,142.11', name: '≥ 14,142.11' }, + { color: '#6092c0', key: '5,722.77 - 8,529.22', name: '5,722.77 - 8,529.22' }, + { color: '#a8bfda', key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66' }, + { color: '#ebeff5', key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11' }, + { color: '#ecb385', key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55' }, { color: '#e7664c', key: '≥ 16,948.55', name: '≥ 16,948.55' }, ]); }); diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts index c8a0e171d5a79..e7b7ba18d62fb 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -8,9 +8,8 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }: FtrProviderContext) { +export default function ({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); - const retry = getService('retry'); describe('lens drag and drop tests', () => { describe('basic drag and drop', () => { @@ -19,9 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); - }); + await PageObjects.lens.dragFieldToWorkspace('@timestamp'); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( '@timestamp' diff --git a/x-pack/test/functional/apps/lens/heatmap.ts b/x-pack/test/functional/apps/lens/heatmap.ts index 0972f4809def1..0655b09e84d56 100644 --- a/x-pack/test/functional/apps/lens/heatmap.ts +++ b/x-pack/test/functional/apps/lens/heatmap.ts @@ -60,11 +60,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#6092c0' }, - { key: '≥ 5,722.77', name: '≥ 5,722.77', color: '#6092c0' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#a8bfda' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#ebeff5' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#ecb385' }, + { key: '5,722.77 - 8,529.22', name: '5,722.77 - 8,529.22', color: '#6092c0' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#a8bfda' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#ebeff5' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#ecb385' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#e7664c' }, ]); }); @@ -85,11 +84,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend has changed expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#6092c0' }, - { key: '≥ 7,126', name: '≥ 7,126', color: '#6092c0' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#a8bfda' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#ebeff5' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#ecb385' }, + { key: '7,126 - 8,529.22', name: '7,126 - 8,529.22', color: '#6092c0' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#a8bfda' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#ebeff5' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#ecb385' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#e7664c' }, ]); }); @@ -106,11 +104,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend has changed expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#6092c0' }, - { key: '≥ 7,126', name: '≥ 7,126', color: '#6092c0' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#a8bfda' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#ebeff5' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#ecb385' }, + { key: '7,126 - 8,529.22', name: '7,126 - 8,529.22', color: '#6092c0' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#a8bfda' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#ebeff5' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#ecb385' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#e7664c' }, ]); }); @@ -129,11 +126,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend has changed expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#6092c0' }, - { key: '≥ 0', name: '≥ 0', color: '#6092c0' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#a8bfda' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#ebeff5' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#ecb385' }, + { key: '0 - 8,529.22', name: '0 - 8,529.22', color: '#6092c0' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#a8bfda' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#ebeff5' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#ecb385' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#e7664c' }, ]); }); @@ -150,11 +146,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend has changed expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#209280' }, - { key: '≥ 5,722.77', name: '≥ 5,722.77', color: '#209280' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#54b399' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#d6bf57' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#e7664c' }, + { key: '5,722.77 - 8,529.22', name: '5,722.77 - 8,529.22', color: '#209280' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#54b399' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#d6bf57' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#e7664c' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#cc5642' }, ]); }); @@ -171,11 +166,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // assert legend has not changed expect(debugState.legend!.items).to.eql([ - { key: '≥ 5,722.775', name: '≥ 5,722.775', color: '#209280' }, - { key: '≥ 5,722.77', name: '≥ 5,722.77', color: '#209280' }, - { key: '≥ 8,529.22', name: '≥ 8,529.22', color: '#54b399' }, - { key: '≥ 11,335.66', name: '≥ 11,335.66', color: '#d6bf57' }, - { key: '≥ 14,142.11', name: '≥ 14,142.11', color: '#e7664c' }, + { key: '5,722.77 - 8,529.22', name: '5,722.77 - 8,529.22', color: '#209280' }, + { key: '8,529.22 - 11,335.66', name: '8,529.22 - 11,335.66', color: '#54b399' }, + { key: '11,335.66 - 14,142.11', name: '11,335.66 - 14,142.11', color: '#d6bf57' }, + { key: '14,142.11 - 16,948.55', name: '14,142.11 - 16,948.55', color: '#e7664c' }, { key: '≥ 16,948.55', name: '≥ 16,948.55', color: '#cc5642' }, ]); }); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 9a440f758ea26..04bc12687a3b3 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -66,6 +66,7 @@ export default function ({ getService }: FtrProviderContext) { ], runtimeFieldsEditorContent: ['{', ' "uppercase_y": {', ' "type": "keyword",'], row: { + memoryStatus: 'ok', type: 'classification', status: 'stopped', progress: '100', @@ -242,6 +243,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, @@ -280,6 +282,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index ef86de29febc7..14edb19cd9adb 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -81,6 +81,7 @@ export default function ({ getService }: FtrProviderContext) { ' "type": "keyword",', ], row: { + memoryStatus: 'ok', type: 'outlier_detection', status: 'stopped', progress: '100', @@ -259,6 +260,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, @@ -297,6 +299,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts index b2bd54ae639ae..6b7b7f2f87fd0 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts @@ -59,6 +59,7 @@ export default function ({ getService }: FtrProviderContext) { ], runtimeFieldsEditorContent: ['{', ' "uppercase_stab": {', ' "type": "keyword",'], row: { + memoryStatus: 'ok', type: 'regression', status: 'stopped', progress: '100', @@ -231,6 +232,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, @@ -268,6 +270,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { id: testData.jobId, description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, sourceIndex: testData.source, destinationIndex: testData.destinationIndex, type: testData.expected.row.type, diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts index 141b5840aab44..f120ab0b450dc 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts @@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { this.tags(['mlqa', 'skipFirefox']); loadTestFile(require.resolve('./synchronize')); + loadTestFile(require.resolve('./manage_spaces')); }); } diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts new file mode 100644 index 0000000000000..9953bc4832b57 --- /dev/null +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts @@ -0,0 +1,251 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const spacesService = getService('spaces'); + + interface TestData { + suiteTitle: string; + initialSpace: string; + adJobId: string; + dfaJobId: string; + spacesToAdd: string[]; + removeInitialSpace: boolean; + assignToAllSpaces: boolean; + } + + const spaceIds = { + idSpaceDefault: 'default', + idSpace1: 'space1', + }; + + // each test run performs all spaces operations and validations for AD and + // DFA in parallel to save test execution time when switching spaces and pages + const testDataList: TestData[] = [ + { + suiteTitle: `add one space`, + initialSpace: spaceIds.idSpaceDefault, + adJobId: `ad_job_1_${Date.now()}`, + dfaJobId: `dfa_job_1_${Date.now()}`, + spacesToAdd: [spaceIds.idSpace1], + removeInitialSpace: false, + assignToAllSpaces: false, + }, + { + suiteTitle: `add one space and remove initial space`, + initialSpace: spaceIds.idSpaceDefault, + adJobId: `ad_job_2_${Date.now()}`, + dfaJobId: `dfa_job_2_${Date.now()}`, + spacesToAdd: [spaceIds.idSpace1], + removeInitialSpace: true, + assignToAllSpaces: false, + }, + { + suiteTitle: `assign to all spaces`, + initialSpace: spaceIds.idSpace1, + adJobId: `ad_job_3_${Date.now()}`, + dfaJobId: `dfa_job_3_${Date.now()}`, + spacesToAdd: [], + removeInitialSpace: false, + assignToAllSpaces: true, + }, + ]; + + async function assertJobsDisplayedInSpace( + adJobId: string, + dfaJobId: string, + spaceId: string, + shouldBeDisplayed: boolean + ) { + await ml.testExecution.logTestStep( + `AD job ${adJobId} and DFA job ${dfaJobId} should${ + shouldBeDisplayed ? '' : ' not' + } be displayed in space ${spaceId}` + ); + await ml.commonUI.changeToSpace(spaceId); + await ml.navigation.navigateToMlViaAppsMenu(); // use apps menu to keep the selected space + + // AD + await ml.navigation.navigateToAnomalyDetection(); + await ml.jobTable.filterWithSearchString(adJobId, shouldBeDisplayed ? 1 : 0); + + // DFA + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalyticsTable.assertAnalyticsJobDisplayedInTable( + dfaJobId, + shouldBeDisplayed + ); + } + + async function selectSpaces(testData: TestData) { + if (testData.assignToAllSpaces) { + await ml.stackManagementJobs.selectShareToAllSpaces(); + } else { + await ml.stackManagementJobs.selectShareToExplicitSpaces(); + + for (const spaceId of testData.spacesToAdd) { + await ml.stackManagementJobs.toggleSpaceSelectionRow(spaceId, true); + } + if (testData.removeInitialSpace) { + await ml.stackManagementJobs.toggleSpaceSelectionRow(testData.initialSpace, false); + } + } + } + + describe('manage spaces', function () { + this.tags(['mlqa']); + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); + + await ml.testResources.setKibanaTimeZoneToUTC(); + await ml.securityUI.loginAsMlPowerUser(); + + for (const spaceId of Object.values(spaceIds)) { + if (spaceId !== 'default') { + await spacesService.create({ + id: spaceId, + name: spaceId, + disabledFeatures: [], + initials: `${spaceId.slice(-1)}`, + }); + } + } + }); + + after(async () => { + for (const spaceId of Object.values(spaceIds)) { + if (spaceId !== 'default') { + await spacesService.delete(spaceId); + } + } + await ml.testResources.cleanMLSavedObjects(); + await ml.api.cleanMlIndices(); + }); + + for (const testData of testDataList) { + describe(testData.suiteTitle, function () { + before(async () => { + await ml.api.createAnomalyDetectionJob( + ml.commonConfig.getADFqSingleMetricJobConfig(testData.adJobId), + testData.initialSpace + ); + await ml.api.createDataFrameAnalyticsJob( + ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(testData.dfaJobId), + testData.initialSpace + ); + + // this is already set during login but is reset between sub-suites + await browser.setLocalStorageItem('home:welcome:show', 'false'); + }); + + after(async () => { + await ml.api.deleteAnomalyDetectionJobES(testData.adJobId); + await ml.api.deleteDataFrameAnalyticsJobES(testData.dfaJobId); + }); + + it('should display original job only in assigned spaces', async () => { + for (const spaceId of Object.values(spaceIds)) { + await assertJobsDisplayedInSpace( + testData.adJobId, + testData.dfaJobId, + spaceId, + spaceId === testData.initialSpace + ); + } + }); + + it('should display the initial job space correctly in the AD and DFA jobs lists', async () => { + await ml.commonUI.changeToSpace(testData.initialSpace); + await ml.navigation.navigateToStackManagementViaAppsMenu(); // use apps menu to keep the selected space + await ml.navigation.navigateToStackManagementJobsListPage(); + + // AD + await ml.jobTable.filterWithSearchString(testData.adJobId); + await ml.stackManagementJobs.assertADJobRowSpaces(testData.adJobId, [ + testData.initialSpace, + ]); + + // DFA + await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab(); + await ml.stackManagementJobs.assertDFAJobRowSpaces(testData.dfaJobId, [ + testData.initialSpace, + ]); + }); + + it('should edit job space assignment', async () => { + // AD + await ml.navigation.navigateToStackManagementJobsListPageAnomalyDetectionTab(); + await ml.stackManagementJobs.openADJobSpacesFlyout(testData.adJobId); + await selectSpaces(testData); + await ml.stackManagementJobs.saveAndCloseSpacesFlyout(); + + // DFA + await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab(); + await ml.stackManagementJobs.openDFAJobSpacesFlyout(testData.dfaJobId); + await selectSpaces(testData); + await ml.stackManagementJobs.saveAndCloseSpacesFlyout(); + }); + + it('should display the updated job spaces correctly in the jobs list', async () => { + if (testData.removeInitialSpace) { + // initial space has been removed so job is not displayed here anymore to + // validate the spaces, so we're changing to the first added space + await ml.commonUI.changeToSpace(testData.spacesToAdd[0]); + await ml.navigation.navigateToStackManagementViaAppsMenu(); // use apps menu to keep the selected space + await ml.navigation.navigateToStackManagementJobsListPage(); + } + + const expectedJobRowSpaces = testData.assignToAllSpaces + ? ['*'] + : [ + ...testData.spacesToAdd, + ...(testData.removeInitialSpace ? [] : [testData.initialSpace]), + ]; + + // AD + await ml.navigation.navigateToStackManagementJobsListPageAnomalyDetectionTab(); + await ml.jobTable.filterWithSearchString(testData.adJobId); + await ml.stackManagementJobs.assertADJobRowSpaces(testData.adJobId, expectedJobRowSpaces); + + // DFA + await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.dfaJobId); + await ml.stackManagementJobs.assertDFAJobRowSpaces( + testData.dfaJobId, + expectedJobRowSpaces + ); + }); + + it('should display updated job only in assigned spaces', async () => { + const assignedSpaces = testData.assignToAllSpaces + ? Object.values(spaceIds) + : [ + ...testData.spacesToAdd, + ...(testData.removeInitialSpace ? [] : [testData.initialSpace]), + ]; + + for (const spaceId of Object.values(spaceIds)) { + await assertJobsDisplayedInSpace( + testData.adJobId, + testData.dfaJobId, + spaceId, + assignedSpaces.includes(spaceId) + ); + } + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/security/users.js b/x-pack/test/functional/apps/security/users.ts similarity index 96% rename from x-pack/test/functional/apps/security/users.js rename to x-pack/test/functional/apps/security/users.ts index 8730ee3aeeaf2..2c64e9ab5bb70 100644 --- a/x-pack/test/functional/apps/security/users.js +++ b/x-pack/test/functional/apps/security/users.ts @@ -7,7 +7,9 @@ import expect from '@kbn/expect'; import { keyBy } from 'lodash'; -export default function ({ getService, getPageObjects }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['security', 'settings']); const config = getService('config'); const log = getService('log'); @@ -104,9 +106,6 @@ export default function ({ getService, getPageObjects }) { expect(roles.kibana_user.reserved).to.be(true); expect(roles.kibana_user.deprecated).to.be(true); - expect(roles.kibana_dashboard_only_user.reserved).to.be(true); - expect(roles.kibana_dashboard_only_user.deprecated).to.be(true); - expect(roles.kibana_system.reserved).to.be(true); expect(roles.kibana_system.deprecated).to.be(false); diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index 2d2c3a8d5faa7..17311bc99e22d 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -15,7 +15,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - describe('overview page', function () { + // FLAKY: https://github.com/elastic/kibana/issues/89072 + describe.skip('overview page', function () { const DEFAULT_DATE_START = 'Sep 10, 2019 @ 12:40:08.078'; const DEFAULT_DATE_END = 'Sep 11, 2019 @ 19:40:08.078'; diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 704ce819b5b38..c8822b62ebd81 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -29,7 +29,6 @@ export default async function ({ readConfigFile }) { resolve(__dirname, './apps/monitoring'), resolve(__dirname, './apps/watcher'), resolve(__dirname, './apps/dashboard'), - resolve(__dirname, './apps/dashboard_mode'), resolve(__dirname, './apps/discover'), resolve(__dirname, './apps/security'), resolve(__dirname, './apps/spaces'), diff --git a/x-pack/test/functional/page_objects/infra_saved_views.ts b/x-pack/test/functional/page_objects/infra_saved_views.ts index ef91b3b5fcb3c..9437dd05d644c 100644 --- a/x-pack/test/functional/page_objects/infra_saved_views.ts +++ b/x-pack/test/functional/page_objects/infra_saved_views.ts @@ -79,9 +79,8 @@ export function InfraSavedViewsProvider({ getService }: FtrProviderContext) { }, async ensureViewIsLoadable(name: string) { - const subjects = await testSubjects.getVisibleTextAll('savedViews-loadList'); - const includesName = subjects.some((s) => s.includes(name)); - expect(includesName).to.be(true); + const subject = await testSubjects.find('savedViews-loadList'); + await subject.findByCssSelector(`li[title="${name}"]`); }, async closeSavedViewsLoadModal() { diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 1acddd4641ff4..2e1151602f311 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -185,8 +185,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param field - the desired field for the dimension * */ async dragFieldToWorkspace(field: string) { + const from = `lnsFieldListPanelField-${field}`; + await find.existsByCssSelector(from); await browser.html5DragAndDrop( - testSubjects.getCssSelector(`lnsFieldListPanelField-${field}`), + testSubjects.getCssSelector(from), testSubjects.getCssSelector('lnsWorkspace') ); await this.waitForLensDragDropToFinish(); @@ -199,8 +201,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param field - the desired geo_point or geo_shape field * */ async dragFieldToGeoFieldWorkspace(field: string) { + const from = `lnsFieldListPanelField-${field}`; + await find.existsByCssSelector(from); await browser.html5DragAndDrop( - testSubjects.getCssSelector(`lnsFieldListPanelField-${field}`), + testSubjects.getCssSelector(from), testSubjects.getCssSelector('lnsGeoFieldWorkspace') ); await this.waitForLensDragDropToFinish(); @@ -262,7 +266,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont `[data-test-subj="lnsDragDrop_draggable-${fieldName}"] [data-test-subj="lnsDragDrop-keyboardHandler"]` ); await field.focus(); - await browser.pressKeys(browser.keys.ENTER); + await retry.try(async () => { + await browser.pressKeys(browser.keys.ENTER); + await testSubjects.exists('.lnsDragDrop-isDropTarget'); // checks if we're in dnd mode and there's any drop target active + }); for (let i = 0; i < steps; i++) { await browser.pressKeys(reverse ? browser.keys.LEFT : browser.keys.RIGHT); } @@ -335,8 +342,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param dimension - the selector of the dimension being changed * */ async dragFieldToDimensionTrigger(field: string, dimension: string) { + const from = `lnsFieldListPanelField-${field}`; + await find.existsByCssSelector(from); await browser.html5DragAndDrop( - testSubjects.getCssSelector(`lnsFieldListPanelField-${field}`), + testSubjects.getCssSelector(from), testSubjects.getCssSelector(dimension) ); await this.waitForLensDragDropToFinish(); @@ -350,6 +359,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param to - the selector of the dimension being dropped to * */ async dragDimensionToDimension(from: string, to: string) { + await find.existsByCssSelector(from); await browser.html5DragAndDrop( testSubjects.getCssSelector(from), testSubjects.getCssSelector(to) @@ -367,6 +377,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async reorderDimensions(dimension: string, startIndex: number, endIndex: number) { const dragging = `[data-test-subj='${dimension}']:nth-of-type(${startIndex}) .lnsDragDrop`; const dropping = `[data-test-subj='${dimension}']:nth-of-type(${endIndex}) [data-test-subj='lnsDragDrop-reorderableDropLayer'`; + await find.existsByCssSelector(dragging); await browser.html5DragAndDrop(dragging, dropping); await this.waitForLensDragDropToFinish(); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index 2de5d83714aee..e772b45f77b2e 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -19,7 +19,12 @@ export interface SetValueOptions { export type MlCommonUI = ProvidedType; -export function MachineLearningCommonUIProvider({ getService }: FtrProviderContext) { +export function MachineLearningCommonUIProvider({ + getPageObjects, + getService, +}: FtrProviderContext) { + const PageObjects = getPageObjects(['spaceSelector']); + const canvasElement = getService('canvasElement'); const log = getService('log'); const retry = getService('retry'); @@ -279,5 +284,11 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte await testSubjects.click(`tablePagination-${rowsNumber}-rows`); await this.assertRowsNumberPerPage(testSubj, rowsNumber); }, + + async changeToSpace(spaceId: string) { + await PageObjects.spaceSelector.openSpacesNav(); + await PageObjects.spaceSelector.goToSpecificSpace(spaceId); + await PageObjects.spaceSelector.expectHomePage(spaceId); + }, }; } diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_table.ts b/x-pack/test/functional/services/ml/data_frame_analytics_table.ts index 0ffb274d28b50..5850919f2adc3 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_table.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_table.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ProvidedType } from '@kbn/test'; import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -17,13 +18,18 @@ export interface ExpectedSectionTable { } export type AnalyticsTableRowDetails = Record<'jobDetails', ExpectedSectionTable[]>; + +export type MlDFAJobTable = ProvidedType; + export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: FtrProviderContext) { const find = getService('find'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); return new (class AnalyticsTable { - public async parseAnalyticsTable() { + public async parseAnalyticsTable( + tableEnvironment: 'mlDataFrameAnalytics' | 'stackMgmtJobList' = 'mlDataFrameAnalytics' + ) { const table = await testSubjects.find('~mlAnalyticsTable'); const $ = await table.parseDomContent(); const rows = []; @@ -31,7 +37,17 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F for (const tr of $.findTestSubjects('~mlAnalyticsTableRow').toArray()) { const $tr = $(tr); - rows.push({ + const rowObject: { + id: string; + description: string; + memoryStatus: string; + sourceIndex: string; + destinationIndex: string; + type: string; + status: string; + progress: string; + spaces?: string[]; + } = { id: $tr .findTestSubject('mlAnalyticsTableColumnId') .find('.euiTableCellContent') @@ -42,6 +58,11 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F .find('.euiTableCellContent') .text() .trim(), + memoryStatus: $tr + .findTestSubject('mlAnalyticsTableColumnJobMemoryStatus') + .find('.euiTableCellContent') + .text() + .trim(), sourceIndex: $tr .findTestSubject('mlAnalyticsTableColumnSourceIndex') .find('.euiTableCellContent') @@ -66,7 +87,23 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F .findTestSubject('mlAnalyticsTableColumnProgress') .findTestSubject('mlAnalyticsTableProgress') .attr('value'), - }); + }; + + if (tableEnvironment === 'stackMgmtJobList') { + const $spaces = $tr + .findTestSubject('mlAnalyticsTableColumnSpaces') + .find('.euiTableCellContent') + .find('.euiAvatar--space'); + const spaces = []; + for (const el of $spaces.toArray()) { + // extract the space id from data-test-subj and add to list + spaces.push($(el).attr('data-test-subj').replace('space-avatar-', '')); + } + + rowObject.spaces = spaces; + } + + rows.push(rowObject); } return rows; @@ -155,6 +192,21 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F ); } + public async assertAnalyticsJobDisplayedInTable( + analyticsId: string, + shouldBeDisplayed: boolean + ) { + if (shouldBeDisplayed) { + await this.filterWithSearchString(analyticsId, 1); + } else { + if (await testSubjects.exists('mlNoDataFrameAnalyticsFound', { timeout: 1000 })) { + // no jobs at all, no other assertion needed + return; + } + await this.filterWithSearchString(analyticsId, 0); + } + } + public async assertAnalyticsRowFields(analyticsId: string, expectedRow: object) { await this.refreshAnalyticsTable(); const rows = await this.parseAnalyticsTable(); diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 4be3dd192f341..c5972bf082e54 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -113,7 +113,11 @@ export function MachineLearningProvider(context: FtrProviderContext) { const settingsCalendar = MachineLearningSettingsCalendarProvider(context, commonUI); const settingsFilterList = MachineLearningSettingsFilterListProvider(context, commonUI); const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context, commonUI); - const stackManagementJobs = MachineLearningStackManagementJobsProvider(context); + const stackManagementJobs = MachineLearningStackManagementJobsProvider( + context, + jobTable, + dataFrameAnalyticsTable + ); const testExecution = MachineLearningTestExecutionProvider(context); const testResources = MachineLearningTestResourcesProvider(context); const alerting = MachineLearningAlertingProvider(context, commonUI); diff --git a/x-pack/test/functional/services/ml/job_table.ts b/x-pack/test/functional/services/ml/job_table.ts index 46cb0ea5527ca..c711ff0ac8909 100644 --- a/x-pack/test/functional/services/ml/job_table.ts +++ b/x-pack/test/functional/services/ml/job_table.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ProvidedType } from '@kbn/test'; import { FtrProviderContext } from '../../ftr_provider_context'; import { MlCommonUI } from './common_ui'; @@ -17,6 +18,8 @@ import { URL_TYPE, } from '../../../../plugins/ml/public/application/jobs/components/custom_url_editor/constants'; +export type MlADJobTable = ProvidedType; + export function MachineLearningJobTableProvider( { getService }: FtrProviderContext, mlCommonUI: MlCommonUI, @@ -26,7 +29,9 @@ export function MachineLearningJobTableProvider( const retry = getService('retry'); return new (class MlJobTable { - public async parseJobTable() { + public async parseJobTable( + tableEnvironment: 'mlAnomalyDetection' | 'stackMgmtJobList' = 'mlAnomalyDetection' + ) { const table = await testSubjects.find('~mlJobListTable'); const $ = await table.parseDomContent(); const rows = []; @@ -47,7 +52,17 @@ export function MachineLearningJobTableProvider( $(el).remove(); } - rows.push({ + const rowObject: { + id: string; + description: string; + jobGroups: string[]; + recordCount: string; + memoryStatus: string; + jobState: string; + datafeedState: string; + latestTimestamp?: string; + spaces?: string[]; + } = { id: $tr.findTestSubject('mlJobListColumnId').find('.euiTableCellContent').text().trim(), description: $description .text() @@ -74,12 +89,33 @@ export function MachineLearningJobTableProvider( .find('.euiTableCellContent') .text() .trim(), - latestTimestamp: $tr + }; + + if (tableEnvironment === 'mlAnomalyDetection') { + const latestTimestamp = $tr .findTestSubject('mlJobListColumnLatestTimestamp') .find('.euiTableCellContent') .text() - .trim(), - }); + .trim(); + + rowObject.latestTimestamp = latestTimestamp; + } + + if (tableEnvironment === 'stackMgmtJobList') { + const $spaces = $tr + .findTestSubject('mlJobListColumnSpaces') + .find('.euiTableCellContent') + .find('.euiAvatar--space'); + const spaces = []; + for (const el of $spaces.toArray()) { + // extract the space id from data-test-subj and add to list + spaces.push($(el).attr('data-test-subj').replace('space-avatar-', '')); + } + + rowObject.spaces = spaces; + } + + rows.push(rowObject); } return rows; @@ -191,7 +227,9 @@ export function MachineLearningJobTableProvider( const filteredRows = rows.filter((row) => row.id === filter); expect(filteredRows).to.have.length( expectedRowCount, - `Filtered AD job table should have ${expectedRowCount} row(s) for filter '${filter}' (got matching items '${filteredRows}')` + `Filtered AD job table should have ${expectedRowCount} row(s) for filter '${filter}' (got matching items '${JSON.stringify( + filteredRows + )}')` ); } diff --git a/x-pack/test/functional/services/ml/navigation.ts b/x-pack/test/functional/services/ml/navigation.ts index 392ed7f7b1fd0..ddd0950c610fd 100644 --- a/x-pack/test/functional/services/ml/navigation.ts +++ b/x-pack/test/functional/services/ml/navigation.ts @@ -13,8 +13,9 @@ export function MachineLearningNavigationProvider({ getService, getPageObjects, }: FtrProviderContext) { - const retry = getService('retry'); + const appsMenu = getService('appsMenu'); const browser = getService('browser'); + const retry = getService('retry'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common']); @@ -26,6 +27,13 @@ export function MachineLearningNavigationProvider({ }); }, + async navigateToMlViaAppsMenu() { + await retry.tryForTime(60 * 1000, async () => { + await appsMenu.clickLink('Machine Learning'); + await testSubjects.existOrFail('mlApp', { timeout: 2000 }); + }); + }, + async navigateToStackManagement({ expectMlLink = true }: { expectMlLink?: boolean } = {}) { await retry.tryForTime(60 * 1000, async () => { await PageObjects.common.navigateToApp('management'); @@ -37,6 +45,13 @@ export function MachineLearningNavigationProvider({ }); }, + async navigateToStackManagementViaAppsMenu() { + await retry.tryForTime(60 * 1000, async () => { + await appsMenu.clickLink('Stack Management'); + await testSubjects.existOrFail('jobsListLink', { timeout: 2000 }); + }); + }, + async navigateToAlertsAndAction() { await PageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); @@ -154,6 +169,15 @@ export function MachineLearningNavigationProvider({ }); }, + async navigateToStackManagementJobsListPageAnomalyDetectionTab() { + // clicks the `Analytics` tab and loads the analytics list page + await testSubjects.click('mlStackManagementJobsListAnomalyDetectionTab'); + await retry.tryForTime(60 * 1000, async () => { + // verify that the empty prompt for analytics jobs list got loaded + await testSubjects.existOrFail('ml-jobs-list'); + }); + }, + async navigateToStackManagementJobsListPageAnalyticsTab() { // clicks the `Analytics` tab and loads the analytics list page await testSubjects.click('mlStackManagementJobsListAnalyticsTab'); diff --git a/x-pack/test/functional/services/ml/stack_management_jobs.ts b/x-pack/test/functional/services/ml/stack_management_jobs.ts index 05f5083a0ab4e..48fb89e51ff11 100644 --- a/x-pack/test/functional/services/ml/stack_management_jobs.ts +++ b/x-pack/test/functional/services/ml/stack_management_jobs.ts @@ -8,6 +8,8 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { MlADJobTable } from './job_table'; +import { MlDFAJobTable } from './data_frame_analytics_table'; type SyncFlyoutObjectType = | 'MissingObjects' @@ -15,7 +17,12 @@ type SyncFlyoutObjectType = | 'ObjectsMissingDatafeed' | 'ObjectsUnmatchedDatafeed'; -export function MachineLearningStackManagementJobsProvider({ getService }: FtrProviderContext) { +export function MachineLearningStackManagementJobsProvider( + { getService }: FtrProviderContext, + mlADJobTable: MlADJobTable, + mlDFAJobTable: MlDFAJobTable +) { + const find = getService('find'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); const toasts = getService('toasts'); @@ -81,5 +88,111 @@ export function MachineLearningStackManagementJobsProvider({ getService }: FtrPr const dismissButton = await testSubjects.findDescendant('toastCloseButton', resultToast); await dismissButton.click(); }, + + async assertADJobRowSpaces(adJobId: string, expectedSpaces: string[]) { + await mlADJobTable.refreshJobList(); + const rows = await mlADJobTable.parseJobTable('stackMgmtJobList'); + const jobRow = rows.filter((row) => row.id === adJobId)[0]; + expect(jobRow).to.have.property('spaces'); + expect(jobRow.spaces!.sort()).to.eql( + expectedSpaces.sort(), + `Expected spaces for AD job '${adJobId}' to be '${JSON.stringify( + expectedSpaces + )}' (got '${JSON.stringify(jobRow.spaces)}')` + ); + }, + + async assertDFAJobRowSpaces(dfaJobId: string, expectedSpaces: string[]) { + await mlDFAJobTable.refreshAnalyticsTable(); + const rows = await mlDFAJobTable.parseAnalyticsTable('stackMgmtJobList'); + const jobRow = rows.filter((row) => row.id === dfaJobId)[0]; + expect(jobRow).to.have.property('spaces'); + expect(jobRow.spaces!.sort()).to.eql( + expectedSpaces.sort(), + `Expected spaces for DFA job '${dfaJobId}' to be '${JSON.stringify( + expectedSpaces + )}' (got '${JSON.stringify(jobRow.spaces)}')` + ); + }, + + async openADJobSpacesFlyout(adJobId: string) { + await retry.tryForTime(5000, async () => { + await testSubjects.click( + mlADJobTable.rowSelector(adJobId, 'mlJobListRowManageSpacesButton'), + 1000 + ); + await testSubjects.existOrFail('share-to-space-flyout', { timeout: 2000 }); + }); + }, + + async openDFAJobSpacesFlyout(dfaJobId: string) { + await retry.tryForTime(5000, async () => { + await testSubjects.click( + mlDFAJobTable.rowSelector(dfaJobId, 'mlJobListRowManageSpacesButton'), + 1000 + ); + await testSubjects.existOrFail('share-to-space-flyout', { timeout: 2000 }); + }); + }, + + async saveAndCloseSpacesFlyout() { + await testSubjects.clickWhenNotDisabled('sts-save-button', { timeout: 2000 }); + await testSubjects.missingOrFail('share-to-space-flyout', { timeout: 2000 }); + }, + + async selectShareToSpacesMode(inputTestSubj: 'shareToExplicitSpacesId' | 'shareToAllSpacesId') { + await retry.tryForTime(5000, async () => { + // The input element can not be clicked directly. + // Instead, we need to click the corresponding label + const inputId = await testSubjects.getAttribute(inputTestSubj, 'id', 1000); + const labelElement = await find.byCssSelector(`[for="${inputId}"]`, 1000); + await labelElement.click(); + + const checked = await testSubjects.getAttribute(inputTestSubj, 'checked', 1000); + expect(checked).to.eql('true', `Input '${inputTestSubj}' should be checked`); + + // sometimes the checked attribute of the input is set but it's not actually + // selected, so we're also checking the class of the corresponding label + const updatedLabelElement = await find.byCssSelector(`[for="${inputId}"]`, 1000); + const labelClasses = await updatedLabelElement.getAttribute('class'); + expect(labelClasses).to.contain( + 'euiButtonGroupButton-isSelected', + `Label for '${inputTestSubj}' should be selected` + ); + }); + }, + + async selectShareToExplicitSpaces() { + await this.selectShareToSpacesMode('shareToExplicitSpacesId'); + }, + + async selectShareToAllSpaces() { + await this.selectShareToSpacesMode('shareToAllSpacesId'); + }, + + async isSpaceSelectionRowSelected(spaceId: string): Promise { + const state = await testSubjects.getAttribute( + `sts-space-selector-row-${spaceId}`, + 'aria-selected', + 1000 + ); + return state === 'true'; + }, + + async assertSpaceSelectionRowSelected(spaceId: string, shouldBeSelected: boolean) { + const isSelected = await this.isSpaceSelectionRowSelected(spaceId); + expect(isSelected).to.eql( + shouldBeSelected, + `Space selection row for '${spaceId}' should${shouldBeSelected ? '' : ' not'} be selected` + ); + }, + + async toggleSpaceSelectionRow(spaceId: string, shouldSelect: boolean) { + const isSelected = await this.isSpaceSelectionRowSelected(spaceId); + if (isSelected !== shouldSelect) { + await testSubjects.click(`sts-space-selector-row-${spaceId}`, 1000); + } + await this.assertSpaceSelectionRowSelected(spaceId, shouldSelect); + }, }; } diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 275002155d7e0..43d4995276239 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -110,7 +110,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi .toArray() .map((cell) => { const cellText = $(cell).text(); - const pattern = /^(.*)Row: (\d+), Column: (\d+):$/; + const pattern = /^(.*)Row: (\d+); Column: (\d+)$/; const matches = cellText.match(pattern); expect(matches).to.not.eql(null, `Cell text should match pattern '${pattern}'`); return { text: matches![1], row: Number(matches![2]), column: Number(matches![3]) }; diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx index 61322261d04d5..3059ff0629a21 100644 --- a/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx +++ b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import type { AlertConsumers as AlertConsumersTyped } from '@kbn/rule-data-utils'; -// @ts-expect-error -import { AlertConsumers as AlertConsumersNonTyped } from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac'; import { Router } from 'react-router-dom'; import React, { useCallback, useRef } from 'react'; import ReactDOM from 'react-dom'; @@ -17,8 +14,6 @@ import { KibanaContextProvider } from '../../../../../../../../src/plugins/kiban import { TimelinesUIStart } from '../../../../../../../plugins/timelines/public'; import { DataPublicPluginStart } from '../../../../../../../../src/plugins/data/public'; -const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped; - type CoreStartTimelines = CoreStart & { data: DataPublicPluginStart }; /** @@ -42,7 +37,6 @@ export function renderApp( ReactDOM.unmountComponentAtNode(parameters.element); }; } -const ALERT_RULE_CONSUMER = [AlertConsumers.SIEM]; const AppRoot = React.memo( ({ @@ -67,7 +61,6 @@ const AppRoot = React.memo( {(timelinesPluginSetup && timelinesPluginSetup.getTGrid && timelinesPluginSetup.getTGrid<'standalone'>({ - alertConsumers: ALERT_RULE_CONSUMER, appId: 'securitySolution', type: 'standalone', casePermissions: { diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts index 2d62725e23989..6ff8946d48c5b 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts @@ -59,7 +59,9 @@ export default function ({ getService }: FtrProviderContext) { "attempts": 0, "created_by": false, "jobtype": "csv", - "meta": Object {}, + "meta": Object { + "isDeprecated": true, + }, "payload": Object { "isDeprecated": true, "title": "A Saved Search With a DATE FILTER", @@ -101,7 +103,9 @@ export default function ({ getService }: FtrProviderContext) { "attempts": 0, "created_by": false, "jobtype": "csv", - "meta": Object {}, + "meta": Object { + "isDeprecated": true, + }, "payload": Object { "isDeprecated": true, "title": "A Saved Search With a DATE FILTER", @@ -136,7 +140,9 @@ export default function ({ getService }: FtrProviderContext) { "attempts": 0, "created_by": false, "jobtype": "csv", - "meta": Object {}, + "meta": Object { + "isDeprecated": true, + }, "payload": Object { "isDeprecated": true, "title": "A Saved Search With a DATE FILTER", @@ -168,7 +174,9 @@ export default function ({ getService }: FtrProviderContext) { "attempts": 0, "created_by": false, "jobtype": "csv", - "meta": Object {}, + "meta": Object { + "isDeprecated": true, + }, "payload": Object { "isDeprecated": true, "title": "A Saved Search With a DATE FILTER", diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index ea2f321458c22..77eb93a4107a5 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -155,8 +155,16 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest { - await common.navigateToApp('home'); // Navigate to Home to make sure all the appIds are loaded + await common.navigateToApp('home'); // Navigate to Home + await common.isChromeVisible(); // Make sure the page is fully loaded const appIds = await browser.execute(() => window.__applicationIds__); + if (!appIds || !Array.isArray(appIds)) { + throw new Error( + 'Failed to retrieve all the existing applications in Kibana. Did it fail to boot or to navigate to home?' + ); + } try { expect(Object.keys(applicationUsageSchema).sort()).to.eql(appIds.sort()); } catch (err) { diff --git a/yarn.lock b/yarn.lock index 84246830a2fff..b396c50c4fb21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1389,10 +1389,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@34.0.0": - version "34.0.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-34.0.0.tgz#42288a6b3a303ccc61385b786f2ccf3549c3b43a" - integrity sha512-gXekMH6iWIo5DaUzPJLjbn02CuPaxwGIOOF2cz/UH9zRY2A5UZ8CDICysDgriK1PcJfKPCa7Yk5cntn590coyg== +"@elastic/charts@34.1.1": + version "34.1.1" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-34.1.1.tgz#ad0a614448d7a3b28a77cc6d3a5ef5c89530ea34" + integrity sha512-9HFqjGZALURKg0uwuo0t91IMDUY1lnRTovnJJgTrE0zIkUtEIX/yL1gKJlVKLECJO6KrwIO1LcGhobtkVQkR/A== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" @@ -1404,7 +1404,6 @@ d3-interpolate "^1.4.0" d3-scale "^1.0.7" d3-shape "^1.3.4" - newtype-ts "^0.2.4" prop-types "^15.7.2" re-reselect "^3.4.0" react-redux "^7.1.0" @@ -1433,10 +1432,10 @@ dependencies: "@elastic/ecs-helpers" "^1.1.0" -"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@^8.0.0-canary.17": - version "8.0.0-canary.17" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.0.0-canary.17.tgz#0625a04cc585e3f311bc6471e04cd4fb0e927e9a" - integrity sha512-rsbEdzxYlEU+jS+qf5Gr3+2U6/1Z/S/Yt2dM7lp1A64mCjuOqqHoR2FTHN27BWvBCjtJrtyhlCJht4fsTLNuYA== +"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@^8.0.0-canary.18": + version "8.0.0-canary.18" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.0.0-canary.18.tgz#d17e3c74809079c7272bb5ee0f8f96c16d723bae" + integrity sha512-YxFeoOWLWRMOLLSv1Zp5TyGe6hwc6FQtrLZT1TjsXucm9LVnxQSpg9tUPnYUfUNDwoY3FZwthUxH3+gb3Lqedw== dependencies: debug "^4.3.1" hpagent "^0.1.1" @@ -1466,10 +1465,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@37.1.1": - version "37.1.1" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-37.1.1.tgz#06bd1e1908b8b56525107a3eca42af036b950ab5" - integrity sha512-wEehwi9Ei81ydl8ExDuALbzDH6dgaHqKSlv/3Pmm+BcuW8FW4ctr8K5YPSJ7+5hdOdG0Bps5nnIpCDEinXSsLw== +"@elastic/eui@37.3.0": + version "37.3.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-37.3.0.tgz#98e19f7fd610df2198de453c6078032057012249" + integrity sha512-CxWPS8GL+0GN/fUnQ8PRb9t6Iep1UNpBGOfbbx/jyoGmVtoMLXp4RRNCd10iCZR0oXMD8gmtdul5OJwRYLkD7g== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -14210,11 +14209,6 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -fp-ts@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.12.0.tgz#d333310e4ac104cdcb6bea47908e381bb09978e7" - integrity sha512-fWwnAgVlTsV26Ruo9nx+fxNHIm6l1puE1VJ/C0XJ3nRQJJJIgRHYw6sigB3MuNFZL1o4fpGlhwFhcbxHK0RsOA== - fp-ts@^2.3.1: version "2.8.6" resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.8.6.tgz#1a0e6c3f29f5b0fbfa3120f034ea266aa73c811b" @@ -19817,11 +19811,6 @@ monitor-event-loop-delay@^1.0.0: resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7" integrity sha512-YRIr1exCIfBDLZle8WHOfSo7Xg3M+phcZfq9Fx1L6Abo+atGp7cge5pM7PjyBn4s1oZI/BRD4EMrzQBbPpVb5Q== -monocle-ts@^1.0.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/monocle-ts/-/monocle-ts-1.7.1.tgz#03a615938aa90983a4fa29749969d30f72d80ba1" - integrity sha512-X9OzpOyd/R83sYex8NYpJjUzi/MLQMvGNVfxDYiIvs+QMXMEUDwR61MQoARFN10Cqz5h/mbFSPnIQNUIGhYd2Q== - moo-color@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.2.tgz#837c40758d2d58763825d1359a84e330531eca64" @@ -20106,14 +20095,6 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== -newtype-ts@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/newtype-ts/-/newtype-ts-0.2.4.tgz#a02a8f160a3d179f871848d687a93de73a964a41" - integrity sha512-HrzPdG0+0FK1qHbc3ld/HXu252OYgmN993bFxUtZ6NFCLUk1eq+yKwdvP07BblXQibGqMWNXBUrNoLUq23Ma2Q== - dependencies: - fp-ts "^1.0.0" - monocle-ts "^1.0.0" - next-line@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-line/-/next-line-1.1.0.tgz#fcae57853052b6a9bae8208e40dd7d3c2d304603"